2016-02-02 13 views
12

Wenn haben Sie eine Liste wie folgt:schnelle Art und Weise der Überquerung Strings in einer Liste

shops=['A','B','C','D'] 

Und möchte folgende neue Listen erstellen (I überqueren jedes Element mit jedem anderen und einen String erstellen, in dem ersten Teil ist alphanummerisch vor dem zweiten):

['A-B', 'A-C', 'A-D'] 

['A-B', 'B-C', 'B-D'] 

['A-C', 'B-C', 'C-D'] 

['A-D', 'B-D', 'C-D'] 

ich so etwas wie dieses:

for a in shops: 
    cons = [] 
    for b in shops: 
     if a!=b: 
      con = [a,b] 
      con = sorted(con, key=lambda x: float(x)) 
      cons.append(con[0]+'-'+con[1]) 
    print(cons) 

dies ist jedoch pr langsam für große Listen (z.B. 1000, wo ich 1000 * 999 * 0,5 Ausgänge habe). Ich suchte nach einer effizienteren Methode, dies zu tun?

Ich hätte eine if-else-Klausel für die Sortierung, z.

for a in shops: 
    cons = [] 
    for b in shops: 
     if a<b: 
      cons.append(a+"-"+b) 
     elif a>b: 
      cons.append(b+"-"+a) 
    print(cons) 

Which, habe ich noch nicht abgelaufen - aber ich die Haupt dachte Verlangsamung der Doppel for-Schleife

+0

Warum nicht '('A_B', 'A_C', 'B_C')'? – Kasramvd

+2

'key = Lambda x: float (x)' ist das gleiche - nur langsamer - als 'key = float' –

+1

Ich kann nicht die allgemeine Komplexität dieser Algorithmen, wie Sie alle generieren müssen diese Kombinationen. Sie können nur Mikro-Tuning durchführen (indem Sie keine unnötigen Lambdas definieren). Aber: Wofür brauchen Sie diese Kombinationen überhaupt? Vielleicht gibt es einen besseren Weg, z.B. nur die Kombinationen generieren, oder ohne die Sortierung. –

Antwort

9

Sie können eine verschachtelte Liste Verständnis mit einigen zusätzlichen Kontrollen erstellen:

>>> shops=['A','B','C','D'] 
>>> [["-".join((min(a,b), max(a,b))) for b in shops if b != a] for a in shops] 
[['A-B', 'A-C', 'A-D'], 
['A-B', 'B-C', 'B-D'], 
['A-C', 'B-C', 'C-D'], 
['A-D', 'B-D', 'C-D']] 

Beachten Sie, dass dies wahrscheinlich der Code nicht viel schneller als, wie Sie noch alle diese Kombinationen zu erzeugen haben. In der Praxis könnte man ihm einen Generator Ausdruck machen, so werden die Elemente nicht alle auf einmal erzeugt, sondern nur „nach Bedarf“:

gen = (["-".join((min(a,b), max(a,b))) for b in shops if b != a] for a in shops) 
for item in gen: 
    print(item) 

Update: Ich habe einige Analyse Timing IPython die mit %timeit. Es stellt sich heraus, dass Ihre zweite Implementierung am schnellsten ist. Getestet mit einer Liste von 100 Strings (map(str, range(100))) und nachdem jede der Methoden in Generatoren umgewandelt wurde.

In [32]: %timeit list(test.f1())   # your first implementation 
100 loops, best of 3: 13.5 ms per loop 

In [33]: %timeit list(test.f2())   # your second implementation 
1000 loops, best of 3: 1.63 ms per loop 

In [34]: %timeit list(test.g())   # my implementation 
100 loops, best of 3: 3.49 ms per loop 

Sie können es beschleunigen, indem Sie einen einfachen if/else statt min/max verwenden, wie in Ihrem zweiten Implementierung, dann sind sie etwa gleich schnell.

(["-".join((a,b) if a < b else (b,a)) for b in shops if b != a] for a in shops) 
+0

Vielen Dank! Was ist der Unterschied zwischen der Verwendung eines Generatorausdrucks oder der Verwendung einer Funktion und der Rückgabe mit Ertrag? Ich habe das erstere gemacht. – mptevsion

+0

@mptevsion Sie sind ungefähr gleichwertig. Ein Generator-Ausdruck steht für eine Funktion mit einem Ergebnis, wenn ein Listenverständnis für eine Funktion ist, die eine Liste zurückgibt. –

+0

@mptevsion 'Ausbeute' ist allgemeiner. Es ist ein Ausdruck und Sie können Werte an die Funktion übergeben, indem Sie die Methode 'send' verwenden. Dies kann nicht in einem Generatorausdruck erfolgen. Probieren Sie es aus: 'def f(): x = yield 0; Ausbeute x 'dann tue' a = f(); nächstes (a); a.Sendung (1); nächste (a) '. – Bakuriu

2

Basierend auf das, was Sie gesagt haben:

ich jedes Element überqueren mit jeder anderen und einen String erstellen, in dem erste Teil ist alphanummerisch vor dem zweiten

können Sie 2 Kombination wie folgt vor:

>>> form itertools import combinations 
>>> list(combinations(['_'.join(i) for i in combinations(shops,2)],3) 
...) 
[('A_B', 'A_C', 'A_D'), ('A_B', 'A_C', 'B_C'), ('A_B', 'A_C', 'B_D'), ('A_B', 'A_C', 'C_D'), ('A_B', 'A_D', 'B_C'), ('A_B', 'A_D', 'B_D'), ('A_B', 'A_D', 'C_D'), ('A_B', 'B_C', 'B_D'), ('A_B', 'B_C', 'C_D'), ('A_B', 'B_D', 'C_D'), ('A_C', 'A_D', 'B_C'), ('A_C', 'A_D', 'B_D'), ('A_C', 'A_D', 'C_D'), ('A_C', 'B_C', 'B_D'), ('A_C', 'B_C', 'C_D'), ('A_C', 'B_D', 'C_D'), ('A_D', 'B_C', 'B_D'), ('A_D', 'B_C', 'C_D'), ('A_D', 'B_D', 'C_D'), ('B_C', 'B_D', 'C_D')] 
>>> 

Zunächst können Sie eine Kombination innerhalb einer Liste Verständnis verwenden, um die geordneten Paare zu erstellen und verketten sie str.join verwenden. Verwenden Sie dann eine andere Kombination, um die Trinalsätze zu erstellen.

+1

Dies scheint sich nicht gut auf den Fall von 6 Elementen in der Liste auszudehnen. – Alexander

+1

Wie ich die Frage verstehe, sollte die erste Unterliste A in jeder Kombination haben, die zweite B, dann C und D, jeweils mit jedem der anderen Buchstaben, alphabetisch sortiert. –

+0

@tobias_k Ja, das ist in der erwarteten Ausgabe klar, aber nicht in dem, was OP gesagt hat. – Kasramvd

4

Wenn die Liste sortiert ist und es gibt keine Duplikate, können Sie den Überblick über Ihre Position in der Liste zu halten, um zu vermeiden Vergleiche zu tun, um den Auftrag zu bekommen.

from itertools import chain, islice 

combos = [] 
for i, s in enumerate(shops): 
    combo = ['{0}-{1}'.format(a, b) for a, b in chain(
     ((c, s) for c in islice(shops, None, i), 
     ((s, c) for c in islice(shops, i+1))] 
    combos.append(combo) 

EDIT: aktualisiert Generatoren verwenden

+0

Ich dachte, dass die Erstellung all dieser temporären Listen furchtbar langsam wäre, aber (wenn man sie in einen Generator umwandelt), ist das ungefähr so ​​schnell wie OP's zweite Implementierung oder mein Generator. Bei großen Listen scheint mir jedoch ein anderes Ergebnis zu erhalten. Für 'shops = map (str, range (10))' die Ergebnisse sind gleich, aber mit 'map (str, range (100))' das Ergebnis von Ihnen unterscheidet sich, kann aber nicht sagen, wie ... –

+0

@ tobias_k: Dies setzt voraus, dass die Liste sortiert ist. Wenn Sie als Strings Zahlen> = 10 haben, werden die Shops am Anfang nicht mehr korrekt sortiert. (Ich hatte die gleiche Idee, aber Brendan hat mich dazu geschlagen :) –

+0

@AleksiTorhamo Sie haben Recht, nur geprüft: Der Unterschied kommt von den Shops, die in unseren Implementierungen anders sortiert sind. –

Verwandte Themen