고유성과 속도에 가장 적합한 해싱 알고리즘은 무엇입니까?


1388

고유성과 속도에 가장 적합한 해싱 알고리즘은 무엇입니까? 예 (양호)에는 해시 사전 포함이 사용됩니다.

내가 좋아하는 일이 알고 SHA-256 등이 있지만, 이러한 알고리즘이되는 설계안전한 보통은 작은 알고리즘보다 느린 것을 의미하는 독특한 . 빠른 해시 알고리즘을 원하지만 충돌을 피하기 위해 상당히 독창적입니다.


9
어떤 목적으로, 보안 또는 기타?
Orbling

19
해시 사전의 구현을 위해 @Orbling. 따라서 충돌을 최소화해야하지만 보안 목적은 전혀 없습니다.
Earlz

4
당신은 적어도 예상 할 필요가 있습니다 어떤 그렇지 않으면 테이블이 키도 상대적으로 적은 수 ... 처리 할 수 엄청난해야합니다, 당신의 해시 테이블에서 충돌을
딘 하딩

19
좋은 포스트! Murmur보다 두 배 빠른 Yann Collet의 xxHash (작성자 또는 LZ4)도 확인할 수 있습니까? 홈페이지 : 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

참고 사항 :

  • LoseLose 알고리즘 (여기서 해시 = 해시 + 문자) 진정으로 끔찍한 . 모든 것이 동일한 1,375 개의 버킷에 충돌
  • SuperFastHash는 빠르며, 사물이 꽤 흩어져 있습니다. 나의 선하심에 의해 숫자 충돌. 나는 그것을 포팅 한 사람이 뭔가 잘못 되기를 바라고 있다. 꽤 나빠
  • CRC32는 꽤 좋습니다 . 느리고 1k 조회 테이블

실제로 충돌이 발생합니까?

예. 해시 충돌이 실제로 발생 하는지 확인하기 위해 테스트 프로그램을 작성하기 시작했으며 이론적 인 구성이 아닙니다. 그들은 실제로 일어난다 :

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
  • ... 79 충돌 사고 ...
  • night ~와 충돌하다 vigil
  • nights ~와 충돌하다 vigils
  • finks ~와 충돌하다 vinic

무작위성

다른 주관적인 척도는 해시가 얼마나 무작위로 분포되어 있는지입니다. 결과 HashTable을 매핑하면 데이터가 얼마나 균등하게 분배되는지 보여줍니다. 테이블을 선형으로 매핑 할 때 모든 해시 함수가 잘 분포되어 있습니다.

여기에 이미지 설명을 입력하십시오

또는 힐버트 맵 ( XKCD는 항상 관련이 있습니다 ) :

여기에 이미지 설명을 입력하십시오

대부분의 해싱 알고리즘에서 패턴이 나타나기 시작하는 숫자 문자열 ( "1",, "2"..., "216553") (예 : 우편 번호 )을 해시하는 경우를 제외하고 다음을 수행하십시오.

SDBM :

여기에 이미지 설명을 입력하십시오

DJB2a :

여기에 이미지 설명을 입력하십시오

FNV-1 :

여기에 이미지 설명을 입력하십시오

FNV-1a를 제외한 모든 것은 여전히 ​​나에게 꽤 무작위로 보입니다.

여기에 이미지 설명을 입력하십시오

사실, Murmur2가 와 더 나은 임의성가있는 것 Numbers이상을 FNV-1a:

여기에 이미지 설명을 입력하십시오

FNV-1a"숫자"맵을 보면 미묘한 수직 패턴이 보인다고 생각 합니다. Murmur를 사용하면 패턴이 전혀 보이지 않습니다. 어떻게 생각해?


*표의 여분 은 무작위성이 얼마나 나쁜지를 나타냅니다. 으로 FNV-1a최선을 것을, 그리고 DJB2x최악 인 :

      Murmur2: .
       FNV-1a: .
        FNV-1: ▪
         DJB2: ▪▪
        DJB2a: ▪▪
         SDBM: ▪▪▪
SuperFastHash: .
          CRC: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
     Loselose: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
                                        ▪
                                 ▪▪▪▪▪▪▪▪▪▪▪▪▪
                        ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
          ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪

나는 원래 충돌에 대해 걱정 해야하는지 결정하기 위해이 프로그램을 작성했습니다 .

그런 다음 해시 함수가 충분히 무작위인지 확인했습니다.

FNV-1a 알고리즘

FNV1 해시는 32, 64, 128, 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_basisFNV_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-1a보다 FNV-1이 더 좋습니까?

아닙니다. FNV-1a가 더 좋습니다. 영어 단어 corpus를 사용할 때 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는 FN-1보다 "400 %" 나쁘지 않고 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 홈페이지 :

(1)-SuperFastHash는 충돌 특성이 매우 좋지 않으며 다른 곳에 기록되어 있습니다.

그래서 나는 단지 나만이 아니라고 생각합니다.

업데이트 :Murmur다른 것보다 빠른지 깨달았습니다 . MurmurHash2는 한 번에 4 바이트에서 작동합니다. 대부분의 알고리즘은 바이트 단위입니다 .

for each octet in Key
   AddTheOctetToTheHash

이것은 키가 길어질수록 Murmur가 빛을 발할 수 있음을 의미합니다.


최신 정보

GUID는 무작위가 아닌 고유하도록 설계되었습니다.

Raymond Chen의 적시 게시물은 "무작위" GUID가 임의성으로 사용되지 않는다는 사실을 반복 합니다. 그것들 또는 그것들의 서브셋은 해시 키로는 적합하지 않습니다 :

알고리즘이 난수 생성기의 품질을 지정하지 않기 때문에 버전 4 GUID 알고리즘조차도 예측할 수없는 것은 아닙니다. GUID에 대한 Wikipedia 기사에는 생성기가 암호화 적으로 강력하지 않기 때문에 난수 생성기 상태에 대한 지식을 기반으로 미래 및 이전 GUID를 예측할 수 있음시사 하는 기본 연구가 포함되어 있습니다 .

Randomess는 충돌 방지와는 다릅니다. "무작위"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);
}

참고 : "랜덤 GUID"GUID 의 "랜덤"변형이므로 따옴표로 묶습니다. 보다 정확한 설명은 다음과 같습니다 Type 4 UUID. 그러나 아무도 유형 4 또는 유형 1, 3 및 5가 무엇인지 모릅니다. 따라서 "무작위"GUID라고 부르는 것이 더 쉽습니다.

모든 영어 단어 거울


41
SHA가 비교하는 방법을 보는 것은 정말 흥미로울 것입니다. 해시 알고리즘의 좋은 후보이기 때문에가 아니라 속도 알고리즘을 위해 만든 암호 해시와 암호화 해시가 어떻게 비교되는지 보는 것이 흥미로울 것입니다.
Michael

8
Yann Collet의 'xxHash'라는 새로운 해시가 최근에 진행되었습니다. 나는 항상 새로운 해시를 의심합니다. 비교해 보면 흥미로울 것입니다 (추가 된 것으로 들었던 임의의 해시를 제안하는 사람들이 지치지 않는다면 ...)
th_in_gs

7
과연. xxHash 프로젝트 페이지에서 발표 한 성능 수치는 인상적입니다. 적어도 오픈 소스 프로젝트입니다 : code.google.com/p/xxhash
ATTracker

9
안녕하십니까, SuperFastHash의 Delphi 구현이 정확합니다. 구현할 때 C와 Delphi에서 테스트 결과를 만들어 구현 결과와 참조 구현을 비교했습니다. 차이점은 없습니다. (나는 또한 MurmurHash 구현 게시 된 이유입니다 그래서 당신이 보는 것은 해시의 실제 불량은 ...입니다 landman-code.blogspot.nl/2009/02/... )
데비 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 Perfect hashing은 일반적으로 100 개 미만의 키와 함께 사용되지만 cmph.sourceforge.net을 살펴 보십시오 ... 여전히 범위가 너무 짧습니다.
Jim Balter

1
@DavidCary 귀하의 링크에서 귀하의 주장을지지하는 것은 없습니다. 아마도 당신은 "충돌 없음"과 O (1)을 혼동했지만, 그것들은 전혀 같지 않습니다. 물론 완벽한 해싱은 충돌을 보장하지 않지만 모든 키를 미리 알고 있어야하며 비교적 적은 수의 키가 있어야합니다. (그러나 위의 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는 대부분의 간단한 해시 함수이므로 0에 민감하므로 이러한 해시를 쉽게 중단 할 수 있습니다. 그것은 대부분의 smhasher 품질 테스트에 나누기, 나쁜 편견 너무 많은 충돌과 나쁜 분포 : 참조 github.com/rurban/smhasher/blob/master/doc/bernstein 그의 CDB 데이터베이스를 사용하지만 나는 그것을 사용하지 것이다 공개 액세스.
rurban

2
DJB는 성능 및 배포 관점에서 상당히 나쁩니다. 나는 오늘 그것을 사용하지 않을 것입니다.
콘래드 마이어

@ConradMeyer 내 생각에 DJB는 이 질문 과 같이 세 가지 요소로 가속화 될 수 있으며 아마도 가장 유용한 알고리즘을 능가 할 것입니다. 배포와 관련하여 동의합니다. 두 문자 문자열에서도 충돌을 일으키는 해시는 실제로 좋지 않습니다.
maaartinus

28

Google의 CityHash는 찾고있는 알고리즘입니다. 암호화에는 좋지 않지만 고유 한 해시 생성에는 좋습니다.

자세한 내용 은 블로그 를 읽고 코드는 여기에서 확인할 수 있습니다 .

CityHash는 C ++로 작성되었습니다. 또한이 일반 C 포트 .

약 32 비트 지원 :

모든 CityHash 기능은 64 비트 프로세서에 맞게 조정되었습니다. 즉, 32 비트 코드에서 SSE4.2를 사용하는 새로운 것을 제외하고는 실행됩니다. 그들은 그렇게 빠르지 않을 것입니다. 32 비트 코드에서 Murmur 또는 다른 것을 사용할 수 있습니다.


11
CityHash는 "City Sushi"와 비슷합니까?
Eric

2
SipHash도 살펴보십시오. MurmurHash / CityHash 등을 대체해야합니다. : 131002.net/siphash
Török Edwin

3
CitHash의 후속 제품인 FarmHash도 참조하십시오. code.google.com/p/farmhash
stevendaniels

7
xxHashCityHash 보다 5 배 빠르다고 주장합니다.
클레이 브리지

plain C port링크가 깨졌습니다
makerj

20

파일을 해싱 할 때 다른 해싱 알고리즘의 짧은 속도 비교를 그렸습니다.

모든 플롯은 tmpfs에 저장 되었기 때문에 개별 플롯은 판독 방법이 약간 다르므로 여기서 무시해도됩니다. 따라서 궁금한 점이 있다면 벤치마킹이 IO 바운드가 아닙니다.

알고리즘은 다음과 같습니다 SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}..

결론 :

  • Murmur3, Cityhash 및 Spooky와 같은 비 암호화 해시 기능은 서로 매우 가깝습니다. Cityhash는 CPU에없는 SSE 4.2s CRC명령 으로 CPU에서 더 빠를 수 있습니다 . SpookyHash는 필자의 경우 CityHash보다 항상 약간 앞서있었습니다.
  • SHA256이 MD5 및 SHA1 의 충돌 취약성 에 대해 더 안전 할 수 있지만 MD5는 암호화 해시 함수를 사용할 때 좋은 절충점 인 것 같습니다 .
  • 모든 알고리즘의 복잡성은 선형 적이며 블록 단위로 작동하기 때문에 놀랍지 않습니다. (읽기 방법이 차이가 있는지 확인하고 싶기 때문에 가장 오른쪽 값을 비교할 수 있습니다).
  • SHA256은 SHA512보다 느 렸습니다.
  • 해시 함수의 무작위성을 조사하지 않았습니다. 그러나 여기 에서 누락 된 해시 함수의 좋은 비교입니다 이안 Boyds의 대답은 . 이것은 CityHash가 코너 케이스에 약간의 문제가 있음을 지적합니다.

플롯에 사용 된 소스 :


1
선형 스케일 그래프는 플로팅 할 수량을 나타내는 y 축 레이블을 잘라냅니다. 아마도 로그 스케일과 같은 "초 단위의 시간"일 것입니다. 고칠 가치가 있습니다.
Craig McQueen

18

(SHA-256 포함) 알고리즘은 SHA되는 설계빠르고 .

사실, 그들의 속도는 때때로 문제가 될 수 있습니다. 특히, 암호 파생 토큰을 저장하는 일반적인 기술은 표준 빠른 해시 알고리즘을 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는 단지 해시 테이블에 값을 저장하려고하지만 암호화 해시 함수가 실제로 적절하지 않다고 생각합니다.
Dean Harding

6
이 문제는 암호화 해시 함수의 주제를 제기했습니다 (이제 등장 함). 그것은 내가 응답하는 비트입니다.
yfeldblum

15
사람들에게 "특히 암호 파생 토큰을 저장하는 일반적인 기술은 표준 빠른 해시 알고리즘을 10,000 번 실행하는 것"이라는 아이디어를 떠올리게하는 것입니다. 이러한 시나리오를 위해 설계된 알고리즘이 있습니다 (예 :) bcrypt. 올바른 도구를 사용하십시오.
TC1

3
암호화 해시는 높은 처리량을 갖도록 설계되었지만 종종 높은 설정, 분해 .rodata및 / 또는 상태 비용을 의미합니다. 해시 테이블에 대한 알고리즘을 원할 때 일반적으로 매우 짧은 키와 많은 키가 있지만 암호화 기능에 대한 추가 보증이 필요하지 않습니다. 나는 한 번에 하나씩 조정 된 Jenkins를 사용합니다.
mirabilos

1
@ChrisMorgan : 암호화 보안 해시를 사용하는 대신 해시 무작위 화를 사용하여 HashTable DoS를 훨씬 효율적으로 해결할 수 있으므로 모든 프로그램 실행 또는 모든 해시 테이블에서 데이터가 매번 동일한 버킷으로 그룹화되지 않습니다. .
Lie Ryan

14

나는 SHA-256 및 같은 것들이 알고 있지만, 이러한 알고리즘이되는 설계안전한 보통은 작은 알고리즘보다 느린 것을 의미하는 독특한 .

암호화 해시 함수가 더 독창적이라는 가정은 잘못이며 실제로 실제로는 종종 거꾸로 표시 될 수 있습니다. 사실 :

  1. 암호화 해시 함수는 이상적으로 무작위구별 할 수 없어야합니다 .
  2. 그러나 비 암호 해시 함수를 사용하면 가능한 입력과 호의적으로 상호 작용 하는 것이 바람직합니다 .

이는 비 암호 해시 함수 가 설계된 "좋은"데이터 세트에 대한 암호화 함수 보다 충돌적을 수 있음을 의미합니다 .

실제로 Ian Boyd의 답변과 약간의 수학 : 생일 문제로 데이터를 보여줄 수 있습니다 . n세트에서 무작위로 정수 를 선택할 경우 예상되는 충돌 쌍 수에 대한 공식 [1, d]은 다음과 같습니다 (Wikipedia에서 가져옴).

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

연결 n= 216553와 d= 2 ^ 32 우리는 약 얻을 5.5 예상 충돌을 . Ian의 테스트는 대부분 그 주변에 대한 결과를 보여 주지만 한 가지 예외를 제외하면 대부분의 함수는 연속적인 숫자 테스트에서 충돌전혀 없습니다 . 무작위로 216,553 개의 32 비트 숫자를 선택하고 제로 충돌이 발생할 확률은 약 0.43 %입니다. 그리고 그것은 하나의 함수만을위한 것입니다. 여기서 우리는 충돌 이없는 5 개의 고유 한 해시 함수 패밀리를 가지고 있습니다!

여기서 우리가보고있는 것은 Ian이 테스트 한 해시가 연속적인 숫자 데이터 셋과 호의적 으로 상호 작용한다는 것입니다. 즉, 이상적인 암호화 해시 함수보다 최소로 다른 입력을 더 널리 분산 시킵니다. (Side note : 이것은 숫자 데이터 세트에서 FNV-1a와 MurmurHash2가 "임의로 보인다"는 Ian의 그래픽 평가는 자신의 데이터에서 반박 할 수 있음을 의미 합니다. 해시 함수, 엄청나게 비 랜덤입니다!)

이것은 많은 해시 함수 사용에 바람직한 동작이기 때문에 놀라운 일이 아닙니다. 예를 들어, 해시 테이블 키는 종종 매우 유사합니다. Ian의 답변 은 MSN이 한 번 ZIP 코드 해시 테이블과 관련하여 발생했던 문제를 언급 합니다 . 이것은 가능한 입력 에 대한 충돌 회피 가 임의의 유사 동작보다 우월한 사용 입니다.

여기서 또 다른 유익한 비교는 CRC와 암호화 해시 함수 간의 디자인 목표의 대비입니다.

  • CRC는 잡음이 많은 통신 채널로 인한 오류 를 포착하도록 설계되었으며 ,이 비트는 적은 수의 비트 플립 일 수 있습니다.
  • 암호화 해시는 제한된 계산 리소스가 할당되었지만 임의로 많은 영리함을 가진 악의적 인 공격자가 수정 한 내용 을 포착하도록 설계되었습니다 .

따라서 CRC의 경우 최소한 다른 입력에서 무작위보다 충돌이 적은 것이 좋습니다 . 암호화 해시를 사용하면 이것이 아닙니다!


10

SipHash를 사용하십시오 . 그것은 많은 바람직한 특성을 가지고 있습니다 :

  • 빠른. 최적화 된 구현은 바이트 당 약 1주기가 걸립니다.

  • 안전한. SipHash는 강력한 PRF (의사 난수 기능)입니다. 즉, 128 비트 비밀 키를 모르는 경우 임의의 기능과 구분할 수 없습니다. 금후:

    • 충돌로 인해 해시 테이블 프로브가 선형 시간이되는 것에 대해 걱정할 필요가 없습니다. SipHash, 당신은 알고 에 관계없이 입력의 평균 평균의 경우 성능을 얻을 것이다.

    • 해시 기반 서비스 거부 공격에 대한 내성.

    • SipHash (특히 128 비트 출력 버전)를 MAC (Message Authentication Code)으로 사용할 수 있습니다. 메시지와 SipHash 태그를 받고 태그가 비밀 키를 사용하여 SipHash를 실행하는 것과 동일한 경우 해시를 만든 사람도 비밀 키를 소유하고 있으며 메시지와 이후 해시가 변경되었습니다.


1
보안이 필요하지 않으면 SipHash가 과도하지 않습니까? 영광스러운 해시 시드 인 128 비트 키가 필요합니다. 물론 MurmurHash3에는 128 비트 출력이 있고 SipHash에는 64 비트 출력 만 있습니다. 분명히 다이제스트가 클수록 충돌 가능성이 낮습니다.
bryc

@bryc 차이점은 SipHash가 악의적 인 입력에서도 계속 잘 작동한다는 것입니다. SipHash를 기반으로하는 해시 테이블은 잠재적으로 적대적인 소스의 데이터에 사용될 수 있으며 해시 함수의 세부 사항에 매우 민감한 선형 프로빙과 같은 알고리즘을 사용할 수 있습니다.
데미

9

해시하는 데이터에 따라 다릅니다. 일부 해싱은 텍스트와 같은 특정 데이터에서 더 잘 작동합니다. 일부 해싱 알고리즘은 특정 데이터에 적합하도록 구체적으로 설계되었습니다.

Paul Hsieh는 한때 빠른 해시를 만들었습니다 . 그는 소스 코드와 설명을 나열합니다. 그러나 이미 이겼습니다. :)


6

Java는 간단한 곱하기 및 추가 알고리즘을 사용합니다.

String 객체의 해시 코드는 다음과 같이 계산됩니다.

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

int 산술을 사용 하면 문자열 s[i]i 번째 문자는 문자열 n의 길이이며 ^지수를 나타냅니다. 빈 문자열의 해시 값은 0입니다.

아마도 훨씬 더 좋은 것들이 있지만 이것은 상당히 널리 퍼져 있으며 속도와 독창성 사이의 좋은 절충안 인 것 같습니다.


12
여기에서 사용되는 것과 똑같은 것을 사용하지 않을 것입니다. 여전히 이것과 충돌하기가 비교적 쉽습니다. 그것은이다 확실히 끔찍한 아니지만, 더 나은 사람이 거기에있다. Java와 호환되어야 할 중요한 이유가 없으면 선택 하지 않아야 합니다.
Joachim Sauer

4
어떤 이유로 든 해싱 방법을 여전히 선택한다면 적어도 92821과 같은 더 나은 소수를 곱셈기로 사용할 수 있습니다. 충돌이 크게 줄어 듭니다. stackoverflow.com/a/2816747/21499
Hans-Peter Störr

1
대신 FNV1a를 사용할 수도 있습니다. 또한 간단한 곱셈 기반 해시이지만 더 큰 배율을 사용하여 해시를 더 잘 분산시킵니다.
bryc

4

우선 왜 자신 만의 해싱을 구현해야합니까? 대부분의 작업에서는 구현이 가능하다고 가정 할 때 (자신의 교육을 위해이 작업을 수행하지 않는 한) 표준 라이브러리의 데이터 구조로 좋은 결과를 얻을 수 있습니다.

실제 해싱 알고리즘이 진행되는 한 개인적으로 가장 좋아하는 것은 FNV입니다. 1

다음은 C에서 32 비트 버전의 구현 예입니다.

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 변종은 무작위성으로 약간 우수합니다. *and ^: h = (h * 16777619) ^ p[i]==> 의 순서 h = (h ^ p[i]) * 16777619
Ian Boyd
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.