Saya hanya bertanya-tanya mengapa bilangan prima digunakan dalam metode kelas hashCode()
? Misalnya, ketika menggunakan Eclipse untuk menghasilkan hashCode()
metode saya selalu ada bilangan prima yang 31
digunakan:
public int hashCode() {
final int prime = 31;
//...
}
Referensi:
Berikut ini adalah primer yang bagus pada Hashcode dan artikel tentang bagaimana hashing bekerja yang saya temukan (C # tetapi konsepnya dapat ditransfer): Pedoman dan aturan Eric Lippert untuk GetHashCode ()
Jawaban:
Karena Anda ingin jumlah yang Anda kalikan dengan dan jumlah ember yang Anda masukkan untuk memiliki faktorisasi prima ortogonal.
Misalkan ada 8 ember untuk dimasukkan. Jika nomor yang Anda gunakan untuk mengalikan dengan adalah kelipatan 8, maka ember yang dimasukkan hanya akan ditentukan oleh entri yang paling tidak signifikan (yang tidak dikalikan sama sekali). Entri yang serupa akan bertabrakan. Tidak bagus untuk fungsi hash.
31 adalah bilangan prima yang cukup besar sehingga jumlah ember tidak mungkin dapat dibagi olehnya (dan pada kenyataannya, implementasi HashMap java modern menjaga jumlah ember pada kekuatan 2).
sumber
(x*8 + y) % 8 = (x*8) % 8 + y % 8 = 0 + y % 8 = y % 8
Nomor prima dipilih untuk mendistribusikan data terbaik di antara ember hash. Jika distribusi input acak dan tersebar merata, maka pilihan kode hash / modulus tidak masalah. Itu hanya berdampak ketika ada pola tertentu pada input.
Ini sering terjadi ketika berhadapan dengan lokasi memori. Misalnya, semua bilangan bulat 32-bit disejajarkan dengan alamat yang dapat dibagi oleh 4. Periksa tabel di bawah ini untuk memvisualisasikan efek menggunakan modulus prime vs non-prime:
Perhatikan distribusi yang hampir sempurna ketika menggunakan modulus prima vs modulus non-prima.
Namun, meskipun contoh di atas sebagian besar dibuat-buat, prinsip umumnya adalah bahwa ketika berhadapan dengan pola input , menggunakan modulus bilangan prima akan menghasilkan distribusi terbaik.
sumber
Untuk apa nilainya, Efektif Java 2nd Edition mengesampingkan masalah matematika dan hanya mengatakan bahwa alasan untuk memilih 31 adalah:
Ini kutipan lengkapnya, dari Butir 9: Selalu timpa
hashCode
ketika Anda menggantiequals
:Agak disederhanakan, dapat dikatakan bahwa menggunakan pengganda dengan banyak pembagi akan menghasilkan lebih banyak tabrakan hash . Karena untuk hashing yang efektif kami ingin meminimalkan jumlah tabrakan, kami mencoba menggunakan pengganda yang memiliki lebih sedikit pembagi. Bilangan prima menurut definisi memiliki dua pembagi positif yang berbeda.
Pertanyaan-pertanyaan Terkait
sumber
3, 5, 17, 257, 65537
atau 2 ^ n - 1 ( Mersenne bilangan prima ):3, 7, 31, 127, 8191, 131071, 524287, 2147483647
. Namun31
(dan bukan, katakanlah,127
) diikutkan.Saya mendengar bahwa 31 dipilih sehingga kompiler dapat mengoptimalkan penggandaan ke shift 5 bit kemudian kurangi nilainya.
sumber
mov reg1, reg2-shl reg1,5-sub reg1,reg2
dapat dijalankan dalam 2 siklus. (Pindah hanya mengganti nama dan mengambil 0 siklus).Berikut ini kutipan yang sedikit lebih dekat dengan sumbernya.
Intinya adalah:
sumber
Pertama Anda menghitung nilai hash modulo 2 ^ 32 (ukuran an
int
), jadi Anda menginginkan sesuatu yang relatif prima ke 2 ^ 32 (relatif prima berarti tidak ada pembagi umum). Angka ganjil akan melakukan untuk itu.Kemudian untuk tabel hash yang diberikan indeks biasanya dihitung dari nilai hash modulo ukuran tabel hash, jadi Anda menginginkan sesuatu yang relatif prima dengan ukuran tabel hash. Seringkali ukuran tabel hash dipilih sebagai bilangan prima karena alasan itu. Dalam kasus Java, implementasi Sun memastikan bahwa ukuran selalu merupakan kekuatan dua, sehingga angka ganjil juga cukup di sini. Ada juga beberapa pemijatan tambahan dari kunci hash untuk membatasi tabrakan lebih lanjut.
Efek buruk jika tabel hash dan pengali memiliki faktor yang sama
n
adalah bahwa dalam keadaan tertentu hanya 1 / n entri dalam tabel hash yang akan digunakan.sumber
Alasan mengapa bilangan prima digunakan adalah untuk meminimalkan tabrakan ketika data menunjukkan beberapa pola tertentu.
Hal pertama yang pertama: Jika data acak maka tidak perlu untuk bilangan prima, Anda dapat melakukan operasi mod terhadap nomor apa pun dan Anda akan memiliki jumlah tabrakan yang sama untuk setiap kemungkinan nilai modulus.
Tetapi ketika data tidak acak maka hal-hal aneh terjadi. Misalnya pertimbangkan data numerik yang selalu kelipatan 10.
Jika kami menggunakan mod 4, kami menemukan:
10 mod 4 = 2
20 mod 4 = 0
30 mod 4 = 2
40 mod 4 = 0
50 mod 4 = 2
Jadi dari 3 kemungkinan nilai modulus (0,1,2,3) hanya 0 dan 2 yang akan bertabrakan, itu buruk.
Jika kita menggunakan bilangan prima seperti 7:
10 mod 7 = 3
20 mod 7 = 6
30 mod 7 = 2
40 mod 7 = 4
50 mod 7 = 1
dll
Kami juga mencatat bahwa 5 bukan pilihan yang baik tetapi 5 adalah prima alasannya adalah bahwa semua kunci kami adalah kelipatan 5. Ini berarti kita harus memilih bilangan prima yang tidak membagi kunci kami, memilih bilangan prima besar adalah biasanya cukup.
Jadi keliru di sisi berulang-ulang alasan bilangan prima digunakan adalah untuk menetralisir efek pola dalam kunci dalam distribusi tabrakan fungsi hash.
sumber
31 juga khusus untuk Java HashMap yang menggunakan tipe data hash int. Dengan demikian kapasitas maks 2 ^ 32. Tidak ada gunanya menggunakan Fermat yang lebih besar atau bilangan prima Mersenne.
sumber
Ini biasanya membantu mencapai penyebaran data Anda yang lebih merata di antara hash bucket, terutama untuk kunci dengan entropi rendah.
sumber