Tôi đã thử nghiệm một số thuật toán khác nhau, đo tốc độ và số lần va chạm.
Tôi đã sử dụng ba bộ khóa khác nhau:
Đối với mỗi kho văn bản, số lần va chạm và thời gian băm trung bình được ghi lại.
Tôi đã thử nghiệm:
Các kết quả
Mỗi kết quả chứa thời gian băm trung bình và số lần va chạm
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
Ghi chú :
Do va chạm thực sự xảy ra?
Đúng. Tôi bắt đầu viết chương trình thử nghiệm của mình để xem liệu va chạm băm có thực sự xảy ra hay không - và không chỉ là một cấu trúc lý thuyết. Họ thực sự xảy ra:
Va chạm FNV-1
creamwove
va chạm với quists
Va chạm FNV-1a
costarring
va chạm với liquid
declinate
va chạm với macallums
altarage
va chạm với zinke
altarages
va chạm với zinkes
Va chạm Murmur2
cataract
va chạm với periti
roquette
va chạm với skivie
shawl
va chạm với stormbound
dowlases
va chạm với tramontane
cricketings
va chạm với twanger
longans
va chạm với whigs
Va chạm DJB2
hetairas
va chạm với mentioner
heliotropes
va chạm với neurospora
depravement
va chạm với serafins
stylist
va chạm với subgenera
joyful
va chạm với synaphea
redescribed
va chạm với urites
dram
va chạm với vivency
Va chạm DJB2a
haggadot
va chạm với loathsomenesses
adorablenesses
va chạm với rentability
playwright
va chạm với snush
playwrighting
va chạm với snushing
treponematoses
va chạm với waterbeds
Va chạm CRC32
codding
va chạm với gnu
exhibiters
va chạm với schlager
Va chạm SuperFastHash
dahabiah
va chạm với drapability
encharm
va chạm với enclave
grahams
va chạm với gramary
- ... bắn 79 va chạm ...
night
va chạm với vigil
nights
va chạm với vigils
finks
va chạm với vinic
Tính ngẫu nhiên
Biện pháp chủ quan khác là cách băm phân phối ngẫu nhiên. Ánh xạ HashTables kết quả cho thấy dữ liệu được phân phối đồng đều như thế nào. Tất cả các hàm băm hiển thị phân phối tốt khi ánh xạ bảng tuyến tính:
Hoặc dưới dạng Bản đồ Hilbert ( XKCD luôn có liên quan ):
Ngoại trừ khi băm chuỗi số ( "1"
,, "2"
..., "216553"
) (ví dụ: mã zip ), trong đó các mẫu bắt đầu xuất hiện trong hầu hết các thuật toán băm:
SDBM :
DJB2a :
FNV-1 :
Tất cả ngoại trừ FNV-1a , trông vẫn khá ngẫu nhiên đối với tôi:
Trên thực tế, Murmur2 dường như thậm chí còn có tính ngẫu nhiên tốt Numbers
hơn so với FNV-1a
:
Khi tôi nhìn vào FNV-1a
bản đồ "số", tôi nghĩ rằng tôi thấy các mẫu dọc tinh tế. Với Murmur tôi không thấy mẫu nào cả. Bạn nghĩ sao?
Phần bổ sung *
trong bảng biểu thị mức độ ngẫu nhiên xấu như thế nào. Với FNV-1a
việc là tốt nhất, và DJB2x
là tồi tệ nhất:
Murmur2: .
FNV-1a: .
FNV-1: ▪
DJB2: ▪▪
DJB2a: ▪▪
SDBM: ▪▪▪
SuperFastHash: .
CRC: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Loselose: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪
▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Ban đầu tôi đã viết chương trình này để quyết định xem tôi thậm chí có phải lo lắng về va chạm hay không: tôi làm.
Và sau đó nó trở thành đảm bảo rằng các hàm băm là đủ ngẫu nhiên.
Thuật toán FNV-1a
Hàm băm FNV1 có các biến thể trả về giá trị băm 32, 64, 128, 256, 512 và 1024 bit.
Các thuật toán FNV-1a là:
hash = FNV_offset_basis
for each octetOfData to be hashed
hash = hash xor octetOfData
hash = hash * FNV_prime
return hash
Trường hợp các hằng số FNV_offset_basis
và FNV_prime
phụ thuộc vào kích thước băm trả về mà bạn muốn:
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
Xem trang FNV chính để biết chi tiết.
Tất cả kết quả của tôi là với biến thể 32 bit.
FNV-1 tốt hơn FNV-1a?
Số FNV-1a là xung quanh tốt hơn. Đã có nhiều va chạm với FNV-1a khi sử dụng kho từ tiếng Anh:
Hash Word Collisions
====== ===============
FNV-1 1
FNV-1a 4
Bây giờ so sánh chữ thường và chữ hoa:
Hash lowercase word Collisions UPPERCASE word collisions
====== ========================= =========================
FNV-1 1 9
FNV-1a 4 11
Trong trường hợp này, FNV-1a không tệ hơn "400%" so với FN-1, chỉ kém hơn 20%.
Tôi nghĩ điều quan trọng hơn là có hai loại thuật toán khi va chạm:
- va chạm hiếm : FNV-1, FNV-1a, DJB2, DJB2a, SDBM
- va chạm phổ biến : SuperFastHash, Loselose
Và sau đó là cách các băm được phân bổ đều:
- phân phối nổi bật: Murmur2, FNV-1a, SuperFastHas
- phân phối tuyệt vời: FNV-1
- phân phối tốt: SDBM, DJB2, DJB2a
- phân phối khủng khiếp: Loselose
Cập nhật
Lầm bầm? Chắc chắn, tại sao không
Cập nhật
@whatshisname tự hỏi làm thế nào một CRC32 sẽ hoạt động, thêm số vào bảng.
CRC32 là khá tốt . Ít va chạm, nhưng chậm hơn và chi phí hoạt động của bảng tra cứu 1k.
Cắt tất cả những thứ sai lầm về phân phối CRC - xấu của tôi
Cho đến hôm nay tôi sẽ sử dụng FNV-1a làm thuật toán băm bảng băm thực tế của mình . Nhưng bây giờ tôi đang chuyển sang Murmur2:
- Nhanh hơn
- Tính ngẫu nhiên tốt hơn của tất cả các lớp đầu vào
Và tôi thực sự, thực sự hy vọng có điều gì đó không đúng với SuperFastHash
thuật toán tôi tìm thấy ; thật tệ khi được phổ biến như nó là.
Cập nhật: Từ trang chủ MurmurHash3 trên Google :
(1) - SuperFastHash có đặc tính va chạm rất kém, đã được ghi nhận ở nơi khác.
Vì vậy, tôi đoán nó không chỉ là tôi.
Cập nhật: Tôi nhận ra tại sao Murmur
nhanh hơn những người khác. MurmurHash2 hoạt động trên bốn byte cùng một lúc. Hầu hết các thuật toán là byte theo byte :
for each octet in Key
AddTheOctetToTheHash
Điều này có nghĩa là khi chìa khóa càng dài thì Murmur càng có cơ hội tỏa sáng.
Cập nhật
Một bài đăng kịp thời của Raymond Chen nhắc lại thực tế rằng GUID "ngẫu nhiên" không có nghĩa là được sử dụng cho tính ngẫu nhiên của chúng. Chúng hoặc một tập hợp con của chúng, không phù hợp làm khóa băm:
Ngay cả thuật toán GUID phiên bản 4 cũng không được đảm bảo là không thể đoán trước được, bởi vì thuật toán không chỉ định chất lượng của trình tạo số ngẫu nhiên. Bài viết Wikipedia cho GUID chứa nghiên cứu chính cho thấy rằng các GUID tương lai và trước đó có thể được dự đoán dựa trên kiến thức về trạng thái trình tạo số ngẫu nhiên, vì trình tạo này không mạnh về mặt mật mã.
Randomess không giống như tránh va chạm; đó là lý do tại sao sẽ là một sai lầm khi cố gắng phát minh ra thuật toán "băm" của riêng bạn bằng cách sử dụng một số tập hợp con của hướng dẫn "ngẫu nhiên":
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);
}
Lưu ý : Một lần nữa, tôi đặt "GUID ngẫu nhiên" trong dấu ngoặc kép, vì đó là biến thể "ngẫu nhiên" của GUID. Một mô tả chính xác hơn sẽ được Type 4 UUID
. Nhưng không ai biết loại 4, hay loại 1, 3 và 5 là gì. Vì vậy, thật dễ dàng hơn để gọi chúng là GUID "ngẫu nhiên".
Tất cả các từ tiếng Anh gương