Ich möchte überprüfen , ob jede der Elemente in einer Liste in eine andere Liste vorhanden sind. Ich kann es einfach mit dem folgenden Code tun, aber ich vermute, dass es eine Bibliotheksfunktion gibt, um dies zu tun. Wenn nicht, gibt es eine pythonischere Methode, um das gleiche Ergebnis zu erzielen.
In [78]: a = [1, 2, 3, 4, 5]
In [79]: b = [8, 7, 6]
In [80]: c = [8, 7, 6, 5]
In [81]: def lists_overlap(a, b):
....: for i in a:
....: if i in b:
....: return True
....: return False
....:
In [82]: lists_overlap(a, b)
Out[82]: False
In [83]: lists_overlap(a, c)
Out[83]: True
In [84]: def lists_overlap2(a, b):
....: return len(set(a).intersection(set(b))) > 0
....:
list
python
intersection
fmark
quelle
quelle
len(...) > 0
weil siebool(set([]))
Falsch ergibt. Und wenn Sie Ihre Listen zunächst als Sets behalten, sparen Sie natürlich den Aufwand für die Set-Erstellung.True
von1
undFalse
von unterscheiden können0
.not set([1]).isdisjoint([True])
bekommtTrue
, das gleiche mit anderen Lösungen.Antworten:
Kurze Antwort : Verwenden Sie
not set(a).isdisjoint(b)
, es ist in der Regel die schnellste.Es gibt vier gängige Methoden, um zu testen, ob zwei Listen vorhanden sind,
a
undb
um Elemente gemeinsam zu nutzen. Die erste Möglichkeit besteht darin, beide in Mengen umzuwandeln und deren Schnittpunkt als solche zu überprüfen:Da Sätze gespeichert sind in Python eine Hash - Tabelle, die Such sie ist
O(1)
(siehe hier für weitere Informationen über die Komplexität von Operatoren in Python). Theoretisch ist diesO(n+m)
im Durchschnitt fürn
undm
Objekte in Listena
undb
. Aber 1) es müssen zuerst Sätze aus den Listen erstellt werden, was eine nicht zu vernachlässigende Zeit in Anspruch nehmen kann, und 2) es wird angenommen, dass Hashing-Kollisionen unter Ihren Daten spärlich sind.Die zweite Möglichkeit besteht darin, einen Generatorausdruck zu verwenden, der eine Iteration der Listen ausführt, z.
Dies ermöglicht die direkte Suche, sodass kein neuer Speicher für Zwischenvariablen zugewiesen wird. Es kommt auch beim ersten Fund heraus. Der
in
Operator ist aber immerO(n)
auf Listen (siehe hier ).Eine andere vorgeschlagene Option ist ein Hybrid, um eine der Listen zu durchlaufen, die andere in einen Satz umzuwandeln und die Mitgliedschaft in diesem Satz zu testen, wie folgt:
Ein vierter Ansatz besteht darin, die
isdisjoint()
Methode der (eingefrorenen) Mengen (siehe hier ) zu nutzen, zum Beispiel:Befinden sich die von Ihnen gesuchten Elemente am Anfang eines Arrays (z. B. sortiert), wird der Generatorausdruck bevorzugt, da die Mengenschnittmethode neuen Speicher für die Zwischenvariablen zuweisen muss:
Hier ist ein Diagramm der Ausführungszeit für dieses Beispiel in Abhängigkeit von der Listengröße:
Beachten Sie, dass beide Achsen logarithmisch sind. Dies ist der beste Fall für den Generatorausdruck. Wie zu sehen ist, ist die
isdisjoint()
Methode für sehr kleine Listengrößen besser, während der Generatorausdruck für größere Listengrößen besser ist.Wenn andererseits die Suche mit dem Beginn des Hybrid- und Generatorausdrucks beginnt und sich das gemeinsam genutzte Element systematisch am Ende des Arrays befindet (oder beide Listen keine Werte gemeinsam haben), werden die disjunkten und festgelegten Schnittpunkte verwendet viel schneller als der Generatorausdruck und der Hybridansatz.
Es ist interessant festzustellen, dass der Generatorausdruck bei größeren Listen viel langsamer ist. Dies gilt nur für 1000 Wiederholungen anstelle der 100000 für die vorherige Abbildung. Dieses Setup kommt auch dann gut an, wenn keine Elemente gemeinsam genutzt werden, und ist der beste Fall für disjunkte und festgelegte Schnittpunkte.
Hier sind zwei Analysen unter Verwendung von Zufallszahlen (anstatt das Setup so zu manipulieren, dass die eine oder andere Technik bevorzugt wird):
Hohe Chance zu teilen: Elemente werden zufällig ausgewählt
[1, 2*len(a)]
. Geringe Chance zum Teilen: Elemente werden zufällig ausgewählt[1, 1000*len(a)]
.Bisher wurde bei dieser Analyse angenommen, dass beide Listen gleich groß sind. Bei zwei Listen unterschiedlicher Größe, zum Beispiel
a
viel kleiner,isdisjoint()
geht es immer schneller:Stellen Sie sicher, dass die
a
Liste kleiner ist, da sonst die Leistung abnimmt. In diesem Experiment wurde diea
Listengröße konstant auf gesetzt5
.Zusammenfassend:
not set(a).isdisjoint(b)
ist immer die schnellste.any(i in a for i in b)
bei großen Listengrößen am schnellsten.not set(a).isdisjoint(b)
, der immer schneller ist alsbool(set(a) & set(b))
.a = set(a); any(i in a for i in b)
ist im Allgemeinen langsamer als andere Methoden.In den meisten Fällen ist die Verwendung der
isdisjoint()
Methode der beste Ansatz, da die Ausführung des Generatorausdrucks viel länger dauert, da er sehr ineffizient ist, wenn keine Elemente gemeinsam genutzt werden.quelle
any
wird beim ersten nicht falschen Wert beendet. Wenn wir eine Liste verwenden, in der der einzige übereinstimmende Wert am Ende steht, erhalten wir Folgendes:timeit('any(i in a for i in b)', setup="a=list(range(1000));b=[x+998 for x in range(999,-0,-1)]", number=1000) 13.739536046981812
timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=[x+998 for x in range(999,-0,-1)]", number=1000) 0.08102107048034668
... und nur mit 1000 Iterationen.not set(a).isdisjoint(b)
getestet werden, ob zwei Listen ein Mitglied gemeinsam nutzen.set(a).isdisjoint(b)
Gibt zurück,True
wenn die beiden Listen kein Mitglied gemeinsam nutzen. Die Antwort sollte bearbeitet werden?Hinweis: In der obigen Beschreibung wird davon ausgegangen, dass Sie einen Booleschen Wert als Antwort wünschen. Wenn Sie nur einen Ausdruck benötigen, der in einer
if
Anweisung verwendet werden soll, verwenden Sie einfachif set(a) & set(b):
quelle
O(n + m)
. Ich würde vermuten, dass Sets mithilfe von Hash-Tabellen implementiert werden und derin
Operator somitO(1)
rechtzeitig arbeiten kann (außer in entarteten Fällen). Ist das richtig? Wenn ja, bedeutet dies, dass Hash-Tabellen angesichts der Worst-Case-Lookup-Leistung eine Leistung aufweisenO(n)
, die im Gegensatz zu einem schlechteren Fall eineO(n * m)
Leistung aufweist?O(n)
;), siehe pastebin.com/Kn3kAW7u Nur für lafs.Dies ist asymptotisch optimal (Worst-Case O (n + m)) und kann aufgrund des
any
Kurzschlusses besser sein als der Schnittpunktansatz .Z.B:
wird True zurückgeben, sobald es soweit ist
3 in sb
EDIT: Eine weitere Variante (danke an Dave Kirby):
Dies beruht auf
imap
dem in C implementierten Iterator und nicht auf einem Generatorverständnis. Es wird auchsb.__contains__
als Zuordnungsfunktion verwendet. Ich weiß nicht, wie viel Leistungsunterschied dies macht. Es wird immer noch kurzgeschlossen.quelle
any(itertools.imap(sb.__contains__, a))
was noch schneller sein sollte, da die Verwendung einer Lambda-Funktion vermieden wird.Sie können auch
any
mit Listenverständnis verwenden:quelle
[]
) und es würde schneller laufen und weniger Speicher verbrauchen, aber die Zeit wäre immer noch O (n * m).In Python 2.6 oder höher können Sie Folgendes tun:
quelle
Sie können den beliebigen Ausdruck für die integrierte Funktion / den Generator verwenden:
Wie John und Lie darauf hingewiesen haben, führt dies zu falschen Ergebnissen, wenn für jedes i, das von den beiden Listen geteilt wird, bool (i) == False. Es sollte sein:
quelle
bool(x)
befindet, an der False ist. In Lie Ryans Beispiel ist x 0. Nur Fix istany(True for i in a if i in b)
das, was besser geschrieben ist als das bereits Geseheneany(i in b for i in a)
.x
in der Kreuzung so sind , dassbool(x)
istFalse
.Diese Frage ist ziemlich alt, aber ich bemerkte, dass während die Leute über Sets gegen Listen stritten, niemand daran dachte, sie zusammen zu verwenden. Nach Soravux 'Beispiel
Schlimmster Fall für Listen:
Und der beste Fall für Listen:
Noch schneller als das Durchlaufen von zwei Listen ist das Durchlaufen einer Liste, um festzustellen, ob sie sich in einem Satz befindet. Dies ist sinnvoll, da das Überprüfen, ob sich eine Zahl in einem Satz befindet, eine konstante Zeit benötigt, während das Überprüfen durch Durchlaufen einer Liste proportional zur Länge von dauert Die Liste.
Daher ist meine Schlussfolgerung, dass ich eine Liste durchlaufen und prüfen muss, ob sie in einem Satz enthalten ist .
quelle
isdisjoint()
Methode für ein (eingefrorenes) Set, wie von @Toughy angegeben, ist sogar noch besser:timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
=> 0,00913715362548828Wenn es Ihnen egal ist, was das überlappende Element sein könnte, können Sie einfach
len
die kombinierte Liste mit den als Satz kombinierten Listen vergleichen. Wenn es überlappende Elemente gibt, wird die Menge kürzer:len(set(a+b+c))==len(a+b+c)
Gibt True zurück, wenn keine Überlappung vorliegt.quelle
Ich werde einen anderen mit einem funktionalen Programmierstil einwerfen:
Erläuterung:
Gibt eine Liste von Booleschen Werten zurück, in denen sich Elemente von
b
befindena
. Diese Liste wird dann an übergebenany
, die einfach zurückgibt,True
wenn Elemente vorhanden sindTrue
.quelle