Mengapa fungsi hash menggunakan modulus bilangan prima?


336

Dahulu kala, saya membeli buku struktur data dari tabel tawar-menawar seharga $ 1,25. Di dalamnya, penjelasan untuk fungsi hashing mengatakan bahwa ia harus diubah oleh bilangan prima karena "sifat matematika".

Apa yang Anda harapkan dari buku seharga $ 1,25?

Lagi pula, saya sudah bertahun-tahun memikirkan sifat matematika, dan masih belum bisa mengetahuinya.

Apakah distribusi angka benar-benar lebih bahkan ketika ada jumlah utama ember? Atau ini adalah kisah seorang programmer lama bahwa semua orang menerima karena semua orang menerimanya?


1
Pertanyaan yang masuk akal: Mengapa harus ada jumlah ember yang prima?
Draemon

1
Pertanyaan ini tampaknya di luar topik karena kemungkinan besar milik Ilmu Komputer .
Lightness Races dalam Orbit

2
cs.stackexchange.com/a/64191/64222 penjelasan lain yang diperdebatkan dengan baik.
Green Tree


Berikut ini penjelasan lain yang bagus untuk pertanyaan yang agak terkait dengan beberapa angka pembuktian yang mengejutkan - quora.com/...
AnBisw

Jawaban:


242

Biasanya fungsi hash sederhana bekerja dengan mengambil "bagian komponen" dari input (karakter dalam kasus string), dan mengalikannya dengan kekuatan beberapa konstanta, dan menambahkannya bersama dalam beberapa tipe integer. Jadi misalnya hash khas (walaupun tidak terlalu bagus) dari string mungkin:

(first char) + k * (second char) + k^2 * (third char) + ...

Kemudian jika sekelompok string yang semuanya memiliki karakter pertama yang sama dimasukkan, maka hasilnya semua akan menjadi modulo k yang sama, setidaknya sampai tipe integer meluap.

[Sebagai contoh, kode Java string hash mirip dengan ini - ia melakukan urutan karakter terbalik, dengan k = 31. Jadi Anda mendapatkan hubungan yang mengejutkan modulo 31 antara string yang berakhir dengan cara yang sama, dan hubungan yang mengejutkan modulo 31 ^ antara string yang sama kecuali di dekat akhir. Ini tidak secara serius mengacaukan perilaku hashtable.]

Hash bekerja dengan mengambil modulus hash di atas jumlah bucket.

Sangat penting dalam hashtabel untuk tidak menghasilkan collision untuk kemungkinan kasus, karena collision mengurangi efisiensi hashtable.

Sekarang, anggaplah seseorang meletakkan sejumlah nilai ke dalam hashtable yang memiliki hubungan antara item-item tersebut, seperti semua yang memiliki karakter pertama yang sama. Ini adalah pola penggunaan yang cukup dapat diprediksi, saya katakan, jadi kami tidak ingin itu menghasilkan terlalu banyak tabrakan.

Ternyata "karena sifat matematika", jika konstanta yang digunakan dalam hash, dan jumlah ember, adalah koprime , maka tabrakan diminimalkan dalam beberapa kasus umum. Jika mereka bukan koprime, maka ada beberapa hubungan yang cukup sederhana antara input yang tabrakannya tidak diminimalkan. Semua hash keluar sama modulo faktor umum, yang berarti mereka semua akan jatuh ke 1 / n dari ember yang memiliki nilai modulo faktor umum. Anda mendapatkan n kali lebih banyak tabrakan, di mana n adalah faktor umum. Karena n setidaknya 2, saya akan mengatakan itu tidak dapat diterima untuk kasus penggunaan yang cukup sederhana untuk menghasilkan setidaknya dua kali lebih banyak tabrakan seperti biasa. Jika beberapa pengguna akan memecah distribusi kami menjadi ember, kami ingin itu menjadi kecelakaan aneh, bukan penggunaan yang dapat diprediksi sederhana.

Sekarang, implementasi hashtable jelas tidak memiliki kendali atas item yang dimasukkan ke dalamnya. Mereka tidak bisa mencegah mereka berhubungan. Jadi yang harus dilakukan adalah memastikan bahwa konstanta dan jumlah bucket adalah koprime. Dengan begitu Anda tidak mengandalkan komponen "terakhir" sendirian untuk menentukan modulus bucket sehubungan dengan beberapa faktor umum kecil. Sejauh yang saya tahu mereka tidak harus menjadi yang utama untuk mencapai ini, hanya coprime.

Tetapi jika fungsi hash dan hashtable ditulis secara independen, maka hashtable tidak tahu bagaimana fungsi hash bekerja. Mungkin menggunakan konstanta dengan faktor-faktor kecil. Jika Anda beruntung, itu mungkin bekerja dengan sangat berbeda dan tidak linier. Jika hash cukup baik, maka jumlah bucket apa pun baik-baik saja. Tetapi hashtable paranoid tidak dapat mengasumsikan fungsi hash yang baik, jadi sebaiknya gunakan jumlah bucket yang prima Demikian pula fungsi hash paranoid harus menggunakan konstanta prime yang lebih besar, untuk mengurangi kemungkinan seseorang menggunakan sejumlah ember yang kebetulan memiliki faktor umum dengan konstanta.

Dalam praktiknya, saya pikir cukup normal untuk menggunakan kekuatan 2 sebagai jumlah ember. Ini nyaman dan menghemat harus mencari sekitar atau pra-pilih nomor utama dari besaran yang tepat. Jadi, Anda mengandalkan fungsi hash untuk tidak menggunakan pengganda genap, yang umumnya merupakan asumsi yang aman. Tetapi Anda masih bisa mendapatkan perilaku hashing buruk sesekali berdasarkan fungsi hash seperti di atas, dan jumlah bucket prima dapat membantu lebih lanjut.

Menerapkan prinsip bahwa "semuanya harus prima" sejauh yang saya tahu cukup tetapi bukan syarat yang diperlukan untuk distribusi yang baik di atas tagar. Ini memungkinkan semua orang untuk beroperasi tanpa perlu berasumsi bahwa yang lain telah mengikuti aturan yang sama.

[Sunting: ada alasan lain yang lebih terspesialisasi untuk menggunakan jumlah bucket prima, yaitu jika Anda menangani tabrakan dengan probe linear. Lalu Anda menghitung langkah dari kode hash, dan jika langkah itu keluar menjadi faktor jumlah ember maka Anda hanya bisa melakukan (bucket_count / langkah) penyelidikan sebelum Anda kembali ke tempat Anda mulai. Kasus yang paling ingin Anda hindari adalah stride = 0, tentu saja, yang harus dikurung khusus, tetapi untuk menghindari juga casing-khusus bucket_count / stride sama dengan integer kecil, Anda bisa menjadikan bucket_count prima dan tidak peduli apa pun yang terjadi. langkahnya asalkan bukan 0.]


Sama seperti catatan tambahan: diskusi untuk pilihan yang masuk akal dari faktor k untuk kode hash ada di sini: stackoverflow.com/q/1835976/21499
Hans-Peter Störr

9
ini jawaban yang luar biasa. dapatkah Anda jelaskan ini lebih lanjut "Jadi Anda mendapatkan hubungan yang mengejutkan modulo 31 antara string yang berakhir dengan cara yang sama, dan hubungan yang mengejutkan modulo 2 ^ 32 antara string yang sama kecuali dekat akhir. Ini tidak secara serius mengacaukan perilaku hashtable. " Saya terutama tidak mengerti bagian 2 ^ 32
biasa

2
Catatan tambahan untuk memperjelas hal ini: "Semua hash keluar sama dengan modulo faktor umum" -> Ini karena, jika Anda mempertimbangkan contoh fungsi hash hash = char pertama + char 2, * k + ..., dan ambil string dengan karakter pertama yang sama, hash% k akan sama untuk string ini. Jika M adalah ukuran hashtable dan g adalah gcd dari M dan k, maka (hash% k)% g sama dengan hash% g (karena g membagi k) dan karenanya hash% g juga akan sama untuk string ini. Sekarang perhatikan (hash% M)% g, ini sama dengan hash% g (karena g membagi M). Jadi (hash% M)% g sama untuk semua string ini.
Quark

1
@DanielMcLaury Joshua Bloch menjelaskan mengapa untuk Jawa - direkomendasikan dalam dua buku populer (K&R, buku Naga) dan berkinerja baik dengan tabrakan rendah pada kamus bahasa Inggris. Itu cepat (menggunakan metode Horner ). Tampaknya bahkan K&R tidak ingat dari mana asalnya. Fungsi serupa adalah sidik jari Rabin dari algoritma Rabin-Karp (1981) tetapi K&R (1978) mendahului itu.
bain

1
@SteveJessop, bisakah Anda menjelaskan "striking relationship modulo 2 ^ 32 antara string yang sama kecuali mendekati akhir."? Terima kasih.
Khanna111

29

Hal pertama yang Anda lakukan ketika memasukkan / mengambil kembali dari tabel hash adalah menghitung kode hash untuk kunci yang diberikan dan kemudian menemukan ember yang benar dengan memotong kode hash ke ukuran hashTable dengan melakukan hashCode% table_length. Berikut adalah 2 'pernyataan' yang kemungkinan besar telah Anda baca di suatu tempat

  1. Jika Anda menggunakan kekuatan 2 untuk table_length, menemukan (kode hash (kunci)% 2 ^ n) sesederhana dan secepat (kode hash (kunci) & (2 ^ n -1)). Tetapi jika fungsi Anda untuk menghitung kode hash untuk kunci yang diberikan tidak baik, Anda pasti akan menderita pengelompokan banyak kunci dalam beberapa ember hash.
  2. Tetapi jika Anda menggunakan bilangan prima untuk table_length, kode hash yang dihitung dapat memetakan ke dalam ember hash yang berbeda bahkan jika Anda memiliki fungsi kode hash yang sedikit bodoh.

Dan inilah buktinya.

Jika seandainya fungsi kode hash Anda menghasilkan kode hash berikut ini antara lain {x, 2x, 3x, 4x, 5x, 6x ...}, maka semua ini akan dikelompokkan dalam hanya sejumlah ember, di mana m = table_length / GreatestCommonFactor (table_length, x). (Sangat sepele untuk memverifikasi / menurunkan ini). Sekarang Anda dapat melakukan salah satu dari yang berikut untuk menghindari pengelompokan

Pastikan Anda tidak menghasilkan terlalu banyak kode hash yang merupakan kelipatan dari kode hash lain seperti dalam {x, 2x, 3x, 4x, 5x, 6x ...}. Tetapi ini mungkin agak sulit jika hashTable Anda seharusnya memiliki jutaan entri. Atau cukup buat m sama dengan table_length dengan membuat GreatestCommonFactor (table_length, x) sama dengan 1, yaitu dengan membuat table_length coprime dengan x. Dan jika x dapat berupa angka apa saja maka pastikan bahwa table_length adalah bilangan prima.

Dari - http://srinvis.blogspot.com/2006/07/hash-table-lengths-and-prime-numbers.html


11

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/

Penjelasan yang cukup jelas, dengan gambar juga.

Sunting: Sebagai ringkasan, bilangan prima digunakan karena Anda memiliki peluang terbaik untuk mendapatkan nilai unik ketika mengalikan nilai dengan bilangan prima yang dipilih dan menambahkan semuanya. Misalnya diberi string, mengalikan setiap nilai huruf dengan bilangan prima dan kemudian menambahkan semuanya akan memberi Anda nilai hash.

Pertanyaan yang lebih baik adalah, mengapa tepatnya angka 31?


5
Meskipun, saya pikir ringkasan akan sangat membantu, jika situs itu pernah mati, beberapa sisa kontennya akan disimpan di sini di SO.
Thomas Owens

2
Artikel itu tidak menjelaskan mengapa, tetapi mengatakan "Para peneliti menemukan bahwa menggunakan prime 31 memberikan distribusi yang lebih baik untuk kunci, dan lebih sedikit tidak ada tabrakan. Tidak ada yang tahu mengapa ..." Lucu, menanyakan pertanyaan yang sama seperti saya berlaku .
theschmitzer

> Pertanyaan yang lebih baik, mengapa tepatnya angka 31? Jika yang Anda maksud adalah mengapa angka 31 digunakan, maka artikel yang Anda tunjukkan memberi tahu Anda alasannya, yaitu karena cepat untuk beberapa kali dan tes cos menunjukkan itu adalah yang terbaik untuk digunakan. Pengganda populer lainnya yang saya lihat adalah 33 yang memberi bobot pada teori bahwa masalah kecepatan (setidaknya pada awalnya) merupakan faktor penting. Jika maksud Anda, ada apa dengan 31 yang membuatnya lebih baik dalam ujian, maka saya khawatir saya tidak tahu.
sgmoore

Tepat, jadi satu-satunya alasan bisa digunakan sebagai pengganda adalah karena mudah digandakan. (Ketika saya mengatakan saya telah melihat 33 digunakan sebagai pengganda, saya tidak bermaksud baru-baru ini, ini mungkin beberapa dekade yang lalu, dan mungkin sebelum banyak analisis dilakukan pada hashing).
sgmoore

3
@SteveJessop Angka 31 mudah dioptimalkan oleh CPU sebagai operasi (x * 32) -1, yang *32merupakan pergeseran bit sederhana, atau bahkan lebih baik faktor skala alamat langsung (misalnya lea eax,eax*8; leax, eax,eax*4pada x86 / x64). Jadi *31adalah kandidat yang baik untuk penggandaan bilangan prima. Ini cukup benar beberapa tahun yang lalu - sekarang arsitektur CPU terbaru memiliki perkalian yang hampir instan - pembagian selalu lebih lambat ...
Arnaud Bouchez

10

tl; dr

index[hash(input)%2]akan menghasilkan tabrakan untuk setengah dari semua hash yang mungkin dan berbagai nilai. index[hash(input)%prime]menghasilkan tabrakan <2 dari semua hash yang mungkin. Memperbaiki pembagi ukuran tabel juga memastikan bahwa jumlahnya tidak boleh lebih besar dari tabel.


1
2 adalah bilangan prima Bung
Ganesh Chowdhary Sadanala

8

Primes digunakan karena Anda memiliki peluang bagus untuk mendapatkan nilai unik untuk fungsi-hash tipikal yang menggunakan polinomial modulo P. Katakanlah, Anda menggunakan fungsi hash tersebut untuk string dengan panjang <<N, dan Anda memiliki tabrakan. Itu berarti bahwa 2 polinomial yang berbeda menghasilkan nilai modulo P. yang sama. Perbedaan polinomial tersebut adalah polinomial dengan derajat N yang sama (atau kurang). Tidak lebih dari N root (di sinilah sifat matematika menunjukkan dirinya, karena klaim ini hanya berlaku untuk polinomial atas bidang => bilangan prima). Jadi, jika N jauh lebih kecil dari P, Anda kemungkinan tidak akan mengalami tabrakan. Setelah itu, percobaan mungkin dapat menunjukkan bahwa 37 cukup besar untuk menghindari tabrakan untuk hash-table dari string yang memiliki panjang 5-10, dan cukup kecil untuk digunakan untuk perhitungan.


1
Sementara penjelasannya tampak jelas sekarang, saya baru dapat membaca buku karya A.Shen "Programming: Theorems and problems" (dalam bahasa Rusia), lihat pembahasan algoritma Rabin. Tidak yakin apakah terjemahan bahasa Inggris ada.
TT_

5

Hanya untuk memberikan sudut pandang alternatif ada situs ini:

http://www.codexon.com/posts/hash-functions-the-modulo-prime-myth

Yang berpendapat bahwa Anda harus menggunakan jumlah ember sebanyak mungkin sebagai lawan untuk membulatkan ke jumlah ember utama. Sepertinya kemungkinan yang masuk akal. Secara intuitif, saya tentu bisa melihat bagaimana jumlah ember yang lebih besar akan lebih baik, tetapi saya tidak dapat membuat argumen matematis tentang ini.


Jumlah ember yang lebih besar berarti lebih sedikit benturan: Lihat prinsip lubang pigeon.
Tidak diketahui

11
@ Tidak Diketahui: Saya tidak percaya itu benar. Tolong perbaiki saya jika saya salah, tapi saya percaya menerapkan prinsip pigeonhole ke tabel hash hanya memungkinkan Anda untuk menyatakan bahwa akan ada tabrakan jika Anda memiliki lebih banyak elemen daripada sampah, tidak untuk menarik kesimpulan pada jumlah atau kepadatan tabrakan. Saya masih percaya bahwa jumlah sampah yang lebih besar adalah rute yang benar.
Falaina

Jika Anda menganggap bahwa tabrakan adalah untuk semua maksud dan tujuan acak, maka dengan paradoks ulang tahun, ruang yang lebih besar (ember) akan mengurangi kemungkinan terjadinya tabrakan.
Tidak dikenal

1
@ Diketahui Anda telah melewatkan bahwa tabrakan juga tergantung pada fungsi hash itu sendiri. Jadi jika fungsi memiliki benar-benar buruk, maka tidak peduli seberapa besar Anda meningkatkan ukuran, mungkin masih ada jumlah tabrakan yang signifikan
Suraj Chandran

Artikel asli sepertinya sudah tidak ada, tetapi ada beberapa komentar mendalam di sini, termasuk diskusi dengan penulis asli. news.ycombinator.com/item?id=650487
Adrian McCarthy

3

Bilangan prima adalah angka unik. Mereka unik dalam hal itu, produk perdana dengan nomor lain memiliki peluang terbaik untuk menjadi unik (tidak seunik perdana itu sendiri tentu saja) karena fakta bahwa perdana digunakan untuk menyusunnya. Properti ini digunakan dalam fungsi hashing.

Diberikan string "Samuel", Anda dapat menghasilkan hash unik dengan mengalikan setiap digit atau huruf konstituen dengan angka prima dan menambahkannya. Inilah sebabnya mengapa bilangan prima digunakan.

Namun menggunakan bilangan prima adalah teknik lama. Kunci di sini untuk memahami bahwa selama Anda dapat menghasilkan kunci yang cukup unik, Anda dapat pindah ke teknik hashing lainnya juga. Buka di sini untuk informasi lebih lanjut tentang topik ini tentang http://www.azillionmonkeys.com/qed/hash.html

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/


1
hahahah .... sebenarnya bukankah produk 2 primes memiliki peluang lebih baik untuk menjadi 'unik' daripada produk perdana dan nomor lainnya?
HasaniH

@Beska Di sini "keunikan" didefinisikan secara rekursif, jadi saya percaya "keunikan" harus didefinisikan dengan cara yang sama :)
TT_

3

Itu tergantung pada pilihan fungsi hash.

Banyak fungsi hash menggabungkan berbagai elemen dalam data dengan mengalikannya dengan beberapa faktor modulo kekuatan dua sesuai dengan ukuran kata mesin (bahwa modulus bebas dengan hanya membiarkan perhitungan melimpah).

Anda tidak ingin ada faktor umum antara pengganda untuk elemen data dan ukuran tabel hash, karena dengan demikian bisa terjadi bahwa memvariasikan elemen data tidak menyebar data ke seluruh tabel. Jika Anda memilih bilangan prima untuk ukuran tabel, faktor umum seperti itu sangat tidak mungkin.

Di sisi lain, faktor-faktor tersebut biasanya terdiri dari bilangan prima ganjil, jadi Anda juga harus aman menggunakan kekuatan dua untuk tabel hash Anda (misalnya Eclipse menggunakan 31 saat menghasilkan metode hashCode Java).


2

Misalkan ukuran meja Anda (atau angka untuk modulo) adalah T = (B * C). Sekarang jika hash untuk input Anda seperti (N * A * B) di mana N dapat berupa bilangan bulat, maka output Anda tidak akan didistribusikan dengan baik. Karena setiap kali n menjadi C, 2C, 3C dll, output Anda akan mulai berulang. yaitu output Anda akan didistribusikan hanya di posisi C. Perhatikan bahwa C di sini adalah (T / HCF (ukuran tabel, hash)).

Masalah ini dapat dihilangkan dengan membuat HCF 1. Angka prima sangat baik untuk itu.

Hal lain yang menarik adalah ketika T adalah 2 ^ N. Ini akan memberikan output yang persis sama dengan semua bit N input-hash yang lebih rendah. Karena setiap angka dapat diwakili kekuatan 2, ketika kita akan mengambil modulo dari angka apa pun dengan T, kita akan mengurangi semua kekuatan 2 angka bentuk, yaitu> = N, maka selalu memberikan jumlah pola tertentu, tergantung pada input . Ini juga pilihan yang buruk.

Demikian pula, T sebagai 10 ^ N buruk juga karena alasan yang sama (pola dalam notasi desimal angka bukan biner).

Jadi, bilangan prima cenderung memberikan hasil yang terdistribusi lebih baik, karenanya merupakan pilihan yang baik untuk ukuran tabel.


2

Menyalin dari jawaban saya yang lain https://stackoverflow.com/a/43126969/917428 . Lihat untuk detail dan contoh lebih lanjut.

Saya percaya bahwa itu hanya ada hubungannya dengan fakta bahwa komputer bekerja dengan basis 2. Coba pikirkan bagaimana hal yang sama bekerja untuk basis 10:

  • 8% 10 = 8
  • 18% 10 = 8
  • 87865378% 10 = 8

Tidak masalah berapa angkanya: selama berakhir dengan 8, modulo 10-nya akan menjadi 8.

Memilih nomor yang cukup besar, non-power-of-two akan memastikan fungsi hash benar-benar merupakan fungsi dari semua bit input, bukan bagian dari mereka.


1

Saya ingin menambahkan sesuatu untuk jawaban Steve Jessop (saya tidak bisa mengomentarinya karena saya tidak memiliki reputasi yang cukup). Tetapi saya menemukan beberapa bahan yang membantu. Jawabannya sangat membantu tetapi dia membuat kesalahan: ukuran ember seharusnya tidak menjadi kekuatan 2. Saya hanya akan mengutip dari buku "Pengantar Algoritma" oleh Thomas Cormen, Charles Leisersen, dkk di halaman 263:

Saat menggunakan metode pembagian, kami biasanya menghindari nilai m tertentu. Sebagai contoh, m seharusnya bukan pangkat 2, karena jika m = 2 ^ p, maka h (k) hanyalah bit urutan terendah k. Kecuali kita tahu bahwa semua pola p-bit orde rendah memiliki kemungkinan yang sama, kita lebih baik merancang fungsi hash untuk bergantung pada semua bit kunci. Seperti Latihan 11.3-3 meminta Anda untuk menunjukkan, memilih m = 2 ^ p-1 ketika k adalah string karakter yang ditafsirkan dalam radix 2 ^ p mungkin merupakan pilihan yang buruk, karena permutasi karakter k tidak mengubah nilai hashnya.

Semoga ini bisa membantu.


0

Untuk fungsi hash itu tidak hanya penting untuk meminimalkan tumbukan secara umum tetapi untuk membuatnya tidak mungkin untuk tetap dengan hash yang sama sambil mengumpulkan beberapa byte.

Katakanlah Anda memiliki persamaan: (x + y*z) % key = xdengan 0<x<keydan 0<z<key. Jika kuncinya adalah angka prima n * y = kunci benar untuk setiap n dalam N dan salah untuk setiap angka lainnya.

Contoh di mana kunci bukan contoh utama: x = 1, z = 2 dan kunci = 8 Karena kunci / z = 4 masih merupakan bilangan alami, 4 menjadi solusi untuk persamaan kami dan dalam kasus ini (n / 2) * y = kunci benar untuk setiap n dalam N. Jumlah solusi untuk persamaan praktis dua kali lipat karena 8 bukan bilangan prima.

Jika penyerang kami sudah tahu bahwa 8 adalah solusi yang mungkin untuk persamaan tersebut, ia dapat mengubah file dari produksi 8 ke 4 dan masih mendapatkan hash yang sama.


0

Saya telah membaca situs web wordpress populer yang ditautkan dalam beberapa jawaban populer di atas di bagian atas. Dari apa yang saya mengerti, saya ingin berbagi pengamatan sederhana yang saya buat.

Anda dapat menemukan semua detail dalam artikel di sini , tetapi anggap hal berikut ini berlaku:

  • Menggunakan bilangan prima memberi kita "peluang terbaik" dari nilai unik

Implementasi hashmap umum ingin 2 hal menjadi unik.

  • Kode hash unik untuk kunci
  • Unik indeks untuk menyimpan sebenarnya nilai

Bagaimana cara mendapatkan indeks unik? Dengan membuat ukuran awal wadah internal menjadi prima juga. Jadi pada dasarnya, prime terlibat karena ia memiliki sifat unik untuk menghasilkan angka unik yang akhirnya kami gunakan untuk mengidentifikasi objek dan menemukan indeks di dalam wadah internal.

Contoh:

key = "key"

value = "value" uniqueId = "k" * 31 ^ 2 + "e" * 31 ^ 1` + "y"

peta ke id unik

Sekarang kami ingin lokasi yang unik untuk nilai kami - kami juga

uniqueId % internalContainerSize == uniqueLocationForValue, dengan asumsi internalContainerSizejuga prima.

Saya tahu ini disederhanakan, tetapi saya berharap untuk mendapatkan ide umum.


0

"Sifat dasar matematika" mengenai moduli daya utama adalah bahwa mereka adalah salah satu blok bangunan bidang terbatas . Dua blok bangunan lainnya adalah operasi penjumlahan dan perkalian. Sifat khusus moduli utama adalah bahwa mereka membentuk bidang terbatas dengan operasi penambahan dan perkalian "biasa", yang hanya dibawa ke modulus. Ini berarti setiap peta perkalian ke modul integer yang berbeda berbeda dengan prime, demikian pula setiap penambahan.

Moduli utama menguntungkan karena:

  • Mereka memberikan kebebasan paling saat memilih pengganda sekunder dalam hashing sekunder, semua pengganda kecuali 0 akan berakhir mengunjungi semua elemen tepat sekali
  • Jika semua hash kurang dari modulus maka tidak akan ada tabrakan sama sekali
  • Bilangan prima acak campuran lebih baik dari kekuatan dua moduli dan kompres informasi semua bit bukan hanya subset

Namun mereka memiliki kelemahan besar, mereka membutuhkan divisi integer, yang membutuhkan banyak (~ 15-40) siklus, bahkan pada CPU modern. Dengan sekitar setengah perhitungan seseorang dapat memastikan hash tercampur dengan sangat baik. Dua operasi multiplikasi dan xorshift akan bercampur lebih baik daripada moudulus utama. Kemudian kita dapat menggunakan ukuran tabel hash apa saja dan pengurangan hash tercepat, memberikan 7 operasi total untuk kekuatan 2 ukuran tabel dan sekitar 9 operasi untuk ukuran sewenang-wenang.

Saya baru-baru ini melihat banyak implementasi tabel hash tercepat dan kebanyakan dari mereka tidak menggunakan moduli utama.


0

Pertanyaan ini digabung dengan pertanyaan yang lebih tepat, mengapa tabel hash harus menggunakan array berukuran prima, dan bukan kekuatan 2. Untuk fungsi hash itu sendiri ada banyak jawaban yang baik di sini, tetapi untuk pertanyaan terkait, mengapa beberapa tabel hash keamanan-kritis , seperti glibc, gunakan array berukuran prima, belum ada.

Secara umum kekuatan 2 tabel jauh lebih cepat. Ada yang mahal h % n => h & bitmask, di mana bitmask dapat dihitung melalui clz("hitung nol terkemuka") dari ukuran n. Fungsi modulo perlu melakukan pembagian integer yang sekitar 50x lebih lambat daripada yang logis and. Ada beberapa trik untuk menghindari modulo, seperti menggunakan https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ Lemire , tetapi umumnya tabel hash cepat menggunakan daya 2, dan tabel hash aman menggunakan bilangan prima.

Kenapa begitu?

Keamanan dalam hal ini didefinisikan oleh serangan pada strategi resolusi tumbukan, yang dengan sebagian besar tabel hash hanya pencarian linear dalam daftar tumbukan terkait. Atau dengan tabel pengalamatan terbuka yang lebih cepat, cari secara langsung dalam tabel. Jadi dengan kekuatan 2 tabel dan beberapa pengetahuan internal tabel, misalnya ukuran atau urutan daftar kunci yang disediakan oleh beberapa antarmuka JSON, Anda mendapatkan jumlah bit yang tepat yang digunakan. Jumlah yang ada di bitmask. Ini biasanya lebih rendah dari 10 bit. Dan untuk 5-10 bit itu sepele untuk benturan paksa bahkan dengan fungsi hash yang paling kuat dan paling lambat. Anda tidak mendapatkan keamanan penuh dari fungsi hash 32bit atau 64 bit Anda lagi. Dan intinya adalah menggunakan fungsi hash kecil cepat, bukan monster seperti murmur atau bahkan siphash.

Jadi, jika Anda menyediakan antarmuka eksternal ke tabel hash Anda, seperti resolver DNS, bahasa pemrograman, ... Anda ingin peduli tentang penyalahgunaan orang-orang yang suka layanan seperti DOS. Biasanya lebih mudah bagi orang-orang seperti itu untuk mematikan layanan publik Anda dengan metode yang jauh lebih mudah, tetapi itu memang terjadi. Jadi orang peduli.

Jadi pilihan terbaik untuk mencegah dari serangan tabrakan tersebut adalah baik

1) untuk menggunakan tabel prima, karena itu

  • semua 32 atau 64 bit relevan untuk menemukan ember, bukan hanya beberapa.
  • fungsi mengubah ukuran tabel hash lebih alami daripada hanya ganda. Fungsi pertumbuhan terbaik adalah deret fibonacci dan bilangan prima mendekati itu daripada menggandakan.

2) menggunakan langkah-langkah yang lebih baik terhadap serangan yang sebenarnya, bersama dengan kekuatan cepat 2 ukuran.

  • hitung tabrakan dan batalkan atau tidur pada serangan yang terdeteksi, yaitu angka tabrakan dengan probabilitas <1%. Seperti 100 dengan tabel hash 32bit. Inilah yang dilakukan oleh resolver djb misalnya djb.
  • mengonversi daftar tumbukan yang ditautkan ke pohon dengan pencarian O (log n) bukan O (n) ketika serangan tumbukan terdeteksi. Inilah yang dilakukan misalnya java.

Ada mitos yang tersebar luas bahwa fungsi hash yang lebih aman membantu mencegah serangan seperti itu, yang salah seperti yang saya jelaskan. Tidak ada keamanan dengan bit rendah saja. Ini hanya akan bekerja dengan tabel berukuran prima, tetapi ini akan menggunakan kombinasi dari dua metode paling lambat, hash lambat ditambah modulo prime lambat.

Fungsi hash untuk tabel hash terutama harus kecil (tidak dapat dielakkan) dan cepat. Keamanan hanya dapat datang dari mencegah pencarian linear di tabrakan. Dan tidak menggunakan fungsi hash yang sepele, seperti yang tidak sensitif terhadap beberapa nilai (seperti \ 0 saat menggunakan perkalian).

Menggunakan benih acak juga merupakan pilihan yang baik, orang-orang mulai dengan yang pertama, tetapi dengan informasi yang cukup dari tabel bahkan benih acak tidak banyak membantu, dan bahasa dinamis biasanya membuatnya sepele untuk mendapatkan benih melalui metode lain, karena disimpan dalam lokasi memori yang dikenal.


-1
function eratosthenes(n) {

    function getPrime(x) {
        var middle = (x-(x%2))/2;
        var arr_rest = [];
        for(var j=2 ; j<=middle;j++){
            arr_rest.push(x%j);
        }

        if(arr_rest.indexOf(0) == -1) {
            return true
        }else {
            return false
        }

    }
    if(n<2)  {
        return []
    }else if(n==2){
        return [2]
    }else {
        var arr = [2]
        for(var i=3;i<n;i++) {
            if(getPrime(i)){
                arr.push(i)
            }
        }
    }

    return arr;
}

2
Bisakah Anda menambahkan komentar untuk menjelaskan solusi Anda?
pom421
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.