Yang hashing algoritma yang terbaik adalah untuk keunikan dan kecepatan? Contoh (baik) menggunakan termasuk hash kamus.
Aku tahu ada hal-hal seperti SHA-256 dan semacamnya, tetapi algoritma ini dirancang untuk aman, yang biasanya berarti mereka lebih lambat dari algoritma yang kurang unik. Aku ingin algoritma hash yang dirancang untuk menjadi cepat, namun tetap cukup unik untuk menghindari tabrakan.
Saya diuji beberapa algoritma yang berbeda, mengukur kecepatan dan jumlah tabrakan. Saya menggunakan tiga berbeda set kunci:
"1"
menjadi "216553"
(berpikir kode POS, dan bagaimana seorang miskin hash mengambil msn.com) xor
daripada +
) Hasil masing-masing berisi rata-rata hash waktu, 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:
Ya. Saya mulai menulis program uji untuk melihat apakah hash collisions benar-benar terjadi - dan tidak hanya membangun teori. Mereka memang terjadi: FNV-1 tabrakan
creamwove
bertabrakan dengan quists
FNV-1a tabrakan costarring
bertabrakan dengan cair
declinate
bertabrakan dengan macallums
altarage
bertabrakan dengan zinke
altarages
bertabrakan dengan zinkes
Murmur2 tabrakan katarak
bertabrakan dengan periti
roquette
bertabrakan dengan skivie
selendang
bertabrakan dengan stormbound
dowlases
bertabrakan dengan tramontane
cricketings
bertabrakan dengan twanger
longans
bertabrakan dengan whig
DJB2 tabrakan hetairas
bertabrakan dengan mentioner
heliotropes
bertabrakan dengan neurospora
depravement
bertabrakan dengan serafins
stylist
bertabrakan dengan subgenera
menyenangkan
bertabrakan dengan synaphea
redescribed
bertabrakan dengan urites
dram
bertabrakan dengan vivency
DJB2a tabrakan haggadot
bertabrakan dengan loathsomenesses
adorablenesses
bertabrakan dengan rentabilitas
drama
bertabrakan dengan snush
playwrighting
bertabrakan dengan snushing
treponematoses
bertabrakan dengan waterbeds
CRC32 tabrakan codding
bertabrakan dengan gnu
exhibiters
bertabrakan dengan schlager
SuperFastHash tabrakan dahabiah
bertabrakan dengan kelangsaian
encharm
bertabrakan dengan enclave
grahams
bertabrakan dengan gramary
malam
bertabrakan dengan berjaga
malam
bertabrakan dengan vigils
finks
bertabrakan dengan vinic
Randomnessification Lainnya ukuran subjektif adalah bagaimana secara acak didistribusikan hash. Pemetaan yang dihasilkan HashTables menunjukkan bagaimana merata data didistribusikan. Semua fungsi hash menunjukkan distribusi yang baik ketika pemetaan tabel linear:
Atau sebagai Hilbert Peta (XKCD selalu relevan):
Kecuali bila hashing jumlah string ("1"
, "2"
, ..., "216553"
) (misalnya, kode pos), di mana pola-pola yang mulai muncul di sebagian besar hashing algoritma:
SDBM:
DJB2a:
FNV-1:
Semua kecuali FNV-1a, yang masih terlihat cukup acak untuk saya:
Bahkan, Murmur2 tampaknya bahkan lebih baik keacakan dengan Angka
dari FNV-1a
:
Ketika saya melihat
FNV-1a
"nomor" peta, I pikir aku melihat halus pola vertikal. Dengan Gumaman saya melihat ada pola sama sekali. Apa yang anda pikirkan?Tambahan *`
** di meja menunjukkan betapa buruknya keacakan. Dengan
FNV-1amenjadi yang terbaik, dan **
DJB2x`** yang terburuk:
Murmur2: .
FNV-1a: .
FNV-1: ▪
DJB2: ▪▪
DJB2a: ▪▪
SDBM: ▪▪▪
SuperFastHash: .
CRC: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Loselose: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪
▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Yang FNV1 hash datang dalam varian yang kembali 32, 64, 128, 256, 512 dan 1024 bit hash. The FNV-1a algoritma 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
tergantung pada return hash ukuran 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
No. FNV-1a adalah yang lebih baik di sekitar. Ada lebih tabrakan dengan FNV-1a ketika menggunakan kata bahasa inggris corpus:
Hash Word Collisions
====== ===============
FNV-1 1
FNV-1a 4
Sekarang bandingkan huruf kecil dan huruf besar:
Hash lowercase word Collisions UPPERCASE word collisions
====== ========================= =========================
FNV-1 1 9
FNV-1a 4 11
Dalam hal ini FNV-1a isn't "400%" lebih buruk dari FN-1, hanya 20% lebih buruk. Saya pikir lebih penting takeaway adalah bahwa ada dua kelas dari algoritma ketika datang untuk tabrakan:
Sampai hari ini saya akan menggunakan FNV-1a seperti saya de facto hash-table algoritma hashing. Tapi sekarang saya'm beralih ke Murmur2:
SuperFastHash
algoritma saya menemukan; it's terlalu buruk untuk menjadi sepopuler itu.
Update:** Dari MurmurHash3 homepage di Google: (1) - SuperFastHash telah sangat miskin tabrakan sifat, yang telah didokumentasikan di tempat lain. Jadi saya kira itu's tidak hanya saya. Update: saya menyadari mengapa
Menggerutu
lebih cepat dari yang lain. MurmurHash2 beroperasi pada empat byte pada suatu waktu. Kebanyakan algoritma byte by byte:
for each octet in Key
AddTheOctetToTheHash
Posting tepat waktu oleh Raymond Chen menegaskan fakta bahwa "random" Guid tidak dimaksudkan untuk digunakan untuk mereka keacakan. Mereka, atau sebagian dari mereka, tidak cocok sebagai key hash:
Bahkan Versi 4 GUID algoritma ini tidak dijamin untuk menjadi tak terduga, karena algoritma tidak menentukan kualitas nomor acak generator. Artikel Wikipedia untuk GUID berisi primer penelitian yang menunjukkan bahwa masa depan dan sebelumnya Guid dapat diprediksi berdasarkan pengetahuan dari random number generator negara, karena generator tidak cryptographically kuat. Randomess tidak sama seperti collision avoidance; yang adalah mengapa hal itu akan menjadi suatu kesalahan untuk mencoba untuk menemukan anda sendiri "hashing" algoritma dengan mengambil beberapa subset dari a "random" guid:
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);
}
Tipe 4 UUID
. Tapi tidak ada yang tahu apa tipe 4, atau jenis 1, 3 dan 5. Jadi itu's hanya lebih mudah untuk menyebutnya "random" Guid.
Semua kata-Kata bahasa inggris cermin Jika anda ingin membuat peta hash dari tidak berubah kamus, anda mungkin ingin mempertimbangkan pilihan hashing https://en.wikipedia.org/wiki/Perfect_hash_function - selama pembangunan hash fungsi hash table, anda bisa menjamin, untuk dataset yang diberikan, bahwa tidak akan ada tabrakan.
Di sini adalah sebuah daftar dari fungsi hash, tapi versi pendek adalah:
Jika anda hanya ingin memiliki sebuah fungsi hash yang baik, dan tidak bisa menunggu,
djb2
adalah salah satu yang terbaik string fungsi hash yang saya tahu. Ini memiliki distribusi yang sangat baik dan kecepatan pada berbagai set kunci dan ukuran meja
unsigned long
hash(unsigned char *str)
{
unsigned long hash = 5381;
int c;
while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
CityHash oleh Google adalah algoritma yang anda cari. Hal ini tidak baik untuk kriptografi, tetapi lebih baik untuk menghasilkan hash yang unik.
Membaca blog untuk informasi lebih lanjut dan kode tersedia di sini.
CityHash ditulis dalam C++. Ada juga polos C port.
Semua CityHash fungsi disetel untuk prosesor 64-bit. Yang mengatakan, mereka akan menjalankan (kecuali untuk orang-orang baru yang menggunakan SSE4.2) dalam 32-bit kode. Mereka tidak't menjadi sangat cepat sekalipun. Anda mungkin ingin menggunakan Murmur atau sesuatu yang lain dalam 32-bit kode.
I've diplot pendek, kecepatan angin dan perbandingan algoritma hashing yang berbeda ketika hashing file.
Plot individu hanya sedikit berbeda dalam membaca metode dan dapat diabaikan di sini, karena semua file disimpan dalam sebuah tmpfs. Oleh karena itu patokan tersebut tidak IO-terikat jika anda bertanya-tanya.
Algoritma yang meliputi: SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}
.
Kesimpulan:
CRC
instruksi, CPU saya yang tidak memiliki. SpookyHash di kasus saya selalu sedikit sebelum CityHash.Sumber yang digunakan untuk plot:
SHA algoritma (termasuk SHA-256) adalah dirancang untuk cepat.
Pada kenyataannya, kecepatan mereka dapat menjadi masalah kadang-kadang. Secara khusus, teknik umum untuk menyimpan password-berasal token adalah untuk menjalankan standar cepat algoritma hash 10.000 kali (menyimpan hash hash hash hash dari password...).
#!/usr/bin/env ruby
require 'securerandom'
require 'digest'
require 'benchmark'
def run_random_digest(digest, count)
v = SecureRandom.random_bytes(digest.block_length)
count.times { v = digest.digest(v) }
v
end
Benchmark.bmbm do |x|
x.report { run_random_digest(Digest::SHA256.new, 1_000_000) }
end
Output:
Rehearsal ------------------------------------
1.480000 0.000000 1.480000 ( 1.391229)
--------------------------- total: 1.480000sec
user system total real
1.400000 0.000000 1.400000 ( 1.382016)
aku tahu ada hal-hal seperti SHA-256 dan semacamnya, tetapi algoritma ini dirancang untuk aman, yang biasanya berarti mereka lebih lambat dari algoritma yang kurang unik.
Asumsi bahwa fungsi hash kriptografi yang lebih unik adalah salah, dan pada kenyataannya hal ini dapat terbukti sering mundur dalam praktek. Dalam kebenaran:
Yang berarti bahwa non-kriptografi fungsi hash yang baik dapat memiliki lebih sedikit tabrakan dari kriptografi untuk "baik" mengatur data—data set yang dirancang untuk.
Kita benar-benar dapat menunjukkan hal ini dengan data di Ian Boyd's jawaban dan sedikit matematika: the Ulang tahun masalah. Rumus untuk jumlah yang diharapkan dari bertabrakan pasang jika anda memilih n
bilangan bulat secara acak dari set [1, d]
ini (diambil dari Wikipedia):
n - d + d * ((d - 1) / d)^n
Memasukkan n
= 216,553 dan d
= 2^32 kita mendapatkan sekitar 5.5 diharapkan tabrakan. Ian's tes sebagian besar menunjukkan hasil sekitar lingkungan itu, tapi dengan satu dramatis pengecualian: sebagian besar fungsi punya nol tabrakan di angka berturut-turut tes. Probabilitas memilih 216,553 32-bit angka secara acak dan mendapatkan nol tabrakan adalah tentang 0.43%. Dan yang's hanya untuk satu fungsi—di sini kita memiliki lima berbeda fungsi hash keluarga dengan nol tabrakan!
Jadi apa yang kita're lihat di sini adalah bahwa hash yang Ian diuji berinteraksi positif dengan angka berturut-turut dataset—yaitu, mereka're pendispersi minimal input yang berbeda lebih luas dari yang ideal fungsi hash kriptografi akan. (Catatan: hal ini berarti bahwa Ian's grafis penilaian yang FNV-1a dan MurmurHash2 "terlihat acak" kepada-nya dalam angka data set dapat disangkal dari data sendiri. Nol tabrakan pada data set yang ukuran, untuk kedua fungsi hash, adalah mencolok nonrandom!)
Ini tidak mengejutkan karena ini adalah perilaku yang diinginkan bagi banyak menggunakan fungsi hash. Misalnya, tabel hash kunci yang sering sangat mirip; Ian's jawaban menyebutkan masalah MSN begitu juga dengan kode POS hash tables. Ini adalah dimana penghindaran tabrakan di mungkin input menang atas acak-seperti perilaku.
Lain instruktif perbandingan di sini adalah kontras dalam desain gol di antara CRC dan fungsi hash kriptografi:
Jadi untuk CRC itu lagi baik memiliki lebih sedikit tabrakan dari random di minimal input yang berbeda. Dengan kripto hash, ini adalah tidak-tidak!
Gunakan SipHash. Memiliki banyak sifat-sifat yang diinginkan:
Cepat. Dioptimalkan pelaksanaannya membutuhkan waktu sekitar 1 cycle per byte.
Aman. SipHash kuat PRF (pseudorandom function). Ini berarti bahwa hal itu tidak dapat dibedakan dari fungsi acak (kecuali anda tahu 128-bit kunci rahasia). Oleh karena itu:
Tidak perlu khawatir tentang tabel hash probe menjadi waktu linier karena tabrakan. Dengan SipHash, anda *tahu *** anda akan mendapatkan rata-kasus kinerja rata-rata, terlepas dari input.
Kekebalan terhadap hash-based serangan denial of service.
Anda dapat menggunakan SipHash (terutama versi dengan 128-bit output) sebagai MAC (Message Authentication Code). Jika anda menerima pesan dan SipHash tag, dan tag yang sama seperti yang dari menjalankan SipHash dengan kunci rahasia anda, maka anda tahu bahwa siapa pun yang dibuat hash juga memiliki kunci rahasia, dan bahwa baik pesan maupun hash yang telah berubah sejak saat itu.
Hal ini tergantung pada data yang anda hashing. Beberapa hashing bekerja lebih baik dengan data yang spesifik seperti teks. Beberapa hashing algoritma yang secara khusus dirancang untuk menjadi baik untuk data tertentu.
Paul Hsieh pernah dibuat fast hash. Dia daftar source code dan penjelasan. Tapi itu sudah dipukuli. :)
Jawa menggunakan ini sederhana biak-dan-menambahkan algoritma:
kode hash untuk String objek dihitung sebagai
s[0]31^(n-1) + s131^(n-2) + ... + s[n-1]
menggunakan int aritmatika, di mana
s[i]
adalah i-th karakter dari string,n
adalah panjang string, dan^
menunjukkan eksponensial. (Nilai hash dari string kosong adalah nol.)
Mungkin ada banyak yang lebih baik di luar sana tapi ini cukup luas dan tampaknya menjadi baik trade-off antara kecepatan dan keunikan.
Pertama-tama, mengapa anda perlu untuk mengimplementasikan anda sendiri hashing? Untuk sebagian besar tugas-tugas yang anda harus mendapatkan hasil yang baik dengan struktur data dari perpustakaan standar, dengan asumsi ada's merupakan implementasi yang tersedia (kecuali anda're hanya melakukan ini untuk anda sendiri pendidikan).
Sejauh sebenarnya hashing algoritma pergi, favorit pribadi saya adalah FNV. 1
Berikut ini's contoh implementasi dari versi 32-bit di C:
unsigned long int FNV_hash(void* dataToHash, unsigned long int length)
{
unsigned char* p = (unsigned char *) dataToHash;
unsigned long int h = 2166136261UL;
unsigned long int i;
for(i = 0; i < length; i++)
h = (h * 16777619) ^ p[i] ;
return h;
}