Kısa cevap : kullanın not set(a).isdisjoint(b)
, genellikle en hızlısıdır.
İki liste a
olup olmadığını test etmenin ve b
herhangi bir öğeyi paylaşmanın dört yaygın yolu vardır . İlk seçenek, her ikisini de kümelere dönüştürmek ve kesişimlerini kontrol etmektir, örneğin:
bool(set(a) & set(b))
Çünkü setleri onları olduğu arama, Python bir karma tablo kullanılarak depolanırO(1)
(bkz burada Python operatörleri karmaşıklığı hakkında daha fazla bilgi için). Teorik olarak, bu O(n+m)
ortalama üzerinde n
ve m
listelerde nesneleri a
ve b
. Ancak 1) önce listelerden ihmal edilemeyecek bir süre alabilen kümeler oluşturmalıdır ve 2) karma çarpışmaların verileriniz arasında seyrek olduğunu varsayar.
Bunu yapmanın ikinci yolu, listelerde yineleme yapan bir üretici ifadesi kullanmaktır, örneğin:
any(i in a for i in b)
Bu, yerinde aramaya izin verir, böylece ara değişkenler için yeni bellek tahsis edilmez. Aynı zamanda ilk bulgudan kurtulur. Ancak in
operatör her zaman O(n)
listelerde bulunur ( buraya bakın ).
Önerilen diğer bir seçenek ise, listeden birini yinelemek, diğerini bir sette dönüştürmek ve bu sette üyeliği test etmek için hibrittir, örneğin:
a = set(a); any(i in a for i in b)
Dördüncü bir yaklaşım, isdisjoint()
(dondurulmuş) kümelerin yönteminden yararlanmaktır ( buraya bakın ), örneğin:
not set(a).isdisjoint(b)
Aradığınız elemanlar bir dizinin başlangıcına yakınsa (örneğin sıralanırsa), setler kesişimi yönteminin ara değişkenler için yeni bellek ayırması gerektiğinden, oluşturucu ifadesi tercih edilir:
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
Liste boyutuna bağlı olarak bu örnek için yürütme süresinin grafiğini aşağıda bulabilirsiniz:
Her iki eksenin de logaritmik olduğuna dikkat edin. Bu, üretici ifadesi için en iyi durumu temsil eder. Görüldüğü gibi, isdisjoint()
yöntem çok küçük liste boyutları için daha iyidir, oysa oluşturucu ifadesi daha büyük liste boyutları için daha iyidir.
Öte yandan, arama hibrit ve üretici ifadesinin başlangıcıyla başladığından, eğer paylaşılan eleman sistematik olarak dizinin sonunda ise (veya her iki liste de herhangi bir değeri paylaşmıyorsa), ayrık ve küme kesişim yaklaşımları o zaman jeneratör ifadesi ve hibrit yaklaşımdan çok daha hızlı.
>>> 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
Daha büyük liste boyutları için oluşturucu ifadesinin çok daha yavaş olduğuna dikkat etmek ilginçtir. Bu, önceki rakam için 100000 yerine sadece 1000 tekrar içindir. Bu kurulum aynı zamanda hiçbir öğe paylaşılmadığında iyi bir şekilde yaklaşır ve ayrık ve kesişme yaklaşımları için en iyi durumdur.
İşte rastgele sayılar kullanan iki analiz (bir tekniği veya diğerini tercih etmek için kurulumu karıştırmak yerine):
Yüksek paylaşma şansı: öğelerden rastgele alınır [1, 2*len(a)]
. Düşük paylaşım şansı: öğelerden rastgele alınır [1, 1000*len(a)]
.
Şimdiye kadar, bu analiz her iki listenin de aynı büyüklükte olduğunu varsayıyordu. Farklı boyutlarda iki liste olması durumunda, örneğin a
çok daha küçükse, isdisjoint()
her zaman daha hızlıdır:
a
Listenin daha küçük olduğundan emin olun , aksi takdirde performans düşer. Bu deneyde, a
liste boyutu olarak sabit ayarlandı 5
.
Özetle:
- Listeler çok küçükse (<10 öğe),
not set(a).isdisjoint(b)
her zaman en hızlısıdır.
- Listelerdeki öğeler sıralanırsa veya yararlanabileceğiniz düzenli bir yapıya sahipse, oluşturucu ifadesi
any(i in a for i in b)
büyük liste boyutlarında en hızlısıdır;
not set(a).isdisjoint(b)
Her zaman daha hızlı olan küme kesişimini test edin bool(set(a) & set(b))
.
- Karma "listeden yineleme, sette test etme"
a = set(a); any(i in a for i in b)
genellikle diğer yöntemlerden daha yavaştır.
- Oluşturucu ifadesi ve melez, öğeleri paylaşmadan listeler söz konusu olduğunda diğer iki yaklaşımdan çok daha yavaştır.
Çoğu durumda, isdisjoint()
hiçbir öğe paylaşılmadığında çok verimsiz olduğundan, oluşturucu ifadenin yürütülmesi çok daha uzun süreceği için yöntemi kullanmak en iyi yaklaşımdır.
len(...) > 0
çünkübool(set([]))
verimleri false. Ve elbette listelerinizi başlangıçta kümeler halinde tutarsanız, set oluşturma ek yükünü kaydedersiniz.