Saya menggunakan kamus sebagai tabel pencarian, tapi aku mulai bertanya-tanya jika sebuah daftar akan lebih baik untuk aplikasi saya -- jumlah entri dalam tabel pencarian wasn't yang besar. Aku tahu daftar menggunakan array C di bawah tenda yang membuat saya menyimpulkan bahwa lookup di daftar dengan hanya beberapa item akan lebih baik dari pada kamus (mengakses beberapa elemen dalam array adalah lebih cepat dari komputasi hash).
Saya memutuskan untuk profil alternatif tapi hasilnya mengejutkan saya. Daftar lookup hanya baik dengan elemen tunggal! Lihat gambar berikut ini (log-log plot):
Jadi di sini muncul pertanyaan: Mengapa daftar pencarian melakukan begitu buruk? Apa yang saya hilang?
Di samping pertanyaan, sesuatu yang lain yang disebut perhatian saya sedikit "diskontinuitas" di dict lookup waktu setelah sekitar 1000 entri. Saya merencanakan dict lookup waktu sendirian untuk menunjukkan hal itu.
p.s.1 aku tahu tentang O(n) vs O(1) diamortisasi waktu untuk array dan hash tabel, tetapi biasanya hal itu untuk sejumlah kecil unsur-unsur iterasi array adalah lebih baik daripada menggunakan tabel hash.
p.s.2 Berikut adalah kode yang saya digunakan untuk membandingkan dict dan daftar lookup kali:
import timeit
lengths = [2 ** i for i in xrange(15)]
list_time = []
dict_time = []
for l in lengths:
list_time.append(timeit.timeit('%i in d' % (l/2), 'd=range(%i)' % l))
dict_time.append(timeit.timeit('%i in d' % (l/2),
'd=dict.fromkeys(range(%i))' % l))
print l, list_time[-1], dict_time[-1]
p.s.3 Menggunakan Python 2.7.13
aku tahu daftar menggunakan array C di bawah tenda yang membuat saya menyimpulkan bahwa lookup di daftar dengan hanya beberapa item akan lebih baik dari pada kamus (mengakses beberapa elemen dalam array adalah lebih cepat dari komputasi hash).
Mengakses beberapa elemen array yang murah, pasti, tapi komputasi ==
ini sangat berat di Python. Melihat bahwa spike di kedua grafik? Yang's biaya komputasi ==
untuk dua ints di sana.
Daftar lookup perlu untuk menghitung ==
lebih banyak dari yang anda dict pencarian dilakukan.
Sementara itu, komputasi hash mungkin cukup berat operasi untuk banyak benda-benda, tetapi untuk semua ints yang terlibat di sini, mereka hanya hash untuk diri mereka sendiri. (-1 akan hash ke -2, dan besar bulat (secara teknis `lama) akan hash untuk bilangan bulat yang lebih kecil, tapi itu doesn't berlaku di sini.)
Dict lookup isn't benar-benar buruk dalam Python, terutama ketika kunci anda hanya berturut-turut berbagai int. Semua ints di sini hash untuk diri mereka sendiri, dan Python menggunakan custom buka skema pengalamatan bukan chaining, sehingga semua tombol end up hampir sama yang berdekatan dalam memori jika anda'd digunakan daftar (yang adalah untuk mengatakan, pointer ke tombol end up di berbagai bersebelahan `PyDictEntry ini). Pencarian prosedur yang lebih cepat, dan dalam kasus uji, itu selalu adalah kunci yang tepat pada probe pertama.
Oke, kembali ke lonjakan grafik 2. Lonjakan pencarian kali di 1024 entri di grafik kedua adalah karena untuk ukuran yang lebih kecil, bulat anda sedang mencari semua <= 256, sehingga mereka semua jatuh dalam kisaran CPython's kecil integer cache. Referensi implementasi Python terus kanonik integer benda-benda untuk semua bilangan bulat dari -5 sampai 256, inklusif. Untuk bilangan bulat, Python dapat menggunakan cepat pointer perbandingan untuk menghindari pergi melalui (anehnya heavyweight) proses komputasi ==
. Untuk bilangan bulat yang lebih besar, argumen untuk di
tidak lagi objek yang sama sebagai pencocokan integer dalam dict, dan Python memiliki untuk pergi melalui seluruh ==
proses.
Jawaban singkatnya adalah bahwa daftar menggunakan pencarian linear dan dicts menggunakan diamortisasi O(1) pencarian.
Selain itu, dict pencarian dapat melewatkan suatu uji kesamaan baik ketika 1) nilai hash don't pertandingan atau 2) bila ada identitas pertandingan. Daftar hanya mendapatkan keuntungan dari identitas-menyiratkan kesetaraan optimasi.
Kembali di tahun 2008, saya berbicara tentang hal ini di mana anda'll menemukan semua rincian:
Kira-kira logika untuk mencari daftar adalah:
for element in s:
if element is target:
# fast check for identity implies equality
return True
if element == target:
# slower check for actual equality
return True
return False
Untuk dicts logika adalah kira-kira:
h = hash(target)
for i in probe_sequence(h, len(table)):
element = key_table[i]
if element is UNUSED:
raise KeyError(target)
if element is target:
# fast path for identity implies equality
return value_table[i]
if h != h_table[i]:
# unequal hashes implies unequal keys
continue
if element == target:
# slower check for actual equality
return value_table[i]
Kamus tabel hash biasanya antara sepertiga dan dua-pertiga penuh, sehingga mereka cenderung memiliki beberapa tabrakan (beberapa perjalanan di seluruh loop yang ditunjukkan di atas) terlepas dari ukuran. Juga, nilai hash check mencegah tak lambat kesetaraan cek (kesempatan terbuang cek kesetaraan adalah sekitar 1 dalam 2**64).
Jika waktu anda berfokus pada bilangan bulat, ada beberapa efek lain yang berperan serta. Bahwa hash dari int int itu sendiri, sehingga hashing adalah sangat cepat. Juga, itu berarti bahwa jika anda're menyimpan berturut-turut bilangan bulat, ada cenderung tidak ada benturan sama sekali.
Anda mengatakan "mengakses beberapa elemen dalam array adalah lebih cepat dari komputasi hash".
Sederhana hashing aturan untuk string mungkin hanya penjumlahan (dengan modulo di akhir). Ini adalah branchless operasi yang dapat menguntungkan dibandingkan dengan karakter perbandingan, terutama ketika ada pertandingan yang panjang di awalan.