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
und b
um Elemente gemeinsam zu nutzen. Die erste Möglichkeit besteht darin, beide in Mengen umzuwandeln und deren Schnittpunkt als solche zu überprüfen:
bool(set(a) & set(b))
Da Sätze gespeichert sind in Python eine Hash - Tabelle, die Such sie istO(1)
(siehe hier für weitere Informationen über die Komplexität von Operatoren in Python). Theoretisch ist dies O(n+m)
im Durchschnitt für n
und m
Objekte in Listen a
und b
. 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.
any(i in a for i in b)
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 immer O(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:
a = set(a); any(i in a for i in b)
Ein vierter Ansatz besteht darin, die isdisjoint()
Methode der (eingefrorenen) Mengen (siehe hier ) zu nutzen, zum Beispiel:
not set(a).isdisjoint(b)
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:
from timeit import timeit
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=list(range(1000))", number=100000)
26.077727576019242
>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=list(range(1000))", number=100000)
0.16220548999262974
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.
>>> 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
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 die a
Listengröße konstant auf gesetzt 5
.
Zusammenfassend:
- Wenn die Listen sehr klein sind (<10 Elemente),
not set(a).isdisjoint(b)
ist immer die schnellste.
- Wenn die Elemente in den Listen sortiert sind oder eine reguläre Struktur haben, die Sie nutzen können, ist der Generatorausdruck
any(i in a for i in b)
bei großen Listengrößen am schnellsten.
- Testen Sie den eingestellten Schnittpunkt mit
not set(a).isdisjoint(b)
, der immer schneller ist als bool(set(a) & set(b))
.
- Der Hybrid "Liste durchlaufen, Test am Set"
a = set(a); any(i in a for i in b)
ist im Allgemeinen langsamer als andere Methoden.
- Der Generatorausdruck und der Hybrid sind viel langsamer als die beiden anderen Ansätze, wenn es um Listen ohne gemeinsame Nutzung von Elementen geht.
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.
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.