ฉันทดสอบอัลกอริทึมที่แตกต่างกันการวัดความเร็วและจำนวนการชน
ฉันใช้ชุดคีย์สามแบบ:
สำหรับแต่ละคลังข้อมูลจะมีการบันทึกจำนวนการชนและเวลาเฉลี่ยที่ใช้ในการแฮช
ฉันทดสอบแล้ว:
ผล
แต่ละผลลัพธ์ประกอบด้วยเวลาแฮชเฉลี่ยและจำนวนการชน
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
หมายเหตุ :
การชนเกิดขึ้นจริงหรือไม่
ใช่. ฉันเริ่มเขียนโปรแกรมทดสอบเพื่อดูว่าการแฮชเกิดขึ้นจริงหรือไม่และไม่ได้เป็นเพียงโครงสร้างทางทฤษฎี พวกเขาเกิดขึ้นจริง ๆ :
FNV-1 การชน
FNV-1a ชนกัน
costarring
ชนกับ liquid
declinate
ชนกับ macallums
altarage
ชนกับ zinke
altarages
ชนกับ zinkes
Murmur2 ชนกัน
cataract
ชนกับ periti
roquette
ชนกับ skivie
shawl
ชนกับ stormbound
dowlases
ชนกับ tramontane
cricketings
ชนกับ twanger
longans
ชนกับ whigs
DJB2 ชนกัน
hetairas
ชนกับ mentioner
heliotropes
ชนกับ neurospora
depravement
ชนกับ serafins
stylist
ชนกับ subgenera
joyful
ชนกับ synaphea
redescribed
ชนกับ urites
dram
ชนกับ vivency
DJB2a ชนกัน
haggadot
ชนกับ loathsomenesses
adorablenesses
ชนกับ rentability
playwright
ชนกับ snush
playwrighting
ชนกับ snushing
treponematoses
ชนกับ waterbeds
การชนกันของ CRC32
codding
ชนกับ gnu
exhibiters
ชนกับ schlager
การชน SuperFastHash
dahabiah
ชนกับ drapability
encharm
ชนกับ enclave
grahams
ชนกับ gramary
- ... snip 79 การชน ...
night
ชนกับ vigil
nights
ชนกับ vigils
finks
ชนกับ vinic
Randomnessification
การวัดแบบอัตนัยอื่น ๆ คือการกระจายแฮชแบบสุ่ม การแมป HashTables ที่เป็นผลลัพธ์แสดงให้เห็นว่ามีการกระจายข้อมูลอย่างสม่ำเสมอ ฟังก์ชันแฮชทั้งหมดแสดงการกระจายที่ดีเมื่อแมปตารางเป็นเส้นตรง:
หรือเป็นแผนที่ Hilbert ( XKCD นั้นมีความเกี่ยวข้องเสมอ ):
ยกเว้นเมื่อ hashing สตริงจำนวน ( "1"
,, "2"
... , "216553"
) (ตัวอย่างเช่นรหัสไปรษณีย์ ) ซึ่งรูปแบบเริ่มปรากฏในอัลกอริทึมการแปลงแป้นพิมพ์ส่วนใหญ่:
SDBM :
DJB2a :
FNV-1 :
ทั้งหมดยกเว้นFNV-1aซึ่งยังดูสุ่มกับฉัน:
ในความเป็นจริงMurmur2ดูเหมือนว่าจะมีการสุ่มดียิ่งขึ้นด้วยNumbers
กว่าFNV-1a
:
เมื่อฉันดูFNV-1a
แผนที่ "หมายเลข" ฉันคิดว่าฉันเห็นรูปแบบแนวตั้งที่ละเอียดอ่อน บ่นกับฉันไม่เห็นรูปแบบเลย คุณคิดอย่างไร?
การเพิ่ม*
ในตารางแสดงว่าการสุ่มนั้นเลวร้ายเพียงใด ด้วยFNV-1a
การเป็นคนที่ดีที่สุดและDJB2x
เป็นคนที่แย่ที่สุด:
Murmur2: .
FNV-1a: .
FNV-1: ▪
DJB2: ▪▪
DJB2a: ▪▪
SDBM: ▪▪▪
SuperFastHash: .
CRC: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Loselose: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪
▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
ตอนแรกฉันเขียนโปรแกรมนี้เพื่อตัดสินใจว่าฉันต้องกังวลเรื่องการชนกันหรือไม่:
จากนั้นมันก็กลายเป็นการทำให้แน่ใจว่าฟังก์ชั่นแฮชสุ่มเพียงพอ
อัลกอริทึม FNV-1a
แฮ FNV1 มาในรูปแบบต่าง ๆ ที่ส่งกลับค่าแฮชขนาด 32, 64, 128, 256, 256, 512 และ 1024 บิต
อัลกอริทึม FNV-1aคือ:
hash = FNV_offset_basis
for each octetOfData to be hashed
hash = hash xor octetOfData
hash = hash * FNV_prime
return hash
ที่ค่าคงที่FNV_offset_basis
และFNV_prime
ขึ้นอยู่กับขนาดแฮชผลตอบแทนที่คุณต้องการ:
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
ดูหน้า FNV หลักสำหรับรายละเอียด
ผลลัพธ์ทั้งหมดของฉันอยู่ในรูปแบบ 32 บิต
FNV-1 ดีกว่า FNV-1a หรือไม่
ไม่ได้ FNV-1a นั้นดีกว่า มีการชนกันมากขึ้นกับ FNV-1a เมื่อใช้คลังคำภาษาอังกฤษ:
Hash Word Collisions
====== ===============
FNV-1 1
FNV-1a 4
ตอนนี้เปรียบเทียบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่:
Hash lowercase word Collisions UPPERCASE word collisions
====== ========================= =========================
FNV-1 1 9
FNV-1a 4 11
ในกรณีนี้ FNV-1a ไม่ใช่"400%"แย่กว่า FN-1 เพียง 20% เท่านั้น
ฉันคิดว่าสิ่งที่สำคัญยิ่งกว่าก็คือการมีอัลกอริทึมสองคลาสเมื่อพูดถึงการชนกัน:
- การชนที่หายาก : FNV-1, FNV-1a, DJB2, DJB2a, SDBM
- การชนกันทั่วไป : SuperFastHash, Loselose
จากนั้นมีการกระจายแฮชอย่างสม่ำเสมอ:
- การกระจายที่โดดเด่น: Murmur2, FNV-1a, SuperFastHas
- การกระจายที่ยอดเยี่ยม: FNV-1
- การกระจายที่ดี: SDBM, DJB2, DJB2a
- การกระจายที่น่ากลัว: Loselose
ปรับปรุง
บ่น? แน่นอนว่าทำไมไม่
ปรับปรุง
@whatshisname สงสัยว่าCRC32จะทำงานอย่างไรเพิ่มตัวเลขลงในตาราง
CRC32 เป็นที่ดีงาม การชนกันน้อย แต่ช้ากว่าและค่าใช้จ่ายของตารางการค้นหาขนาด 1k
ตัดสิ่งที่ผิดพลาดทั้งหมดเกี่ยวกับการกระจาย CRC - ไม่ดีของฉัน
จนถึงวันนี้ฉันจะใช้ FNV-1a เป็นอัลกอริทึมการแฮชตารางแฮชของตารางโดยแท้จริง แต่ตอนนี้ฉันเปลี่ยนมาใช้ Murmur2:
- ได้เร็วขึ้น
- การจำแนกแบบสุ่มที่ดีขึ้นของคลาสทั้งหมดของอินพุต
และฉันจริงๆจริงๆหวังว่าจะมีบางสิ่งบางอย่างผิดปกติกับSuperFastHash
อัลกอริทึมที่ฉันพบ ; มันเลวร้ายเกินไปที่จะได้รับความนิยมเท่าที่เป็นอยู่
อัปเดต:จากหน้าแรก MurmurHash3 บน Google :
(1) - SuperFastHash มีคุณสมบัติการชนที่ไม่ดีซึ่งมีการบันทึกไว้ที่อื่น
ดังนั้นฉันเดาว่ามันไม่ใช่แค่ฉัน
อัปเดต:ฉันรู้ว่าเพราะเหตุใดจึงMurmur
เร็วกว่ารายการอื่น MurmurHash2 ทำงานสี่ไบต์ต่อครั้ง อัลกอริธึมส่วนใหญ่เป็นไบต์ต่อไบต์ :
for each octet in Key
AddTheOctetToTheHash
ซึ่งหมายความว่าเมื่อคีย์ใช้เวลานานขึ้นเสียงพึมพำจะมีโอกาสส่องแสง
ปรับปรุง
การโพสต์ตามเวลาที่เหมาะสมโดยเรย์มอนด์เฉินย้ำความจริงที่ว่า"สุ่ม" GUID นั้นไม่ได้มีไว้สำหรับใช้ในการสุ่ม พวกเขาหรือส่วนย่อยของพวกเขาจะไม่เหมาะสมเป็นคีย์แฮ:
แม้แต่อัลกอริทึมเวอร์ชัน 4 GUID ก็ไม่สามารถรับประกันได้ว่าจะไม่สามารถคาดการณ์ได้เนื่องจากอัลกอริทึมไม่ได้ระบุคุณภาพของตัวสร้างตัวเลขแบบสุ่ม บทความ Wikipedia สำหรับ GUID มีการวิจัยเบื้องต้นซึ่งชี้ให้เห็นว่า GUID ในอนาคตและก่อนหน้านี้สามารถคาดการณ์ได้โดยอาศัยความรู้เกี่ยวกับสถานะของตัวสร้างตัวเลขสุ่มเนื่องจากตัวกำเนิดนั้นไม่ได้เข้ารหัสลับอย่างแข็งแกร่ง
Randomess ไม่เหมือนกับการหลีกเลี่ยงการชน ซึ่งเป็นสาเหตุที่เป็นความผิดพลาดที่จะลองคิดค้นอัลกอริทึม "hashing" ของคุณเองโดยใช้ guid บางส่วนของ "random":
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);
}
หมายเหตุ : อีกครั้งฉันใส่"random GUID"ในเครื่องหมายคำพูดเพราะเป็นตัวแปร "สุ่ม" ของ GUID คำอธิบายที่แม่นยำยิ่งขึ้นก็Type 4 UUID
คือ แต่ไม่มีใครรู้ว่าประเภท 4 หรือประเภท 1, 3 และ 5 คืออะไร ดังนั้นจึงง่ายกว่าที่จะเรียกว่า "สุ่ม" GUID
คำศัพท์ภาษาอังกฤษทั้งหมดสะท้อน