อัลกอริทึมการแปลงแป้นพิมพ์แบบใดดีที่สุดสำหรับความเป็นเอกลักษณ์และความเร็ว


1388

อัลกอริทึมการแปลงแป้นพิมพ์แบบใดดีที่สุดสำหรับความเป็นเอกลักษณ์และความเร็ว ตัวอย่างการใช้ (ดี) รวมถึงพจนานุกรมแฮช

ฉันรู้ว่ามีสิ่งที่ต้องการSHA-256และดังกล่าว แต่ขั้นตอนวิธีการเหล่านี้จะถูกออกแบบมาให้เป็นที่เชื่อถือได้ซึ่งมักจะหมายความว่าพวกเขาจะช้ากว่าอัลกอริทึมที่น้อยที่ไม่ซ้ำกัน ฉันต้องการอัลกอริทึมแฮชที่ออกแบบมาให้เร็ว แต่ก็ยังคงมีเอกลักษณ์ที่ไม่เหมือนใคร


9
เพื่อวัตถุประสงค์อะไรความปลอดภัยหรืออื่น ๆ
Orbling

19
@Orbling สำหรับการนำพจนานุกรมแฮชไปใช้ ดังนั้นควรมีการชนกันน้อยที่สุด แต่ก็ไม่มีจุดประสงค์ด้านความปลอดภัยเลย
Earlz

4
โปรดทราบว่าคุณจะต้องคาดหวังว่ามีการชนกันอย่างน้อยในตารางแฮชของคุณมิฉะนั้นตารางจะต้องมีขนาดใหญ่มากเพื่อให้สามารถจัดการกับกุญแจจำนวนเล็กน้อย ...
Dean Harding

19
โพสต์ที่ยอดเยี่ยม! คุณสามารถตรวจสอบ xxHash (ผู้สร้างหรือ LZ4) ของ Yann Collet ซึ่งเร็วกว่า Murmur สองเท่าได้ไหม โฮมเพจ: code.google.com/p/xxhashข้อมูลเพิ่มเติม: fastcompression.blogspot.fr/2012/04/…

24
@zvrba ขึ้นอยู่กับอัลกอริทึม bcrypt ถูกออกแบบมาให้ช้า
Izkata

คำตอบ:


2461

ฉันทดสอบอัลกอริทึมที่แตกต่างกันการวัดความเร็วและจำนวนการชน

ฉันใช้ชุดคีย์สามแบบ:

สำหรับแต่ละคลังข้อมูลจะมีการบันทึกจำนวนการชนและเวลาเฉลี่ยที่ใช้ในการแฮช

ฉันทดสอบแล้ว:

ผล

แต่ละผลลัพธ์ประกอบด้วยเวลาแฮชเฉลี่ยและจำนวนการชน

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 การชน

  • creamwove ชนกับ quists

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 ได้รับการออกแบบให้ไม่ซ้ำกันไม่ใช่แบบสุ่ม

การโพสต์ตามเวลาที่เหมาะสมโดยเรย์มอนด์เฉินย้ำความจริงที่ว่า"สุ่ม" 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

คำศัพท์ภาษาอังกฤษทั้งหมดสะท้อน


41
มันจะน่าสนใจจริง ๆ เพื่อดูว่า SHA เปรียบเทียบไม่ใช่เพราะมันเป็นตัวเลือกที่ดีสำหรับอัลกอริทึมการแปลงแป้นพิมพ์ที่นี่ แต่มันน่าสนใจมากที่จะดูว่าแฮชการเข้ารหัสใด ๆ เปรียบเทียบกับสิ่งเหล่านี้
Michael

8
แฮชใหม่โดยใช้ชื่อ 'xxHash' โดย Yann Collet กำลังทำรอบนี้ ฉันมักจะสงสัยแฮชใหม่ มันจะน่าสนใจที่จะเห็นมันในการเปรียบเทียบของคุณ (ถ้าคุณไม่เบื่อที่จะมีคนแนะนำแฮชแบบสุ่มที่พวกเขาเคยได้ยินว่าจะเพิ่ม ... )
th_in_gs

7
จริง ตัวเลขประสิทธิภาพที่ประกาศโดยหน้าโปรเจ็กต์ xxHash นั้นดูน่าประทับใจน่าจะเกินจริง อย่างน้อยก็เป็นโครงการโอเพ่นซอร์ส: code.google.com/p/xxhash
ATTracker

9
สวัสดีเอียนการใช้งาน SuperFastHash ของ Delphi นั้นถูกต้อง เมื่อนำไปใช้ฉันสร้างชุดทดสอบใน C และ Delphi เพื่อเปรียบเทียบผลลัพธ์ของการติดตั้งใช้งานและการอ้างอิง ไม่มีความแตกต่าง ดังนั้นสิ่งที่คุณเห็นคือความเลวร้ายที่แท้จริงของแฮช ... (นั่นคือเหตุผลที่ฉันเผยแพร่การใช้งานMurmurHash : landman-code.blogspot.nl/2009/02/ … )
Davy Landman

19
ผู้โพสต์ทราบหรือไม่ว่านี่ไม่ใช่คำตอบที่ยอดเยี่ยมนี่เป็นแหล่งอ้างอิงทางโลกในเรื่องนี้ เมื่อใดก็ตามที่ฉันต้องจัดการกับแฮชที่แก้ปัญหาของฉันอย่างรวดเร็วและมีอำนาจที่ฉันไม่ต้องการอะไรอีกเลย
MaiaVictor

59

หากคุณต้องการสร้างแผนที่แฮชจากพจนานุกรมที่ไม่มีการเปลี่ยนแปลงคุณอาจต้องการพิจารณาการแฮชที่สมบูรณ์แบบhttps://en.wikipedia.org/wiki/Perfect_hash_function - ในระหว่างการสร้างฟังก์ชันแฮชและตารางแฮชคุณสามารถรับประกันได้ว่า สำหรับชุดข้อมูลที่กำหนดว่าจะไม่มีการชนกัน


2
นี่คือเพิ่มเติมเกี่ยวกับ (ขั้นต่ำ) Perfect Hashing burtleburtle.net/bob/hash/perfect.htmlรวมถึงข้อมูลประสิทธิภาพแม้ว่าจะไม่ได้ใช้หน่วยประมวลผลล่าสุด ฯลฯ
Ellie Kesselman

4
เห็นได้ชัดว่าสวยมาก แต่ก็คุ้มค่าที่ชี้ให้เห็นว่าเพื่อรับประกันว่าไม่มีการชนคีย์จะต้องมีขนาดเท่ากับค่ายกเว้นว่ามีข้อ จำกัด ในค่าที่อัลกอริทึมสามารถใช้ประโยชน์ได้
devios1

1
@ devios1 ข้อความของคุณไม่มีความหมาย ก่อนอื่นค่าในตารางแฮชสมบูรณ์แบบหรือไม่นั้นขึ้นอยู่กับกุญแจ ประการที่สองตารางแฮชที่สมบูรณ์แบบเป็นเพียงอาร์เรย์เชิงเส้นของค่าซึ่งจัดทำดัชนีโดยผลลัพธ์ของฟังก์ชันที่สร้างขึ้นเพื่อให้ดัชนีทั้งหมดไม่ซ้ำกัน
Jim Balter

1
@MarcusJ การแฮชที่สมบูรณ์แบบมักใช้กับคีย์น้อยกว่า 100 ตัว แต่ลองดูที่cmph.sourceforge.net ... ยังห่างไกลจากช่วงของคุณ
Jim Balter

1
@DavidCary ไม่มีสิ่งใดในลิงก์ของคุณที่สนับสนุนการอ้างสิทธิ์ของคุณ อาจเป็นไปได้ว่าคุณสับสน O (1) กับ "no collision" แต่มันไม่เหมือนกันทั้งหมด แน่นอนว่าการแฮ็กที่สมบูรณ์แบบรับประกันได้ว่าไม่มีการชนกัน แต่จำเป็นต้องทราบกุญแจทั้งหมดล่วงหน้าและมีปุ่มค่อนข้างน้อย (แต่ดูลิงค์ไปยัง cmph ด้านบน)
Jim Balter

34

นี่คือรายการฟังก์ชั่นแฮช แต่รุ่นสั้นคือ:

ถ้าคุณแค่อยากจะมีฟังก์ชั่นแฮชที่ดีและไม่สามารถรอได้djb2มันเป็นหนึ่งในฟังก์ชั่นแฮชสตริงที่ดีที่สุดที่ฉันรู้จัก มันมีการกระจายและความเร็วที่ยอดเยี่ยมสำหรับชุดคีย์และขนาดตารางที่แตกต่างกันมากมาย

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;
}

6
ที่จริงแล้ว djb2 นั้นไม่มีความอ่อนไหวเป็นศูนย์เพราะฟังก์ชั่นแฮชธรรมดาส่วนใหญ่ดังนั้นคุณสามารถทำลายแฮชได้อย่างง่ายดาย มันมีอคติที่ไม่ดีมากเกินไปและมีการแจกแจงที่ไม่ดีมันทำการทดสอบคุณภาพของ smhasher ส่วนใหญ่: ดูgithub.com/rurban/smhasher/blob/master/doc/bernstein ฐานข้อมูล cdb ของเขาใช้ แต่ฉันจะไม่ใช้มัน ด้วยการเข้าถึงสาธารณะ
rurban

2
DJB ค่อนข้างแย่จากประสิทธิภาพและจุดยืนในการจัดจำหน่าย ฉันจะไม่ใช้มันวันนี้
Conrad Meyer

@ConradMeyer ฉันพนันได้เลยว่า DJB สามารถถูกเร่งขึ้นด้วยปัจจัยสามอย่างในคำถามของฉันและจากนั้นมันอาจเอาชนะอัลกอริทึมที่ใช้งานได้มากที่สุด เกี่ยวกับการกระจายตัวฉันเห็นด้วย ความยุ่งเหยิงที่ทำให้เกิดการชนแม้สำหรับตัวอักษรสองตัวนั้นไม่สามารถทำได้ดีนัก
maaartinus

28

CityHash โดย Google เป็นอัลกอริทึมที่คุณกำลังมองหา มันไม่ดีสำหรับการเข้ารหัส แต่ดีสำหรับการสร้างแฮชที่เป็นเอกลักษณ์

อ่านบล็อกสำหรับรายละเอียดเพิ่มเติมและรหัสสามารถใช้ได้ที่นี่

CityHash เขียนด้วย C ++ นอกจากนี้ยังเป็นธรรมดาพอร์ต C

เกี่ยวกับการสนับสนุนแบบ 32 บิต:

ฟังก์ชัน CityHash ทั้งหมดได้รับการปรับแต่งสำหรับโปรเซสเซอร์ 64 บิต ที่กล่าวว่าพวกเขาจะทำงาน (ยกเว้นใหม่ที่ใช้ SSE4.2) ในรหัส 32 บิต พวกเขาจะไม่เร็วมากแม้ว่า คุณอาจต้องการใช้เสียงพึมพำหรืออย่างอื่นในรหัส 32 บิต


11
CityHash ออกเสียงคล้ายกับ "City Sushi หรือไม่?"
Eric

2
ลองดูที่ SipHash ด้วยเช่นกันเพื่อใช้แทน MurmurHash / CityHash / etc : 131002.net/siphash
Török Edwin

3
ดู FarmHash ผู้สืบทอดต่อ CitHash code.google.com/p/farmhash
stevendaniels

7
xxHashอ้างว่าเร็วกว่า CityHash 5 เท่า
Clay Bridges

plain C portลิงก์ใช้งานไม่ได้
makerj

20

ฉันได้วางแผนการเปรียบเทียบความเร็วสั้น ๆ ของอัลกอริทึมการแฮชที่แตกต่างกันเมื่อทำการแฮชไฟล์

แต่ละแปลงแตกต่างกันเล็กน้อยในวิธีการอ่านและสามารถละเว้นได้ที่นี่เนื่องจากไฟล์ทั้งหมดถูกเก็บไว้ใน tmpfs ดังนั้นมาตรฐานไม่ได้ถูกผูกไว้กับ IO หากคุณสงสัย

SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}ขั้นตอนวิธีการรวมถึง:

สรุป:

  • ฟังก์ชันแฮชที่ไม่มีการเข้ารหัสเช่น Murmur3, Cityhash และ Spooky นั้นอยู่ใกล้กัน สิ่งหนึ่งที่ควรทราบว่า Cityhash อาจเร็วกว่าสำหรับซีพียูที่มีCRCคำสั่งSSE 4.2 ซึ่ง CPU ของฉันไม่มี SpookyHash เป็นในกรณีของฉันเล็กน้อยก่อน CityHash
  • MD5 ดูเหมือนจะเป็นการแลกเปลี่ยนที่ดีเมื่อใช้ฟังก์ชันแฮชการเข้ารหัสแม้ว่า SHA256 อาจมีความปลอดภัยมากกว่าสำหรับช่องโหว่การชนของ MD5 และ SHA1
  • ความซับซ้อนของอัลกอริธึมทั้งหมดเป็นแบบเชิงเส้น - ซึ่งไม่น่าแปลกใจนักเนื่องจากมันทำงานแบบบล็อค (ฉันต้องการดูว่าวิธีการอ่านสร้างความแตกต่างหรือไม่เพื่อให้คุณสามารถเปรียบเทียบค่าที่ถูกต้องที่สุด)
  • SHA256 ช้ากว่า SHA512
  • ฉันไม่ได้ตรวจสอบการสุ่มของฟังก์ชันแฮช แต่นี่คือการเปรียบเทียบที่ดีของฟังก์ชันแฮชที่หายไปในคำตอบเอียนสครีก นี่ชี้ให้เห็นว่า CityHash มีปัญหาบางอย่างในมุมกรณี

แหล่งที่มาสำหรับแปลง:


1
กราฟสเกลเชิงเส้นตัดเลเบลแกน y ซึ่งบอกปริมาณที่กำลังวางแผน ฉันเดาว่าคงเป็น "เวลาเป็นวินาที" เช่นเดียวกับมาตราส่วนลอการิทึม มันคุ้มค่าที่จะแก้ไข
Craig McQueen

18

อัลกอริทึม SHA (รวมถึง SHA-256) ได้รับการออกแบบให้เป็นไปอย่างรวดเร็ว

ในความเป็นจริงความเร็วของพวกเขาอาจมีปัญหาบางครั้ง โดยเฉพาะอย่างยิ่งเทคนิคทั่วไปในการจัดเก็บโทเค็นที่ได้รับรหัสผ่านคือการใช้อัลกอริทึมแฮ็บแบบเร็วมาตรฐาน 10,000 ครั้ง (การเก็บแฮชของแฮชของแฮชของแฮชของรหัสผ่าน ... )

#!/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

เอาท์พุท:

Rehearsal ------------------------------------
   1.480000   0.000000   1.480000 (  1.391229)
--------------------------- total: 1.480000sec

       user     system      total        real
   1.400000   0.000000   1.400000 (  1.382016)

57
มันค่อนข้างเร็วแน่ใจว่าสำหรับขั้นตอนวิธีการเข้ารหัสลับคร่ำเครียด แต่ OP ต้องการเก็บค่าไว้ใน hashtable และฉันไม่คิดว่าฟังก์ชันการเข้ารหัสลับนั้นเหมาะสมกับมันจริงๆ
Dean Harding

6
คำถามที่นำมาใช้ (โดยทั่วไปแล้วจะปรากฏขึ้น) เรื่องของฟังก์ชันแฮชการเข้ารหัส นั่นคือสิ่งที่ฉันกำลังตอบสนอง
yfeldblum

15
เพียงเพื่อทำให้ผู้คนไม่คิดว่า "โดยเฉพาะเทคนิคทั่วไปในการจัดเก็บโทเค็นที่ได้รับรหัสผ่านคือการใช้อัลกอริทึมแฮ็บแบบเร็วมาตรฐาน 10,000 ครั้ง" - ในขณะที่เป็นเรื่องธรรมดา มีอัลกอริทึมที่ออกแบบมาสำหรับสถานการณ์เหล่านี้เช่นbcrypt. ใช้เครื่องมือที่เหมาะสม
TC1

3
แฮ็คการเข้ารหัสได้รับการออกแบบให้มีปริมาณงานสูง แต่บ่อยครั้งหมายความว่ามี.rodataค่าใช้จ่ายในการติดตั้งการฉีกขาดและ / หรือสถานะสูง เมื่อคุณต้องการอัลกอริทึมสำหรับ hashtable คุณมักจะมีคีย์สั้นมากและจำนวนมาก แต่ไม่ต้องการการรับรองเพิ่มเติมของการเข้ารหัส ฉันใช้ Jenkins 'ครั้งเดียวต่อตัวเอง
mirabilos

1
@ChrisMorgan: แทนที่จะใช้แฮชที่มีการเข้ารหัสแบบปลอดภัย HashTable DoS สามารถแก้ไขได้อย่างมีประสิทธิภาพมากขึ้นโดยใช้การสุ่มแฮชเพื่อให้ทุกการทำงานของโปรแกรมหรือแม้แต่ในแฮชตารางทุกครั้งดังนั้นข้อมูลจึงไม่ถูกจัดกลุ่มในที่เก็บข้อมูลเดียวกันทุกครั้ง .
Lie Ryan

14

ฉันรู้ว่ามีสิ่งที่ต้องการ SHA-256 และดังกล่าว แต่ขั้นตอนวิธีการเหล่านี้จะถูกออกแบบมาให้เป็นที่เชื่อถือได้ซึ่งมักจะหมายความว่าพวกเขาจะช้ากว่าอัลกอริทึมที่น้อยที่ไม่ซ้ำกัน

การสันนิษฐานว่าฟังก์ชั่นการเข้ารหัสลับนั้นมีลักษณะเฉพาะที่ผิดมากและในความเป็นจริงมันสามารถแสดงให้เห็นได้ว่าบ่อยครั้งในทางปฏิบัติ ในความจริง:

  1. ฟังก์ชันแฮชเข้ารหัสลับควรจะต้องแยกไม่ออกจากการสุ่ม ;
  2. แต่ด้วยฟังก์ชั่นแฮชที่ไม่ใช่การเข้ารหัสลับมันเป็นที่พึงปรารถนาสำหรับพวกเขาที่จะโต้ตอบอย่างเหมาะสมกับอินพุตที่เป็นไปได้

ซึ่งหมายความว่าฟังก์ชันแฮชที่ไม่ใช่การเข้ารหัสอาจมีการชนกันน้อยกว่าการเข้ารหัสลับสำหรับชุดข้อมูล "ดี" ซึ่งเป็นชุดข้อมูลที่ได้รับการออกแบบมา

เราสามารถแสดงให้เห็นจริงนี้กับข้อมูลในคำตอบของเอียนบอยด์และบิตของคณิตศาสตร์ที่: ปัญหาวันเกิด สูตรสำหรับจำนวนการชนที่คาดหวังถ้าคุณเลือกnจำนวนเต็มแบบสุ่มจากชุด[1, d]คือ (นำมาจาก Wikipedia):

n - d + d * ((d - 1) / d)^n

การเสียบn= 216,553 และd= 2 ^ 32 เราได้รับการชนประมาณ5.5ครั้ง การทดสอบของเอียนส่วนใหญ่แสดงผลลัพธ์รอบ ๆ ละแวกนั้น แต่มีข้อยกเว้นอย่างมาก: ฟังก์ชั่นส่วนใหญ่มีการชนกันเป็นศูนย์ในการทดสอบหมายเลขติดต่อกัน ความน่าจะเป็นในการเลือกตัวเลขแบบสุ่ม 216,553 32 บิตและการชนแบบไม่มีศูนย์มีค่าประมาณ 0.43% และนั่นเป็นเพียงฟังก์ชั่นเดียว - ที่นี่เรามีตระกูลแฮชฟังก์ชันที่แตกต่างกันห้ารายการโดยไม่มีการชน!

ดังนั้นสิ่งที่เราได้เห็นที่นี่เป็นที่แฮชที่เอียนทดสอบจะมีปฏิสัมพันธ์ในเกณฑ์ดีกับชุดข้อมูลที่หมายเลขติดต่อกันนั่นคือพวกเขากำลังกระจายปัจจัยการผลิตที่แตกต่างกันน้อยที่สุดอย่างกว้างขวางมากขึ้นกว่าฟังก์ชันแฮชที่เหมาะสำหรับการเข้ารหัสลับจะ (หมายเหตุด้านข้าง:. นี้หมายความว่าการประเมินผลกราฟิกของเอียนที่ FNV-1a และ MurmurHash2 "มองสุ่ม" ให้เขาอยู่ในชุดตัวเลขข้อมูลที่สามารถข้องแวะจากข้อมูลของตัวเองเป็นศูนย์การชนกันในชุดข้อมูลที่มีขนาดว่าสำหรับทั้งฟังก์ชั่นกัญชา ไม่ใช่แบบสุ่มที่ยอดเยี่ยม!)

สิ่งนี้ไม่น่าแปลกใจเพราะนี่เป็นพฤติกรรมที่พึงประสงค์สำหรับการใช้งานฟังก์ชันแฮชจำนวนมาก ตัวอย่างเช่นคีย์ตารางแฮชมักจะคล้ายกันมาก คำตอบของเอียนกล่าวถึงปัญหา MSN เคยมีกับตารางแฮชรหัสไปรษณีย์ นี่คือการใช้งานที่การหลีกเลี่ยงการชนกันของข้อมูลที่น่าจะชนะเหนือพฤติกรรมแบบสุ่ม

การเปรียบเทียบที่ให้คำแนะนำที่นี่อีกประการหนึ่งคือความแตกต่างในเป้าหมายการออกแบบระหว่าง CRC และฟังก์ชันแฮชการเข้ารหัส:

  • ซีอาร์ซีถูกออกแบบมาเพื่อจับข้อผิดพลาดที่เกิดจากช่องทางการสื่อสารที่มีเสียงดังซึ่งมีแนวโน้มที่จะเป็นจำนวนน้อยของบิตพลิก;
  • แฮชของ Crypto ได้รับการออกแบบมาเพื่อตรวจจับการเปลี่ยนแปลงที่ทำโดยผู้โจมตีที่เป็นอันตรายซึ่งได้รับการจัดสรรทรัพยากรการคำนวณที่ จำกัด แต่ก็มีความฉลาดมาก

ดังนั้นสำหรับ CRC จะเป็นการดีที่จะมีการชนน้อยกว่าสุ่มในอินพุตที่ต่างกันเล็กน้อย ด้วยแฮ็กเข้ารหัสลับนี่เป็นข้อห้าม!


10

ใช้SipHash มันมีคุณสมบัติที่ต้องการจำนวนมาก :

  • รวดเร็ว การปรับใช้ที่เหมาะสมจะใช้เวลาประมาณ 1 รอบต่อไบต์

  • การรักษาความปลอดภัย SipHash เป็น PRF ที่แข็งแกร่ง (ฟังก์ชัน pseudorandom) ซึ่งหมายความว่าไม่สามารถแยกได้จากฟังก์ชั่นแบบสุ่ม (เว้นแต่คุณจะรู้รหัสลับ 128 บิต) ดังนั้น:

    • ไม่จำเป็นต้องกังวลเกี่ยวกับโพรบตารางแฮชของคุณกลายเป็นเวลาเชิงเส้นเนื่องจากการชน ด้วย SipHash คุณจะรู้ว่าคุณจะได้รับผลการปฏิบัติงานโดยเฉลี่ยเป็นกรณี ๆ ไปโดยไม่คำนึงถึงปัจจัยการผลิต

    • การยกเว้นบริการแฮชตามการปฏิเสธของการโจมตีบริการ

    • คุณสามารถใช้ SipHash (โดยเฉพาะรุ่นที่มีเอาต์พุต 128- บิต) เป็น MAC (รหัสการตรวจสอบข้อความ) หากคุณได้รับข้อความและแท็ก SipHash และแท็กนั้นเหมือนกับที่เรียกใช้ SipHash ด้วยรหัสลับของคุณคุณจะรู้ว่าใครก็ตามที่สร้างแฮชนั้นก็เป็นเจ้าของคีย์ลับของคุณด้วยและไม่ว่าข้อความหรือ แฮชได้รับการแก้ไขตั้งแต่


1
SipHash ไม่ overkill เว้นแต่คุณต้องการความปลอดภัยหรือไม่ ต้องการคีย์ 128- บิตซึ่งเป็นเพียงเมล็ดกัญชาที่ได้รับเกียรติ ไม่ต้องพูดถึง MurmurHash3 มีเอาต์พุต 128- บิตและ SipHash มีเพียง 64- บิตเอาต์พุต เห็นได้ชัดว่าการแยกย่อยที่ใหญ่กว่ามีโอกาสชนน้อยกว่า
bryc

@bryc ความแตกต่างคือ SipHash จะยังคงทำงานได้ดีแม้ในการป้อนข้อมูลที่เป็นอันตราย ตารางแฮชตาม SipHash สามารถใช้สำหรับข้อมูลจากแหล่งที่อาจเป็นศัตรูและสามารถใช้อัลกอริทึมเช่นการตรวจสอบเชิงเส้นที่มีความไวต่อรายละเอียดของฟังก์ชันแฮช
Demi

9

ขึ้นอยู่กับข้อมูลที่คุณกำลังแฮช การแปลงแป้นพิมพ์บางแบบทำงานได้ดีขึ้นกับข้อมูลเฉพาะเช่นข้อความ อัลกอริทึมการแฮชบางอย่างนั้นได้รับการออกแบบมาเป็นพิเศษสำหรับข้อมูลที่เฉพาะเจาะจง

พอล Hsieh ทำครั้งเดียวกัญชาได้อย่างรวดเร็ว เขาแสดงรายการซอร์สโค้ดและคำอธิบาย แต่มันถูกตีแล้ว :)


6

Java ใช้อัลกอริทึมคูณและเพิ่มง่าย ๆนี้ :

รหัสแฮชสำหรับวัตถุสตริงจะถูกคำนวณเป็น

 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

การหาค่า int ที่s[i]เป็นฉันตัวอักษร -th ของสตริงที่nมีความยาวของสตริงและ^บ่งบอกถึงการยกกำลัง (ค่าแฮชของสตริงว่างคือศูนย์)

อาจมีสิ่งที่ดีกว่าออกไป แต่สิ่งนี้ค่อนข้างแพร่หลายและดูเหมือนว่าเป็นการแลกเปลี่ยนที่ดีระหว่างความเร็วและเอกลักษณ์


12
ฉันจะไม่ใช้อันเดียวกับที่ใช้ตรงนี้เพราะมันยังค่อนข้างง่ายต่อการชนกับสิ่งนี้ แน่นอนว่ามันไม่ได้น่ากลัว แต่มีคนที่ดีกว่าอยู่ที่นั่น และถ้าไม่มีเหตุผลสำคัญที่จะเข้ากันได้กับ Java ก็ไม่ควรเลือก
Joachim Sauer

4
หากคุณยังคงเลือกวิธีการแฮ็กนี้ด้วยเหตุผลบางอย่างคุณอย่างน้อยก็สามารถใช้นายกที่ดีกว่าเช่น 92821 เป็นตัวคูณ ที่ช่วยลดการชนกันมาก stackoverflow.com/a/2816747/21499
Hans-Peter Störr

1
คุณอาจใช้ FNV1a แทน นอกจากนี้ยังเป็นแฮชที่ใช้การคูณแบบง่าย ๆ แต่ใช้ตัวคูณที่ใหญ่กว่าซึ่งจะทำให้การแฮชดีขึ้น
bryc

4

ก่อนอื่นคุณต้องใช้การแปลงแป้นพิมพ์ของคุณเองทำไม สำหรับงานส่วนใหญ่คุณควรได้รับผลลัพธ์ที่ดีกับโครงสร้างข้อมูลจากไลบรารีมาตรฐานโดยสมมติว่ามีการติดตั้งใช้งานอยู่ (เว้นแต่คุณจะทำสิ่งนี้เพื่อการศึกษาของคุณเอง)

เท่าที่อัลกอริทึมการแฮชจริงไปแล้วสิ่งที่ฉันชอบคือ FNV 1

นี่คือตัวอย่างการใช้งานของรุ่น 32 บิตใน 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;
}

2
ตัวแปร FNV-1a นั้นดีขึ้นเล็กน้อยด้วยการสุ่ม สลับลำดับของ*และ^: h = (h * 16777619) ^ p[i]==>h = (h ^ p[i]) * 16777619
Ian Boyd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.