xor
adalah fungsi default berbahaya untuk digunakan saat hashing. Itu lebih baik daripada and
dan or
, tapi itu tidak banyak bicara.
xor
simetris, sehingga urutan unsur-unsurnya hilang. Jadi "bad"
hash akan menggabungkan sama dengan "dab"
.
xor
memetakan nilai identik berpasangan ke nol, dan Anda harus menghindari pemetaan nilai "umum" ke nol:
Jadi (a,a)
dipetakan ke 0, dan (b,b)
juga dipetakan ke 0. Karena pasangan seperti itu hampir selalu lebih umum daripada keacakan mungkin menyiratkan, Anda berakhir dengan banyak tabrakan jauh di nol dari yang seharusnya.
Dengan dua masalah ini, xor
akhirnya menjadi hash combiner yang terlihat setengah layak di permukaan, tetapi tidak setelah pemeriksaan lebih lanjut.
Pada perangkat keras modern, menambahkan biasanya sekitar secepat xor
(mungkin menggunakan lebih banyak daya untuk melakukan ini, diakui). Menambahkan tabel kebenaran mirip dengan xor
pada bit yang dimaksud, tetapi juga mengirimkan sedikit ke bit berikutnya ketika kedua nilai adalah 1. Ini berarti ia menghapus lebih sedikit informasi.
Jadi hash(a) + hash(b)
lebih baik daripada hash(a) xor hash(b)
jika a==b
, hasilnya hash(a)<<1
bukan 0.
Ini tetap simetris; jadi "bad"
dan"dab"
mendapatkan hasil yang sama tetap menjadi masalah. Kami dapat memutus simetri ini dengan biaya sederhana:
hash(a)<<1 + hash(a) + hash(b)
alias hash(a)*3 + hash(b)
. (menghitung hash(a)
sekali dan menyimpan disarankan jika Anda menggunakan solusi shift). Konstanta ganjil mana pun alih-alih 3
secara bijektif akan memetakan k
bilangan bulat tak bertanda "-bit" ke dirinya sendiri, karena peta bilangan bulat tak bertanda adalah modulo matematika 2^k
untuk beberapak
, dan konstanta ganjil apa pun relatif utama 2^k
.
Untuk versi yang lebih keren, kita dapat memeriksa boost::hash_combine
, yang secara efektif:
size_t hash_combine( size_t lhs, size_t rhs ) {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
return lhs;
}
di sini kami menambahkan bersama beberapa versi bergeser dari seed
dengan konstanta (yang pada dasarnya acak 0
s dan 1
s - khususnya itu adalah kebalikan dari rasio emas sebagai fraksi titik tetap 32 bit) dengan beberapa tambahan dan xor. Ini memecah simetri, dan memperkenalkan beberapa "noise" jika nilai hash yang masuk buruk (yaitu, bayangkan setiap komponen hash ke 0 - di atas menanganinya dengan baik, menghasilkan noda 1
dan0
s setelah masing-masing digabungkan. Naif saya 3*hash(a)+hash(b)
hanya menghasilkan a 0
di kasus itu).
(Bagi mereka yang tidak terbiasa dengan C / C ++, a size_t
adalah nilai integer yang tidak ditandatangani yang cukup besar untuk menggambarkan ukuran objek apa pun dalam memori. Pada sistem 64 bit, biasanya integer 64 bit yang tidak ditandai. Pada sistem 32 bit , bilangan bulat 32 bit unsigned.)