Jawaban singkat : gunakan not set(a).isdisjoint(b)
, umumnya yang tercepat.
Ada empat cara umum untuk menguji apakah dua daftar a
dan b
berbagi item apa pun. Opsi pertama adalah mengonversi keduanya menjadi set dan memeriksa persimpangannya, seperti:
bool(set(a) & set(b))
Karena set disimpan menggunakan tabel hash di Python, pencarian mereka adalahO(1)
(lihat di sini untuk informasi lebih lanjut tentang kompleksitas operator di Python). Secara teoritis, ini O(n+m)
rata-rata untuk n
dan m
objek dalam daftar a
dan b
. 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:
any(i in a for i in b)
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 ada O(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:
a = set(a); any(i in a for i in b)
Pendekatan keempat adalah mengambil keuntungan dari isdisjoint()
metode set (beku) (lihat di sini ), misalnya:
not set(a).isdisjoint(b)
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:
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
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.
>>> 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
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:
- Jika daftar sangat kecil (<10 elemen),
not set(a).isdisjoint(b)
selalu yang tercepat.
- Jika elemen dalam daftar diurutkan atau memiliki struktur reguler yang dapat Anda manfaatkan, ekspresi generator
any(i in a for i in b)
adalah yang tercepat pada ukuran daftar besar;
- Tes persimpangan dengan set
not set(a).isdisjoint(b)
, yang selalu lebih cepat dari bool(set(a) & set(b))
.
- Hibrid "iterate through list, test on set"
a = set(a); any(i in a for i in b)
umumnya lebih lambat daripada metode lain.
- Ekspresi generator dan hibrida jauh lebih lambat daripada dua pendekatan lain ketika datang ke daftar tanpa berbagi elemen.
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.
len(...) > 0
karenabool(set([]))
menghasilkan False. Dan tentu saja jika Anda menyimpan daftar sebagai set untuk memulai, Anda akan menghemat set overhead penciptaan.