Saya sedang mengerjakan tabel hash dalam bahasa C dan saya sedang menguji fungsi hash untuk string.
Fungsi pertama yang saya coba adalah menambahkan kode ascii dan menggunakan modulo (% 100) tetapi saya mendapatkan hasil yang buruk dengan tes pertama data: 40 tabrakan untuk 130 kata.
Data masukan akhir akan berisi 8 000 kata (ini adalah kamus yang disimpan dalam sebuah file). Tabel hash dideklarasikan sebagai tabel int [10000] dan berisi posisi kata dalam file txt.
Pertanyaan pertama adalah algoritma mana yang terbaik untuk hashing string? dan bagaimana cara menentukan ukuran tabel hash?
Terima kasih sebelumnya !
:-)
Jawaban:
Saya mendapatkan hasil yang bagus dengan
djb2
Dan Bernstein.sumber
size_t
atau nilai unsigned lainnya (seperti unsigned long dalam kode ini). The pemanggil bertanggung jawab untuk mengambil modulo hasilnya untuk menyesuaikan dengan tabel hash. Pemanggil mengontrol slot tabel yang sedang di-hash; bukan fungsinya. Itu hanya mengembalikan beberapa nomor yang tidak ditandatangani.Pertama, Anda biasanya tidak ingin menggunakan hash kriptografi untuk tabel hash. Algoritme yang sangat cepat menurut standar kriptografi masih sangat lambat menurut standar tabel hash.
Kedua, Anda ingin memastikan bahwa setiap bit masukan dapat / akan mempengaruhi hasilnya. Salah satu cara mudah untuk melakukannya adalah dengan memutar hasil saat ini dengan beberapa bit, kemudian XOR kode hash saat ini dengan byte saat ini. Ulangi sampai Anda mencapai ujung benang. Perhatikan bahwa Anda biasanya tidak ingin rotasi menjadi kelipatan genap dari ukuran byte juga.
Misalnya, dengan asumsi kasus umum 8 bit byte, Anda mungkin memutar 5 bit:
Sunting: Perhatikan juga bahwa 10.000 slot jarang merupakan pilihan yang baik untuk ukuran tabel hash. Anda biasanya menginginkan salah satu dari dua hal: Anda ingin bilangan prima sebagai ukuran (diperlukan untuk memastikan kebenaran dengan beberapa jenis resolusi hash) atau pangkat 2 (sehingga mengurangi nilai ke kisaran yang benar dapat dilakukan dengan sederhana bit-mask).
sumber
Wikipedia menunjukkan fungsi hash string yang bagus yang disebut Jenkins One At A Time Hash. Itu juga mengutip versi perbaikan dari hash ini.
sumber
Ada sejumlah implementasi hashtable yang ada untuk C, mulai dari library standar C hcreate / hdestroy / hsearch, hingga yang ada di APR dan glib , yang juga menyediakan fungsi hash bawaan. Saya sangat merekomendasikan untuk menggunakannya daripada menciptakan fungsi hashtable atau hash Anda sendiri; mereka telah sangat dioptimalkan untuk kasus penggunaan umum.
Namun, jika kumpulan data Anda statis, solusi terbaik Anda mungkin menggunakan hash yang sempurna . gperf akan menghasilkan hash yang sempurna untuk Anda untuk kumpulan data tertentu.
sumber
djb2 memiliki 317 benturan untuk kamus bahasa Inggris 466k ini sementara MurmurHash tidak memiliki satupun untuk hash 64 bit, dan 21 untuk hash 32 bit (sekitar 25 diharapkan untuk hash 466k acak 32 bit). Rekomendasi saya adalah menggunakan MurmurHash jika tersedia, ini sangat cepat, karena membutuhkan beberapa byte sekaligus. Tetapi jika Anda memerlukan fungsi hash yang sederhana dan singkat untuk menyalin dan menempel ke proyek Anda, saya akan merekomendasikan menggunakan versi murmur satu byte-at-a-time:
Ukuran optimal dari tabel hash adalah - singkatnya - sebesar mungkin sambil tetap masuk ke dalam memori. Karena kita biasanya tidak tahu atau ingin mencari berapa banyak memori yang kita miliki, dan bahkan mungkin berubah, ukuran tabel hash yang optimal kira-kira 2x jumlah elemen yang diharapkan untuk disimpan dalam tabel. Mengalokasikan lebih dari itu akan membuat tabel hash Anda lebih cepat tetapi dengan pengembalian yang berkurang dengan cepat, membuat tabel hash Anda lebih kecil dari itu akan membuatnya lebih lambat secara eksponensial. Ini karena ada trade-off non-linear antara kompleksitas ruang dan waktu untuk tabel hash, dengan faktor beban optimal 2-sqrt (2) = 0,58 ... tampaknya.
sumber
Pertama, apakah 40 tabrakan untuk 130 kata di-hash ke 0..99 buruk? Anda tidak dapat mengharapkan hashing yang sempurna jika Anda tidak mengambil langkah-langkah khusus untuk mewujudkannya. Fungsi hash biasa tidak akan memiliki tabrakan yang lebih sedikit daripada generator acak di sebagian besar waktu.
Fungsi hash dengan reputasi yang baik adalah MurmurHash3 .
Terakhir, mengenai ukuran tabel hash, itu sangat tergantung pada jenis tabel hash yang Anda pikirkan, terutama, apakah bucket dapat diperpanjang atau satu slot. Jika bucket dapat diperpanjang, sekali lagi ada pilihan: Anda memilih panjang bucket rata-rata untuk batasan memori / kecepatan yang Anda miliki.
sumber
n - m * (1 - ((m-1)/m)^n) = 57.075...
. 40 tabrakan lebih baik daripada yang diharapkan secara kebetulan (46 hingga 70 dengan p-score 0,999). Fungsi hash yang dimaksud lebih seragam daripada jika acak atau kita menyaksikan peristiwa yang sangat langka.Meskipun
djb2
, seperti yang disajikan di stackoverflow oleh cnicutar , hampir pasti lebih baik, saya rasa ada baiknya juga menampilkan hash K&R :1) Ternyata algoritma hash yang buruk , seperti yang disajikan dalam K&R 1st edition ( sumber )
2) Mungkin algoritma hash yang lumayan bagus, seperti yang disajikan dalam K&R versi 2 (diverifikasi oleh saya di halaman 144 buku); NB: pastikan untuk menghapus
% HASHSIZE
dari pernyataan return jika Anda berencana melakukan modulus sizing-to-your-array-length di luar algoritma hash. Juga, saya sarankan Anda membuat tipe return dan "hashval"unsigned long
daripada yang simpleunsigned
(int).Perhatikan bahwa jelas dari kedua algoritme bahwa salah satu alasan hash edisi pertama sangat buruk adalah karena TIDAK mempertimbangkan urutan karakter string , sehingga
hash("ab")
akan mengembalikan nilai yang sama sepertihash("ba")
. Ini tidak demikian dengan hash edisi ke-2, yang (jauh lebih baik!) Mengembalikan dua nilai berbeda untuk string tersebut.Fungsi hashing GCC C ++ 11 yang digunakan untuk
unordered_map
(template tabel hash) danunordered_set
(template kumpulan hash) tampak seperti berikut.Kode:
sumber
Saya telah mencoba fungsi hash ini dan mendapatkan hasil sebagai berikut. Saya memiliki sekitar 960 ^ 3 entri, masing-masing sepanjang 64 byte, 64 karakter dalam urutan berbeda, nilai hash 32bit. Kode dari sini .
Satu hal yang aneh adalah bahwa hampir semua fungsi hash memiliki tingkat tabrakan 6% untuk data saya.
sumber
Satu hal yang saya gunakan dengan hasil yang baik adalah yang berikut ini (saya tidak tahu apakah sudah disebutkan karena saya tidak ingat namanya).
Anda menghitung sebelumnya T tabel dengan nomor acak untuk setiap karakter dalam alfabet kunci Anda [0,255]. Anda mencirikan kunci Anda 'k0 k1 k2 ... kN' dengan mengambil T [k0] xor T [k1] xor ... xor T [kN]. Anda dapat dengan mudah menunjukkan bahwa ini sama acaknya dengan generator bilangan acak Anda dan secara komputasi sangat layak dan jika Anda benar-benar mengalami kejadian yang sangat buruk dengan banyak tabrakan, Anda dapat mengulangi semuanya menggunakan kumpulan bilangan acak baru.
sumber