Saya ingin memeriksa apakah ada item dalam satu daftar ada di daftar lain. Saya dapat melakukannya hanya dengan kode di bawah ini, tetapi saya menduga mungkin ada fungsi perpustakaan untuk melakukan ini. Jika tidak, apakah ada metode yang lebih pythonic untuk mencapai hasil yang sama.
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
sumber
sumber
len(...) > 0
karenabool(set([]))
menghasilkan False. Dan tentu saja jika Anda menyimpan daftar sebagai set untuk memulai, Anda akan menghemat set overhead penciptaan.True
dari1
danFalse
dari0
.not set([1]).isdisjoint([True])
dapatkanTrue
, sama dengan solusi lain.Jawaban:
Jawaban singkat : gunakan
not set(a).isdisjoint(b)
, umumnya yang tercepat.Ada empat cara umum untuk menguji apakah dua daftar
a
danb
berbagi item apa pun. Opsi pertama adalah mengonversi keduanya menjadi set dan memeriksa persimpangannya, seperti:Karena set disimpan menggunakan tabel hash di Python, pencarian mereka adalah
O(1)
(lihat di sini untuk informasi lebih lanjut tentang kompleksitas operator di Python). Secara teoritis, iniO(n+m)
rata-rata untukn
danm
objek dalam daftara
danb
. Tapi 1) pertama-tama ia harus membuat set dari daftar, yang dapat mengambil jumlah waktu yang tidak dapat diabaikan, dan 2) itu mengandaikan bahwa tabrakan hashing jarang di antara data Anda.Cara kedua untuk melakukannya adalah menggunakan ekspresi generator yang melakukan iterasi pada daftar, seperti:
Ini memungkinkan untuk mencari di tempat, sehingga tidak ada memori baru yang dialokasikan untuk variabel perantara. Itu juga menyelamatkan pada penemuan pertama. Tetapi
in
operator selalu adaO(n)
dalam daftar (lihat di sini ).Opsi lain yang diusulkan adalah hibrida untuk beralih melalui salah satu daftar, mengonversi yang lain dalam satu set dan menguji keanggotaan pada set ini, seperti:
Pendekatan keempat adalah mengambil keuntungan dari
isdisjoint()
metode set (beku) (lihat di sini ), misalnya:Jika elemen yang Anda cari berada di dekat awal array (misalnya diurutkan), ekspresi generator lebih disukai, karena metode persimpangan set harus mengalokasikan memori baru untuk variabel perantara:
Berikut adalah grafik waktu eksekusi untuk contoh ini dalam fungsi ukuran daftar:
Perhatikan bahwa kedua sumbu bersifat logaritmik. Ini mewakili kasus terbaik untuk ekspresi generator. Seperti dapat dilihat,
isdisjoint()
metode ini lebih baik untuk ukuran daftar yang sangat kecil, sedangkan ekspresi generator lebih baik untuk ukuran daftar yang lebih besar.Di sisi lain, saat pencarian dimulai dengan awal untuk ekspresi hybrid dan generator, jika elemen bersama secara sistematis di akhir array (atau kedua daftar tidak berbagi nilai apa pun), maka pendekatan persimpangan disjoint dan set kemudian jauh lebih cepat daripada ekspresi generator dan pendekatan hybrid.
Sangat menarik untuk dicatat bahwa ekspresi generator jauh lebih lambat untuk ukuran daftar yang lebih besar. Ini hanya untuk 1000 repetisi, bukan 100000 untuk gambar sebelumnya. Pengaturan ini juga mendekati dengan baik ketika ketika tidak ada elemen yang dibagikan, dan merupakan kasus terbaik untuk pemisahan dan mengatur pendekatan persimpangan.
Berikut adalah dua analisis menggunakan angka acak (alih-alih mencurangi pengaturan untuk mendukung satu teknik atau lainnya):
Peluang berbagi yang tinggi: elemen diambil secara acak
[1, 2*len(a)]
. Peluang berbagi yang rendah: elemen diambil secara acak[1, 1000*len(a)]
.Hingga kini, analisis ini seharusnya kedua daftar memiliki ukuran yang sama. Dalam hal dua daftar ukuran yang berbeda, misalnya
a
jauh lebih kecil,isdisjoint()
selalu lebih cepat:Pastikan
a
daftar tersebut lebih kecil, jika tidak kinerjanya menurun. Dalam percobaan ini,a
ukuran daftar ditetapkan menjadi5
.Singkatnya:
not set(a).isdisjoint(b)
selalu yang tercepat.any(i in a for i in b)
adalah yang tercepat pada ukuran daftar besar;not set(a).isdisjoint(b)
, yang selalu lebih cepat daribool(set(a) & set(b))
.a = set(a); any(i in a for i in b)
umumnya lebih lambat daripada metode lain.Dalam kebanyakan kasus, menggunakan
isdisjoint()
metode adalah pendekatan terbaik karena ekspresi generator akan membutuhkan waktu lebih lama untuk dieksekusi, karena sangat tidak efisien ketika tidak ada elemen yang dibagikan.sumber
any
keluar pada nilai non-False pertama. Dengan menggunakan daftar di mana satu-satunya nilai yang cocok adalah di akhir, kami mendapatkan ini: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
... dan hanya dengan 1000 iterasi.not set(a).isdisjoint(b)
untuk menguji apakah dua daftar berbagi anggota.set(a).isdisjoint(b)
kembaliTrue
jika kedua daftar tidak membagikan anggota. Jawabannya harus diedit?Catatan: di atas mengasumsikan bahwa Anda menginginkan boolean sebagai jawabannya. Jika yang Anda butuhkan adalah ekspresi untuk digunakan dalam
if
pernyataan, cukup gunakanif set(a) & set(b):
sumber
O(n + m)
. Dugaan saya adalah bahwa set diimplementasikan menggunakan tabel hash, dan dengan demikianin
operator dapat bekerja dalamO(1)
waktu (kecuali dalam kasus degenerasi). Apakah ini benar? Jika demikian, mengingat bahwa tabel hash memiliki kinerja pencarian kasus terburukO(n)
, apakah ini berarti bahwa dalam kasus tidak seperti terburuk itu akan memilikiO(n * m)
kinerja?O(n)
kinerja pencarian;), lihat pastebin.com/Kn3kAW7u Hanya untuk lafs.Ini asimptotik optimal (kasus terburuk O (n + m)), dan mungkin lebih baik daripada pendekatan persimpangan karena
any
hubungan arus pendek.Misalnya:
akan mengembalikan True segera setelah tiba
3 in sb
EDIT: Variasi lain (dengan terima kasih kepada Dave Kirby):
Ini bergantung pada
imap
iterator, yang diimplementasikan dalam C, daripada pemahaman generator. Ini juga digunakansb.__contains__
sebagai fungsi pemetaan. Saya tidak tahu seberapa besar perbedaan kinerja ini. Itu masih akan mengalami arus pendek.sumber
any(itertools.imap(sb.__contains__, a))
yang seharusnya lebih cepat karena tidak menggunakan fungsi lambda.Anda juga bisa menggunakan
any
dengan pemahaman daftar:sumber
[]
) dan itu akan berjalan lebih cepat dan menggunakan lebih sedikit memori, tetapi waktunya masih akan O (n * m).Dengan python 2.6 atau yang lebih baru, Anda dapat melakukan:
sumber
Anda dapat menggunakan ekspresi generator fungsi / fungsi bawaan apa saja:
Seperti yang ditunjukkan oleh John dan Lie, ini memberikan hasil yang salah ketika untuk setiap saya dibagikan oleh dua daftar, bool (i) == Salah. Harus:
sumber
bool(x)
False. Dalam contoh Lie Ryan, x adalah 0. Hanya memperbaikiany(True for i in a if i in b)
yang lebih baik ditulis seperti yang sudah terlihatany(i in b for i in a)
.x
di persimpangan adalah seperti yangbool(x)
adalahFalse
.Pertanyaan ini cukup lama, tetapi saya perhatikan bahwa sementara orang-orang berdebat set vs daftar, bahwa tidak ada yang berpikir untuk menggunakannya bersama-sama. Mengikuti contoh Soravux,
Kasus terburuk untuk daftar:
Dan kasus terbaik untuk daftar:
Jadi, bahkan lebih cepat daripada iterasi melalui dua daftar adalah iterasi daftar untuk melihat apakah itu dalam set, yang masuk akal karena memeriksa apakah nomor dalam set membutuhkan waktu yang konstan sementara memeriksa dengan iterasi melalui daftar membutuhkan waktu sebanding dengan panjang Daftar.
Jadi, kesimpulan saya adalah bahwa iterate melalui daftar, dan periksa apakah sudah di set .
sumber
isdisjoint()
metode pada set (beku) seperti yang ditunjukkan oleh @Toughy bahkan lebih baik: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,00913715362548828jika Anda tidak peduli apa elemen yang tumpang tindih mungkin, Anda cukup memeriksa
len
daftar gabungan vs daftar digabungkan sebagai satu set. Jika ada elemen yang tumpang tindih, set akan lebih pendek:len(set(a+b+c))==len(a+b+c)
mengembalikan True, jika tidak ada tumpang tindih.sumber
Saya akan melempar yang lain dengan gaya pemrograman fungsional:
Penjelasan:
mengembalikan daftar boolean di mana elemen
b
ditemukan dia
. Daftar itu kemudian diteruskan keany
, yang hanya mengembalikanTrue
jika ada elemenTrue
.sumber