Thuật toán băm nào là tốt nhất cho tính độc đáo và tốc độ?


1388

Thuật toán băm nào là tốt nhất cho tính độc đáo và tốc độ? Ví dụ (tốt) sử dụng bao gồm từ điển băm.

Tôi biết có những thứ như SHA-256 và những thứ như vậy, nhưng các thuật toán này được thiết kế để bảo mật , điều này thường có nghĩa là chúng chậm hơn các thuật toán ít độc đáo hơn . Tôi muốn một thuật toán băm được thiết kế nhanh, nhưng vẫn khá độc đáo để tránh va chạm.


9
Vì mục đích gì, bảo mật hay khác?
Orble

19
@ Đơn giản, để thực hiện một từ điển băm. Vì vậy, va chạm nên được giữ ở mức tối thiểu, nhưng nó không có mục đích bảo mật nào cả.
Earlz

4
Lưu ý rằng bạn sẽ cần phải mong đợi ít nhất một số va chạm trong bảng băm của mình, nếu không, bảng sẽ cần rất lớn để có thể xử lý ngay cả một số lượng khóa tương đối nhỏ ...
Dean Harding

19
Bài đăng tuyệt vời! Bạn cũng có thể kiểm tra xxHash của Yann Collet (người tạo hoặc LZ4), nhanh gấp đôi so với Murmur? Trang chủ: code.google.com/p/xxhash Thông tin khác: fastcompression.blogspot.fr/2012/04/ Kẻ

24
@zvrba Phụ thuộc vào thuật toán. bcrypt được thiết kế để chậm.
Izkata

Câu trả lời:


2461

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:

Nhập mô tả hình ảnh ở đây

Hoặc dưới dạng Bản đồ Hilbert ( XKCD luôn có liên quan ):

Nhập mô tả hình ảnh ở đây

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 :

Nhập mô tả hình ảnh ở đây

DJB2a :

Nhập mô tả hình ảnh ở đây

FNV-1 :

Nhập mô tả hình ảnh ở đây

Tất cả ngoại trừ FNV-1a , trông vẫn khá ngẫu nhiên đối với tôi:

Nhập mô tả hình ảnh ở đây

Trên thực tế, Murmur2 dường như thậm chí còn có tính ngẫu nhiên tốt Numbershơn so với FNV-1a:

Nhập mô tả hình ảnh ở đây

Khi tôi nhìn vào FNV-1abả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-1aviệc là tốt nhất, và DJB2xlà 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_basisFNV_primephụ 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 SuperFastHashthuậ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 Murmurnhanh 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

GUID được thiết kế độc đáo, không ngẫu nhiên

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


41
Sẽ rất thú vị khi xem SHA so sánh như thế nào, không phải vì đây là một ứng cử viên tốt cho thuật toán băm ở đây nhưng sẽ rất thú vị khi xem bất kỳ hàm băm mật mã nào so sánh với các thuật toán tốc độ này.
Michael

8
Một hàm băm mới có tên là 'xxHash', bởi Yann Collet, đã thực hiện các vòng gần đây. Tôi luôn nghi ngờ về một hàm băm mới. Thật thú vị khi xem nó trong so sánh của bạn, (nếu bạn không mệt mỏi với những người gợi ý băm ngẫu nhiên mà họ đã nghe nói để thêm vào ...)
th_in_ss

7
Thật. Các con số hiệu suất được công bố bởi trang dự án xxHash trông rất ấn tượng, có thể quá nhiều để trở thành sự thật. Chà, ít nhất, đó là một dự án nguồn mở: code.google.com/p/xxhash
ATTracker

9
Xin chào Ian, việc triển khai Delphi của tôi về SuperFastHash là chính xác. Khi triển khai tôi đã tạo một bộ kiểm tra trong C và Delphi để so sánh kết quả thực hiện của tôi và việc thực hiện tham chiếu. Không có sự khác biệt. Vì vậy, những gì bạn thấy là những thứ xấu thực tế của băm ... (Đó là lý do tại sao tôi cũng đã công bố một thực hiện MurmurHash: landman-code.blogspot.nl/2009/02/... )
Davy Landman

19
Có phải người đăng nhận thức đây không chỉ là một câu trả lời tuyệt vời - đây là tài liệu tham khảo thực tế của thế giới về chủ đề này? Bất cứ khi nào tôi cần xử lý băm, điều đó giải quyết vấn đề của tôi rất nhanh và có thẩm quyền mà tôi không bao giờ cần bất cứ điều gì khác.
MaiaVictor

59

Nếu bạn muốn tạo một bản đồ băm từ một từ điển không thay đổi, bạn có thể muốn xem xét băm hoàn hảo https://en.wikipedia.org/wiki/Perinf_hash_feft - trong quá trình xây dựng hàm băm và bảng băm, bạn có thể đảm bảo, đối với một tập dữ liệu nhất định, sẽ không có va chạm.


2
Dưới đây là thêm về (tối thiểu) Perfect Băm burtleburtle.net/bob/hash/perfect.html bao gồm dữ liệu hiệu suất, mặc dù nó không sử dụng bộ vi xử lý, vv mới nhất
Ellie Kesselman

4
Điều này khá rõ ràng, nhưng đáng để chỉ ra rằng để đảm bảo không có va chạm, các khóa sẽ phải có cùng kích thước với các giá trị, trừ khi có các ràng buộc về các giá trị mà thuật toán có thể tận dụng.
devios1

1
@ devios1 Tuyên bố của bạn là vô nghĩa. Đầu tiên, các giá trị trong bảng băm, hoàn hảo hay không, độc lập với các khóa. Thứ hai, một bảng băm hoàn hảo chỉ là một mảng các giá trị tuyến tính, được lập chỉ mục bởi kết quả của hàm đã được tạo để tất cả các chỉ mục là duy nhất.
Jim Balter

1
@MarcusJ Băm hoàn hảo thường được sử dụng với ít hơn 100 khóa, nhưng hãy xem cmph.sourceforge.net ... vẫn còn xa tầm của bạn.
Jim Balter

1
@DavidCary Không có gì tại liên kết của bạn hỗ trợ yêu cầu của bạn. Có thể bạn đã nhầm lẫn O (1) với "không va chạm", nhưng chúng hoàn toàn không giống nhau. Tất nhiên, băm hoàn hảo đảm bảo không có va chạm, nhưng nó yêu cầu tất cả các khóa được biết trước và có tương đối ít trong số chúng. (Nhưng xem liên kết đến cmph ở trên.)
Jim Balter

34

Dưới đây là danh sách các hàm băm, nhưng phiên bản ngắn là:

Nếu bạn chỉ muốn có một hàm băm tốt và không thể chờ đợi, djb2là một trong những hàm băm chuỗi tốt nhất mà tôi biết. Nó có sự phân phối và tốc độ tuyệt vời trên nhiều bộ khóa và kích cỡ bảng khác nhau

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
Trên thực tế djb2 không nhạy, vì hầu hết các hàm băm đơn giản như vậy, vì vậy bạn có thể dễ dàng phá vỡ các giá trị băm như vậy. Nó có sự thiên vị xấu quá nhiều va chạm và phân phối kém, nó phá vỡ hầu hết các bài kiểm tra chất lượng của smhasher: Xem github.com/rurban/smhasher/blob/master/doc/bernstein Cơ sở dữ liệu cdb của anh ấy sử dụng nó, nhưng tôi sẽ không sử dụng nó với quyền truy cập công cộng.
rurban

2
DJB là khá xấu từ quan điểm hiệu suất và phân phối. Tôi sẽ không sử dụng nó ngày hôm nay.
Conrad Meyer

@ConradMeyer Tôi cá là, DJB có thể được tăng tốc theo hệ số ba giống như trong câu hỏi này của tôi và sau đó có lẽ nó sẽ đánh bại hầu hết các thuật toán có thể sử dụng. Liên quan đến việc phân phối, tôi đồng ý. Một băm tạo ra va chạm ngay cả đối với hai chuỗi ký tự không thể thực sự tốt.
maaartinus

28

CityHash của Google là thuật toán bạn đang tìm kiếm. Nó không tốt cho mật mã nhưng tốt cho việc tạo các băm độc đáo.

Đọc blog để biết thêm chi tiết và mã có sẵn ở đây .

CityHash được viết bằng C ++. Ngoài ra còn có một cổng C đơn giản .

Giới thiệu về hỗ trợ 32 bit:

Tất cả các chức năng CityHash được điều chỉnh cho bộ xử lý 64 bit. Điều đó nói rằng, họ sẽ chạy (ngoại trừ những cái mới sử dụng SSE4.2) trong mã 32 bit. Họ sẽ không rất nhanh mặc dù. Bạn có thể muốn sử dụng Murmur hoặc một cái gì đó khác trong mã 32 bit.


11
CityHash có được phát âm tương tự như "City Sushi không?"
Eric

2
Có một cái nhìn về SipHash, nó có nghĩa là để thay thế MurmurHash / CityHash / vv. : 131002.net/siphash
Török Edwin

3
Cũng xem FarmHash, một người kế nhiệm CitHash. code.google.com/p/farmhash
stevendaniels 18/03/2015

7
xxHash tuyên bố là nhanh hơn 5x so với CityHash.
Cầu đất sét

plain C portliên kết bị hỏng
Makerj

20

Tôi đã lên kế hoạch so sánh tốc độ ngắn của các thuật toán băm khác nhau khi băm tập tin.

Các lô riêng lẻ chỉ khác nhau một chút trong phương thức đọc và có thể bỏ qua ở đây, vì tất cả các tệp được lưu trữ trong một tmpfs. Do đó, điểm chuẩn không bị ràng buộc IO nếu bạn đang tự hỏi.

Các thuật toán bao gồm : SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}.

Kết luận:

  • Các hàm băm không mã hóa như Murmur3, Cityhash và Spooky khá gần nhau. Mọi người nên lưu ý rằng Cityhash có thể nhanh hơn trên CPU với CRChướng dẫn SSE 4.2 , điều mà CPU của tôi không có. SpookyHash trong trường hợp của tôi luôn luôn là một chút nhỏ trước CityHash.
  • MD5 dường như là một sự đánh đổi tốt khi sử dụng các hàm băm mật mã, mặc dù SHA256 có thể an toàn hơn đối với các lỗ hổng va chạm của MD5 và SHA1.
  • Độ phức tạp của tất cả các thuật toán là tuyến tính - điều này thực sự không đáng ngạc nhiên vì chúng hoạt động theo khối. (Tôi muốn xem liệu phương thức đọc có tạo ra sự khác biệt hay không, vì vậy bạn chỉ có thể so sánh các giá trị ngoài cùng bên phải).
  • SHA256 chậm hơn SHA512.
  • Tôi đã không điều tra tính ngẫu nhiên của các hàm băm. Nhưng đây là một so sánh tốt về các hàm băm bị thiếu trong câu trả lời của Ian Boyds . Điều này chỉ ra rằng CityHash có một số vấn đề trong các trường hợp góc.

Nguồn được sử dụng cho các ô:


1
Biểu đồ tỷ lệ tuyến tính cắt nhãn trục y cho biết số lượng nó đang vẽ. Tôi đoán có lẽ nó sẽ là "thời gian tính bằng giây", giống như thang đo logarit. Đó là giá trị sửa chữa.
Craig McQueen

18

Các thuật toán SHA (bao gồm SHA-256) được thiết kế để nhanh chóng .

Trong thực tế, tốc độ của họ đôi khi có thể là một vấn đề. Cụ thể, một kỹ thuật phổ biến để lưu trữ mã thông báo có nguồn gốc mật khẩu là chạy thuật toán băm nhanh tiêu chuẩn 10.000 lần (lưu trữ hàm băm của hàm băm của hàm băm của ... mật khẩu).

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

Đầu ra:

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
Nó tương đối nhanh, chắc chắn, đối với thuật toán băm mật mã . Nhưng OP chỉ muốn lưu trữ các giá trị trong một hashtable và tôi không nghĩ rằng hàm băm mật mã thực sự phù hợp với điều đó.
Dean Harding

6
Câu hỏi đưa ra (một cách tiếp tuyến, bây giờ nó xuất hiện) chủ đề của các hàm băm mật mã. Đó là bit tôi đang trả lời.
yfeldblum

15
Chỉ cần gạt mọi người ra khỏi ý tưởng "Đặc biệt, một kỹ thuật phổ biến để lưu trữ mã thông báo có nguồn gốc mật khẩu là chạy thuật toán băm nhanh tiêu chuẩn 10.000 lần" - trong khi thông thường, điều đó thật ngu ngốc. Có các thuật toán được thiết kế cho các kịch bản này, ví dụ : bcrypt. Sử dụng đúng công cụ.
TC1

3
Băm mật mã được thiết kế để có thông lượng cao, nhưng điều đó thường có nghĩa là chúng có thiết lập cao, phá vỡ .rodatavà / hoặc chi phí nhà nước. Khi bạn muốn một thuật toán cho một hashtable, bạn thường có các khóa rất ngắn và rất nhiều trong số chúng, nhưng không cần sự đảm bảo bổ sung của một loại tiền mã hóa. Bản thân tôi sử dụng một lần chỉnh sửa của Jenkins.
mirabilos

1
@ChrisMorgan: thay vì sử dụng hàm băm bảo mật bằng mật mã, HashTable DoS có thể được giải quyết hiệu quả hơn bằng cách sử dụng ngẫu nhiên hàm băm, để mỗi lần chạy chương trình hoặc thậm chí trên mọi hàm băm, do đó, dữ liệu không được nhóm vào cùng một nhóm mỗi lần .
Lie Ryan

14

Tôi biết có những thứ như SHA-256 và những thứ như vậy, nhưng các thuật toán này được thiết kế để bảo mật , điều này thường có nghĩa là chúng chậm hơn các thuật toán ít độc đáo hơn .

Giả định rằng các hàm băm mật mã độc đáo hơn là sai và trên thực tế, nó có thể được chứng minh là thường lạc hậu trong thực tế. Trong sự thật:

  1. Các hàm băm mật mã lý tưởng nên không thể phân biệt với ngẫu nhiên ;
  2. Nhưng với các hàm băm không mã hóa, họ mong muốn tương tác thuận lợi với các đầu vào có khả năng .

Điều đó có nghĩa là hàm băm không mã hóa có thể có ít va chạm hơn so với hàm mã hóa đối với tập dữ liệu "tốt" tập dữ liệu mà nó được thiết kế cho.

Chúng ta thực sự có thể chứng minh điều này bằng dữ liệu trong câu trả lời của Ian Boyd và một chút toán học: vấn đề sinh nhật . Công thức cho số lượng cặp va chạm dự kiến ​​nếu bạn chọn nsố nguyên ngẫu nhiên từ tập hợp [1, d]này là (lấy từ Wikipedia):

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

Cắm n= 216,553 và d= 2 ^ 32, chúng tôi nhận được khoảng 5,5 va chạm dự kiến . Các thử nghiệm của Ian hầu hết cho thấy kết quả xung quanh khu vực đó, nhưng với một ngoại lệ kịch tính: hầu hết các chức năng đều có xung đột bằng 0 trong các thử nghiệm số liên tiếp. Xác suất chọn ngẫu nhiên 216.553 số 32 bit và không bị va chạm là khoảng 0,43%. Và đó chỉ là một chức năng, ở đây chúng ta có năm họ hàm băm riêng biệt với các va chạm bằng không!

Vì vậy, những gì chúng ta thấy ở đây là các giá trị băm mà Ian đã thử nghiệm đang tương tác thuận lợi với bộ dữ liệu số liên tiếp, tức là, chúng phân tán các đầu vào khác nhau tối thiểu rộng rãi hơn một hàm băm mật mã lý tưởng. (Side lưu ý:. Điều này có nghĩa rằng đánh giá đồ họa Ian rằng FNV-1a và MurmurHash2 "nhìn ngẫu nhiên" với anh trong bộ số dữ liệu có thể bị bác bỏ từ dữ liệu của mình Zero, va chạm trên một tập hợp dữ liệu kích thước đó, cho cả hàm băm, thật đáng kinh ngạc!)

Đây không phải là một bất ngờ vì đây là một hành vi mong muốn cho nhiều sử dụng hàm băm. Ví dụ, các khóa bảng băm thường rất giống nhau; Câu trả lời của Ian đề cập đến một vấn đề MSN từng gặp phải với các bảng băm mã ZIP . Đây là một cách sử dụng trong đó tránh va chạm vào các đầu vào có khả năng chiến thắng hành vi giống như ngẫu nhiên.

Một so sánh mang tính hướng dẫn khác ở đây là sự tương phản trong các mục tiêu thiết kế giữa CRC và các hàm băm mật mã:

  • CRC được thiết kế để bắt lỗi do các kênh truyền thông ồn ào , có khả năng là một số lượng nhỏ các bit lật;
  • Băm tiền điện tử được thiết kế để bắt các sửa đổi được thực hiện bởi những kẻ tấn công độc hại , những người được phân bổ tài nguyên tính toán hạn chế nhưng thông minh nhiều tùy ý.

Vì vậy, đối với CRC, một lần nữa tốt hơn là có ít va chạm hơn ngẫu nhiên trong các đầu vào khác nhau tối thiểu. Với băm tiền điện tử, đây là điều không nên!


10

Sử dụng SipHash . Nó có nhiều đặc tính mong muốn:

  • Nhanh. Việc thực hiện tối ưu hóa mất khoảng 1 chu kỳ cho mỗi byte.

  • Đảm bảo. SipHash là một PRF mạnh (chức năng giả ngẫu nhiên). Điều này có nghĩa là nó không thể phân biệt được với một hàm ngẫu nhiên (trừ khi bạn biết khóa bí mật 128 bit). Vì thế:

    • Không cần phải lo lắng về việc thăm dò bảng băm của bạn trở thành thời gian tuyến tính do va chạm. Với SipHash, bạn biết rằng bạn sẽ có hiệu suất trung bình trong trường hợp trung bình, bất kể đầu vào.

    • Miễn nhiễm với các cuộc tấn công từ chối dịch vụ dựa trên hàm băm.

    • Bạn có thể sử dụng SipHash (đặc biệt là phiên bản có đầu ra 128 bit) làm MAC (Mã xác thực thư). Nếu bạn nhận được một tin nhắn và thẻ SipHash và thẻ này giống như khi chạy SipHash bằng khóa bí mật của bạn, thì bạn biết rằng bất cứ ai tạo ra hàm băm cũng đều sở hữu khóa bí mật của bạn và cả tin nhắn cũng không phải là tin nhắn băm đã được thay đổi kể từ đó.


1
Không phải SipHash quá mức cần thiết trừ khi bạn cần bảo mật? Yêu cầu khóa 128 bit chỉ là một hạt băm được tôn vinh. Chưa kể MurmurHash3 có đầu ra 128 bit và SipHash chỉ có đầu ra 64 bit. Rõ ràng các tiêu hóa lớn hơn có cơ hội va chạm thấp hơn.
bryc

@bryc Sự khác biệt là SipHash sẽ tiếp tục hoạt động tốt, ngay cả trên đầu vào độc hại. Bảng băm dựa trên SipHash có thể được sử dụng cho dữ liệu từ các nguồn có khả năng thù địch và có thể sử dụng thuật toán như thăm dò tuyến tính rất nhạy cảm với các chi tiết của hàm băm.
Demi

9

Nó phụ thuộc vào dữ liệu bạn đang băm. Một số băm hoạt động tốt hơn với dữ liệu cụ thể như văn bản. Một số thuật toán băm được thiết kế cụ thể là tốt cho dữ liệu cụ thể.

Paul Hsieh từng thực hiện băm nhanh . Ông liệt kê mã nguồn và giải thích. Nhưng nó đã bị đánh rồi. :)


6

Java sử dụng thuật toán nhân và thêm đơn giản này :

Mã băm cho một đối tượng String được tính là

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

sử dụng int số học, nơi s[i]tôi nhân vật -thứ của chuỗi, nlà chiều dài của chuỗi, và ^chỉ lũy thừa. (Giá trị băm của chuỗi rỗng bằng không.)

Có thể có nhiều cái tốt hơn ngoài kia nhưng điều này khá phổ biến và dường như là một sự đánh đổi tốt giữa tốc độ và tính độc đáo.


12
Tôi sẽ không sử dụng chính xác cùng một thứ được sử dụng ở đây, vì nó vẫn tương đối dễ tạo ra va chạm với thứ này. Nó chắc chắn không khủng khiếp, nhưng có nhiều thứ tốt hơn ngoài kia. Và nếu không có lý do quan trọng nào để tương thích với Java, thì không nên chọn nó.
Joachim Sauer

4
Nếu bạn vẫn chọn cách băm này vì một số lý do, ít nhất bạn có thể sử dụng một số nguyên tố tốt hơn như 92821 như một số nhân. Điều đó làm giảm va chạm nhiều. stackoverflow.com/a/2816747/21499
Hans-Peter Störr

1
Bạn cũng có thể sử dụng FNV1a thay thế. Đây cũng là một hàm băm dựa trên phép nhân đơn giản, nhưng sử dụng số nhân lớn hơn, giúp phân tán hàm băm tốt hơn.
bryc

4

Trước hết, tại sao bạn cần phải thực hiện băm của riêng mình? Đối với hầu hết các tác vụ, bạn sẽ nhận được kết quả tốt với cấu trúc dữ liệu từ thư viện chuẩn, giả sử có sẵn một triển khai (trừ khi bạn chỉ làm việc này cho giáo dục của chính mình).

Theo như các thuật toán băm thực tế, yêu thích cá nhân của tôi là FNV. 1

Đây là một ví dụ triển khai phiên bản 32 bit trong 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
Biến thể FNV-1a tốt hơn một chút với tính ngẫu nhiên. Hoán đổi thứ tự của *^: h = (h * 16777619) ^ p[i]==>h = (h ^ p[i]) * 16777619
Ian Boyd
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.