Saya menguji beberapa algoritma yang berbeda, mengukur kecepatan dan jumlah tabrakan.
Saya menggunakan tiga set kunci yang berbeda:
Untuk setiap korpus, jumlah tabrakan dan rata-rata waktu yang dihabiskan dicatat.
Saya menguji:
Hasil
Setiap hasil berisi waktu hash rata-rata, dan jumlah tabrakan
Hash Lowercase Random UUID Numbers
============= ============= =========== ==============
Murmur 145 ns 259 ns 92 ns
6 collis 5 collis 0 collis
FNV-1a 152 ns 504 ns 86 ns
4 collis 4 collis 0 collis
FNV-1 184 ns 730 ns 92 ns
1 collis 5 collis 0 collis▪
DBJ2a 158 ns 443 ns 91 ns
5 collis 6 collis 0 collis▪▪▪
DJB2 156 ns 437 ns 93 ns
7 collis 6 collis 0 collis▪▪▪
SDBM 148 ns 484 ns 90 ns
4 collis 6 collis 0 collis**
SuperFastHash 164 ns 344 ns 118 ns
85 collis 4 collis 18742 collis
CRC32 250 ns 946 ns 130 ns
2 collis 0 collis 0 collis
LoseLose 338 ns - -
215178 collis
Catatan :
Apakah tabrakan benar-benar terjadi?
Iya. Saya mulai menulis program pengujian saya untuk melihat apakah tabrakan hash benar - benar terjadi - dan bukan hanya konstruksi teoretis. Mereka memang terjadi:
FNV-1 tabrakan
creamwove
bertabrakan dengan quists
FNV-1a tabrakan
costarring
bertabrakan dengan liquid
declinate
bertabrakan dengan macallums
altarage
bertabrakan dengan zinke
altarages
bertabrakan dengan zinkes
Murmur2 tabrakan
cataract
bertabrakan dengan periti
roquette
bertabrakan dengan skivie
shawl
bertabrakan dengan stormbound
dowlases
bertabrakan dengan tramontane
cricketings
bertabrakan dengan twanger
longans
bertabrakan dengan whigs
Tabrakan DJB2
hetairas
bertabrakan dengan mentioner
heliotropes
bertabrakan dengan neurospora
depravement
bertabrakan dengan serafins
stylist
bertabrakan dengan subgenera
joyful
bertabrakan dengan synaphea
redescribed
bertabrakan dengan urites
dram
bertabrakan dengan vivency
Tabrakan DJB2a
haggadot
bertabrakan dengan loathsomenesses
adorablenesses
bertabrakan dengan rentability
playwright
bertabrakan dengan snush
playwrighting
bertabrakan dengan snushing
treponematoses
bertabrakan dengan waterbeds
Tabrakan CRC32
codding
bertabrakan dengan gnu
exhibiters
bertabrakan dengan schlager
Tabrakan SuperFastHash
dahabiah
bertabrakan dengan drapability
encharm
bertabrakan dengan enclave
grahams
bertabrakan dengan gramary
- ... memotong 79 tabrakan ...
night
bertabrakan dengan vigil
nights
bertabrakan dengan vigils
finks
bertabrakan dengan vinic
Pengacakan
Ukuran subyektif lainnya adalah seberapa besar hash didistribusikan secara acak. Memetakan HashTables yang dihasilkan menunjukkan bagaimana data didistribusikan secara merata. Semua fungsi hash menunjukkan distribusi yang baik ketika memetakan tabel secara linear:
Atau sebagai Peta Hilbert ( XKCD selalu relevan ):
Kecuali ketika hashing string angka ( "1"
,, "2"
..., "216553"
) (misalnya, kode pos ), di mana pola mulai muncul di sebagian besar algoritma hashing:
SDBM :
DJB2a :
FNV-1 :
Semua kecuali FNV-1a , yang masih terlihat sangat acak bagi saya:
Bahkan, Murmur2 tampaknya memiliki keacakan yang lebih baik Numbers
daripada FNV-1a
:
Ketika saya melihat FNV-1a
peta "angka", saya pikir saya melihat pola vertikal yang halus. Dengan Murmur saya tidak melihat pola sama sekali. Bagaimana menurut anda?
Ekstra *
dalam tabel menunjukkan seberapa buruk keacakan itu. Dengan FNV-1a
menjadi yang terbaik, dan DJB2x
menjadi yang terburuk:
Murmur2: .
FNV-1a: .
FNV-1: ▪
DJB2: ▪▪
DJB2a: ▪▪
SDBM: ▪▪▪
SuperFastHash: .
CRC: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Loselose: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪
▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Saya awalnya menulis program ini untuk memutuskan apakah saya bahkan harus khawatir tentang tabrakan: Saya lakukan.
Dan kemudian itu berubah menjadi memastikan bahwa fungsi hash cukup acak.
Algoritma FNV-1a
Hash FNV1 hadir dalam varian yang mengembalikan hash 32, 64, 128, 256, 512 dan 1024 bit.
The algoritma FNV-1a adalah:
hash = FNV_offset_basis
for each octetOfData to be hashed
hash = hash xor octetOfData
hash = hash * FNV_prime
return hash
Di mana konstanta FNV_offset_basis
dan FNV_prime
bergantung pada ukuran hash pengembalian yang Anda inginkan:
Hash Size
===========
32-bit
prime: 2^24 + 2^8 + 0x93 = 16777619
offset: 2166136261
64-bit
prime: 2^40 + 2^8 + 0xb3 = 1099511628211
offset: 14695981039346656037
128-bit
prime: 2^88 + 2^8 + 0x3b = 309485009821345068724781371
offset: 144066263297769815596495629667062367629
256-bit
prime: 2^168 + 2^8 + 0x63 = 374144419156711147060143317175368453031918731002211
offset: 100029257958052580907070968620625704837092796014241193945225284501741471925557
512-bit
prime: 2^344 + 2^8 + 0x57 = 35835915874844867368919076489095108449946327955754392558399825615420669938882575126094039892345713852759
offset: 9659303129496669498009435400716310466090418745672637896108374329434462657994582932197716438449813051892206539805784495328239340083876191928701583869517785
1024-bit
prime: 2^680 + 2^8 + 0x8d = 5016456510113118655434598811035278955030765345404790744303017523831112055108147451509157692220295382716162651878526895249385292291816524375083746691371804094271873160484737966720260389217684476157468082573
offset: 1419779506494762106872207064140321832088062279544193396087847491461758272325229673230371772250864096521202355549365628174669108571814760471015076148029755969804077320157692458563003215304957150157403644460363550505412711285966361610267868082893823963790439336411086884584107735010676915
Lihat halaman FNV utama untuk detailnya.
Semua hasil saya dengan varian 32-bit.
FNV-1 lebih baik dari FNV-1a?
Tidak. FNV-1a lebih baik. Ada lebih banyak tabrakan dengan FNV-1a saat menggunakan kata Inggris corpus:
Hash Word Collisions
====== ===============
FNV-1 1
FNV-1a 4
Sekarang bandingkan huruf kecil dan besar:
Hash lowercase word Collisions UPPERCASE word collisions
====== ========================= =========================
FNV-1 1 9
FNV-1a 4 11
Dalam hal ini FNV-1a tidak "400%" lebih buruk dari FN-1, hanya 20% lebih buruk.
Saya pikir takeaway yang lebih penting adalah bahwa ada dua kelas algoritma ketika datang ke tabrakan:
- tabrakan langka : FNV-1, FNV-1a, DJB2, DJB2a, SDBM
- tabrakan umum : SuperFastHash, Loselose
Dan kemudian ada seberapa merata hash tersebut:
- distribusi luar biasa: Murmur2, FNV-1a, SuperFastHas
- distribusi yang sangat baik: FNV-1
- distribusi yang baik: SDBM, DJB2, DJB2a
- distribusi mengerikan: Loselose
Memperbarui
Berbisik? Tentu, mengapa tidak
Memperbarui
@whatshisname bertanya-tanya bagaimana kinerja CRC32 , menambahkan nomor ke tabel.
CRC32 cukup bagus . Beberapa tabrakan, tetapi lebih lambat, dan overhead tabel pencarian 1k.
Gunting semua hal yang salah tentang distribusi CRC - salah saya
Sampai hari ini saya akan menggunakan FNV-1a sebagai algoritma hash-table hash de facto saya . Tapi sekarang saya beralih ke Murmur2:
- Lebih cepat
- Pengacakan yang lebih baik dari semua kelas input
Dan saya benar- benar berharap ada yang salah dengan SuperFastHash
algoritma yang saya temukan ; Sayang sekali menjadi sepopuler itu.
Pembaruan: Dari beranda MurmurHash3 di Google :
(1) - SuperFastHash memiliki sifat tabrakan yang sangat buruk, yang telah didokumentasikan di tempat lain.
Jadi saya kira itu bukan hanya saya.
Pembaruan: Saya menyadari mengapa Murmur
lebih cepat dari yang lain. MurmurHash2 beroperasi pada empat byte sekaligus. Sebagian besar algoritma adalah byte demi byte :
for each octet in Key
AddTheOctetToTheHash
Ini berarti bahwa ketika kunci semakin lama Murmur mendapat kesempatan untuk bersinar.
Memperbarui
Sebuah posting yang tepat waktu oleh Raymond Chen menegaskan fakta bahwa GUID "acak" tidak dimaksudkan untuk digunakan untuk keacakan mereka. Mereka, atau sebagian dari mereka, tidak cocok sebagai kunci hash:
Bahkan algoritma GUID Versi 4 tidak dijamin tidak dapat diprediksi, karena algoritma tersebut tidak menentukan kualitas generator angka acak. Artikel Wikipedia untuk GUID berisi penelitian utama yang menunjukkan bahwa GUID di masa depan dan sebelumnya dapat diprediksi berdasarkan pengetahuan tentang keadaan generator nomor acak, karena generator tersebut tidak kuat secara kriptografis.
Keacakan tidak sama dengan menghindari tabrakan; itulah sebabnya akan menjadi kesalahan untuk mencoba menemukan algoritma "hashing" Anda sendiri dengan mengambil beberapa bagian dari panduan "acak":
int HashKeyFromGuid(Guid type4uuid)
{
//A "4" is put somewhere in the GUID.
//I can't remember exactly where, but it doesn't matter for
//the illustrative purposes of this pseudocode
int guidVersion = ((type4uuid.D3 & 0x0f00) >> 8);
Assert(guidVersion == 4);
return (int)GetFirstFourBytesOfGuid(type4uuid);
}
Catatan : Sekali lagi, saya memberi tanda "GUID acak" dalam tanda kutip, karena ini adalah varian "acak" dari GUID. Deskripsi yang lebih akurat adalah Type 4 UUID
. Tetapi tidak ada yang tahu apa tipe 4, atau tipe 1, 3 dan 5. Jadi, lebih mudah untuk memanggil mereka GUID "acak".
Semua Kata Bahasa Inggris mencerminkan