Krótka odpowiedź : używaj not set(a).isdisjoint(b)
, generalnie jest najszybsza.
Istnieją cztery typowe sposoby sprawdzenia, czy dwie listy a
i b
udostępnienie dowolnych elementów. Pierwszą opcją jest przekonwertowanie obu na zbiory i sprawdzenie ich przecięcia, jako takie:
bool(set(a) & set(b))
Ponieważ zestawy są przechowywane w Pythonie przy użyciu tablicy skrótów, wyszukiwanie ich odbywa sięO(1)
(zobacz tutaj, aby uzyskać więcej informacji o złożoności operatorów w Pythonie). Teoretycznie jest to O(n+m)
średnio dla obiektów n
i m
na listach a
i b
. Ale 1) musi najpierw utworzyć zestawy z list, co może zająć dużo czasu, a 2) zakłada, że kolizje haszowania są rzadkie wśród twoich danych.
Drugim sposobem jest użycie wyrażenia generatora wykonującego iterację na listach, takich jak:
any(i in a for i in b)
Pozwala to na wyszukiwanie w miejscu, więc żadna nowa pamięć nie jest przydzielana dla zmiennych pośrednich. Wyskakuje również przy pierwszym znalezieniu. Ale in
operator jest zawsze O(n)
na listach (patrz tutaj ).
Inną proponowaną opcją jest hybryda polegająca na iteracji jednej z list, konwersji drugiej w zestawie i przetestowaniu członkostwa w tym zestawie, na przykład:
a = set(a); any(i in a for i in b)
Czwartym podejściem jest wykorzystanie isdisjoint()
metody (zamrożonych) zbiorów (patrz tutaj ), na przykład:
not set(a).isdisjoint(b)
Jeśli wyszukiwane elementy znajdują się blisko początku tablicy (np. Są posortowane), preferowane jest wyrażenie generatora, ponieważ metoda intersection zestawów musi przydzielić nową pamięć dla zmiennych pośrednich:
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
Oto wykres czasu wykonywania dla tego przykładu w funkcji rozmiaru listy:
Zauważ, że obie osie są logarytmiczne. Stanowi to najlepszy przypadek dla wyrażenia generatora. Jak widać, isdisjoint()
metoda jest lepsza dla list o bardzo małych rozmiarach, natomiast wyrażenie generatora jest lepsze dla list o większych rozmiarach.
Z drugiej strony, ponieważ wyszukiwanie rozpoczyna się od początku wyrażenia hybrydy i generatora, jeśli element współdzielony znajduje się systematycznie na końcu tablicy (lub obie listy nie mają wspólnych wartości), wówczas podejścia rozłączne i zestaw przecięć są znacznie szybciej niż wyrażenie generatora i podejście hybrydowe.
>>> 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
Warto zauważyć, że wyrażenie generatora jest znacznie wolniejsze dla większych list. To tylko dla 1000 powtórzeń, zamiast 100000 dla poprzedniej figury. Ta konfiguracja jest również dobrze przybliżana, gdy żadne elementy nie są współdzielone, i jest najlepszym przypadkiem dla podejść rozłącznych i zestawionych przecięć.
Oto dwie analizy z wykorzystaniem liczb losowych (zamiast fałszować konfigurację na korzyść jednej lub drugiej techniki):
Duża szansa na udostępnienie: elementy są pobierane losowo [1, 2*len(a)]
. Mała szansa na udostępnienie: elementy są pobierane losowo [1, 1000*len(a)]
.
Do tej pory ta analiza zakładała, że obie listy są tej samej wielkości. W przypadku dwóch list o różnych rozmiarach, na przykład a
jest znacznie mniejsza, isdisjoint()
jest zawsze szybsza:
Upewnij się, że a
lista jest mniejsza, w przeciwnym razie wydajność spadnie. W tym eksperymencie a
rozmiar listy został ustawiony jako stały 5
.
W podsumowaniu:
- Jeśli listy są bardzo małe (<10 elementów),
not set(a).isdisjoint(b)
jest zawsze najszybsze.
- Jeśli elementy na listach są posortowane lub mają regularną strukturę, z której można skorzystać, wyrażenie generatora
any(i in a for i in b)
jest najszybsze w przypadku dużych list;
- Przetestuj ustawione przecięcie z
not set(a).isdisjoint(b)
, które jest zawsze szybsze niż bool(set(a) & set(b))
.
- Metoda hybrydowa „iteruj po liście, testuj na zestawie”
a = set(a); any(i in a for i in b)
jest generalnie wolniejsza niż inne metody.
- Wyrażenie generatora i hybryda są znacznie wolniejsze niż dwa inne podejścia, jeśli chodzi o listy bez współdzielenia elementów.
W większości przypadków użycie tej isdisjoint()
metody jest najlepszym podejściem, ponieważ wykonanie wyrażenia generatora zajmie znacznie więcej czasu, ponieważ jest bardzo nieefektywne, gdy żadne elementy nie są współużytkowane.
len(...) > 0
ponieważbool(set([]))
daje wartość Fałsz. I oczywiście, jeśli na początku trzymasz listy jako zestawy, zaoszczędzisz na kosztach tworzenia zestawów.