Risposta breve : usare not set(a).isdisjoint(b), è generalmente il più veloce.
Esistono quattro modi comuni per verificare se due elenchi ae bcondividere elementi. La prima opzione è convertire entrambi in set e controllare la loro intersezione, come tale:
bool(set(a) & set(b))
Poiché i set vengono archiviati utilizzando una tabella hash in Python, la ricerca èO(1) (vedere qui per ulteriori informazioni sulla complessità degli operatori in Python). Teoricamente, questo è O(n+m)in media per ne moggetti negli elenchi ae b. Ma 1) deve prima creare serie fuori dagli elenchi, il che può richiedere una quantità non trascurabile di tempo, e 2) supporre che le collisioni di hashing siano sparse tra i tuoi dati.
Il secondo modo per farlo è usare un'espressione del generatore che esegue l'iterazione sugli elenchi, come ad esempio:
any(i in a for i in b)
Ciò consente la ricerca sul posto, quindi non viene allocata nuova memoria per le variabili intermedie. Si salva anche alla prima scoperta. Ma l' inoperatore è sempre O(n)in lista (vedi qui ).
Un'altra opzione proposta è un ibrido per scorrere l'elenco, convertire l'altro in un set e testare l'appartenenza a questo set, in questo modo:
a = set(a); any(i in a for i in b)
Un quarto approccio consiste nell'utilizzare il isdisjoint()metodo degli insiemi (congelati) (vedere qui ), ad esempio:
not set(a).isdisjoint(b)
Se gli elementi ricercati sono vicini all'inizio di un array (ad es. È ordinato), viene favorita l'espressione del generatore, poiché il metodo di intersezione insiemi deve allocare nuova memoria per le variabili intermedie:
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
Ecco un grafico del tempo di esecuzione per questo esempio in funzione della dimensione dell'elenco:

Si noti che entrambi gli assi sono logaritmici. Questo rappresenta il caso migliore per l'espressione del generatore. Come si può vedere, ilisdisjoint() metodo è migliore per dimensioni di elenco molto piccole, mentre l'espressione del generatore è migliore per dimensioni di elenco più grandi.
D'altra parte, poiché la ricerca inizia con l'inizio dell'espressione ibrida e del generatore, se l'elemento condiviso è sistematicamente alla fine dell'array (o entrambi gli elenchi non condividono alcun valore), gli approcci di intersezione disgiunti e impostati sono quindi molto più veloce dell'espressione del generatore e dell'approccio ibrido.
>>> 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

È interessante notare che l'espressione del generatore è molto più lenta per elenchi di dimensioni maggiori. Questo è solo per 1000 ripetizioni, anziché 100000 per la figura precedente. Questa impostazione si avvicina bene anche quando non sono condivisi elementi ed è il caso migliore per gli approcci di intersezione disgiunti e impostati.
Ecco due analisi usando numeri casuali (invece di attrezzare l'installazione per favorire una tecnica o un'altra):

Elevata possibilità di condivisione: gli elementi vengono presi casualmente [1, 2*len(a)] . Bassa possibilità di condivisione: gli elementi vengono presi casualmente[1, 1000*len(a)] .
Fino ad ora, questa analisi supponeva che entrambe le liste avessero le stesse dimensioni. Nel caso di due elenchi di dimensioni diverse, ad esempio aè molto più piccolo, isdisjoint()è sempre più veloce:

Assicurarsi che l' aelenco sia più piccolo, altrimenti le prestazioni diminuiscono. In questo esperimento, la adimensione dell'elenco è stata impostata su costante5 .
In sintesi:
- Se gli elenchi sono molto piccoli (<10 elementi),
not set(a).isdisjoint(b) è sempre il più veloce.
- Se gli elementi negli elenchi sono ordinati o hanno una struttura regolare di cui puoi trarre vantaggio, l'espressione del generatore
any(i in a for i in b) è la più veloce su elenchi di grandi dimensioni;
- Prova l'intersezione impostata con
not set(a).isdisjoint(b), che è sempre più veloce dibool(set(a) & set(b)) .
- L'ibrido "scorre attraverso l'elenco, prova sul set"
a = set(a); any(i in a for i in b) è generalmente più lento di altri metodi.
- L'espressione del generatore e l'ibrido sono molto più lenti rispetto agli altri due approcci quando si tratta di elenchi senza condividere elementi.
Nella maggior parte dei casi, l'utilizzo del isdisjoint()metodo è l'approccio migliore in quanto l'esecuzione del generatore richiederà molto più tempo per l'esecuzione, poiché è molto inefficiente quando non sono condivisi elementi.
len(...) > 0perchébool(set([]))produce Falso. E, naturalmente, se hai tenuto le tue liste come set per cominciare, risparmierai il sovraccarico di creazione del set.