고유성과 속도에 가장 적합한 해싱 알고리즘은 무엇입니까? 예 (양호)에는 해시 사전 포함이 사용됩니다.
내가 좋아하는 일이 알고 SHA-256 등이 있지만, 이러한 알고리즘이되는 설계 로 안전한 보통은 작은 알고리즘보다 느린 것을 의미하는 독특한 . 빠른 해시 알고리즘을 원하지만 충돌을 피하기 위해 상당히 독창적입니다.
고유성과 속도에 가장 적합한 해싱 알고리즘은 무엇입니까? 예 (양호)에는 해시 사전 포함이 사용됩니다.
내가 좋아하는 일이 알고 SHA-256 등이 있지만, 이러한 알고리즘이되는 설계 로 안전한 보통은 작은 알고리즘보다 느린 것을 의미하는 독특한 . 빠른 해시 알고리즘을 원하지만 충돌을 피하기 위해 상당히 독창적입니다.
답변:
속도와 충돌 횟수를 측정하는 몇 가지 알고리즘을 테스트했습니다.
세 가지 다른 키 세트를 사용했습니다.
"1"
에 "216553"
(ZIP 코드를 생각하고, 가난한 해시 아래로 걸린 msn.com )각 모음에 대해 충돌 횟수와 평균 해싱 소요 시간이 기록되었습니다.
나는 테스트했다 :
각 결과에는 평균 해시 시간과 충돌 횟수가 포함됩니다
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
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: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪
▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
나는 원래 충돌에 대해 걱정 해야하는지 결정하기 위해이 프로그램을 작성했습니다 .
그런 다음 해시 함수가 충분히 무작위인지 확인했습니다.
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_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-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 % 만 나빠집니다.
더 중요한 점은 충돌과 관련하여 두 가지 클래스의 알고리즘이 있다는 것입니다.
그리고 해시가 얼마나 고르게 분포되어 있는지가 있습니다 :
최신 정보
어렴풋한 말소리? 물론이지
최신 정보
@whatshisname은 CRC32의 성능이 궁금해 표에 숫자를 추가했습니다.
CRC32는 꽤 좋습니다 . 충돌이 적지 만 속도가 느리고 1k 조회 테이블의 오버 헤드가 발생합니다.
CRC 배포에 관한 모든 잘못된 것을 싹둑 거리다
오늘까지 나는 사실상의 해시 테이블 해싱 알고리즘 으로 FNV-1a를 사용하려고했습니다 . 그러나 이제 Murmur2로 전환하고 있습니다.
그리고 난 정말, 정말 에 문제가있을 희망 SuperFastHash
내가 찾은 알고리즘 ; 인기만큼 나쁘다.
업데이트 : 에서 구글에 MurmurHash3 홈페이지 :
(1)-SuperFastHash는 충돌 특성이 매우 좋지 않으며 다른 곳에 기록되어 있습니다.
그래서 나는 단지 나만이 아니라고 생각합니다.
업데이트 : 왜 Murmur
다른 것보다 빠른지 깨달았습니다 . MurmurHash2는 한 번에 4 바이트에서 작동합니다. 대부분의 알고리즘은 바이트 단위입니다 .
for each octet in Key
AddTheOctetToTheHash
이것은 키가 길어질수록 Murmur가 빛을 발할 수 있음을 의미합니다.
최신 정보
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라고 부르는 것이 더 쉽습니다.
변경되지 않은 사전에서 해시 맵을 작성하려는 경우 완벽한 해싱 https://en.wikipedia.org/wiki/Perfect_hash_function 을 고려할 수 있습니다. 주어진 데이터 세트에 대해 충돌이 없습니다.
다음 은 해시 함수 목록이지만 짧은 버전은 다음과 같습니다.
좋은 해시 함수를 원하고 기다릴 수 없다면
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;
}
Google의 CityHash는 찾고있는 알고리즘입니다. 암호화에는 좋지 않지만 고유 한 해시 생성에는 좋습니다.
자세한 내용 은 블로그 를 읽고 코드는 여기에서 확인할 수 있습니다 .
CityHash는 C ++로 작성되었습니다. 또한이 일반 C 포트 .
모든 CityHash 기능은 64 비트 프로세서에 맞게 조정되었습니다. 즉, 32 비트 코드에서 SSE4.2를 사용하는 새로운 것을 제외하고는 실행됩니다. 그들은 그렇게 빠르지 않을 것입니다. 32 비트 코드에서 Murmur 또는 다른 것을 사용할 수 있습니다.
plain C port
링크가 깨졌습니다
파일을 해싱 할 때 다른 해싱 알고리즘의 짧은 속도 비교를 그렸습니다.
모든 플롯은 tmpfs에 저장 되었기 때문에 개별 플롯은 판독 방법이 약간 다르므로 여기서 무시해도됩니다. 따라서 궁금한 점이 있다면 벤치마킹이 IO 바운드가 아닙니다.
알고리즘은 다음과 같습니다 SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}
..
결론 :
CRC
명령 으로 CPU에서 더 빠를 수 있습니다 . SpookyHash는 필자의 경우 CityHash보다 항상 약간 앞서있었습니다.플롯에 사용 된 소스 :
(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)
bcrypt
. 올바른 도구를 사용하십시오.
.rodata
및 / 또는 상태 비용을 의미합니다. 해시 테이블에 대한 알고리즘을 원할 때 일반적으로 매우 짧은 키와 많은 키가 있지만 암호화 기능에 대한 추가 보증이 필요하지 않습니다. 나는 한 번에 하나씩 조정 된 Jenkins를 사용합니다.
나는 SHA-256 및 같은 것들이 알고 있지만, 이러한 알고리즘이되는 설계 로 안전한 보통은 작은 알고리즘보다 느린 것을 의미하는 독특한 .
암호화 해시 함수가 더 독창적이라는 가정은 잘못이며 실제로 실제로는 종종 거꾸로 표시 될 수 있습니다. 사실 :
이는 비 암호 해시 함수 가 설계된 "좋은"데이터 세트에 대한 암호화 함수 보다 충돌 이 적을 수 있음을 의미합니다 .
실제로 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의 경우 최소한 다른 입력에서 무작위보다 충돌이 적은 것이 좋습니다 . 암호화 해시를 사용하면 이것이 아닙니다!
SipHash를 사용하십시오 . 그것은 많은 바람직한 특성을 가지고 있습니다 :
빠른. 최적화 된 구현은 바이트 당 약 1주기가 걸립니다.
안전한. SipHash는 강력한 PRF (의사 난수 기능)입니다. 즉, 128 비트 비밀 키를 모르는 경우 임의의 기능과 구분할 수 없습니다. 금후:
충돌로 인해 해시 테이블 프로브가 선형 시간이되는 것에 대해 걱정할 필요가 없습니다. SipHash, 당신은 알고 에 관계없이 입력의 평균 평균의 경우 성능을 얻을 것이다.
해시 기반 서비스 거부 공격에 대한 내성.
SipHash (특히 128 비트 출력 버전)를 MAC (Message Authentication Code)으로 사용할 수 있습니다. 메시지와 SipHash 태그를 받고 태그가 비밀 키를 사용하여 SipHash를 실행하는 것과 동일한 경우 해시를 만든 사람도 비밀 키를 소유하고 있으며 메시지와 이후 해시가 변경되었습니다.
해시하는 데이터에 따라 다릅니다. 일부 해싱은 텍스트와 같은 특정 데이터에서 더 잘 작동합니다. 일부 해싱 알고리즘은 특정 데이터에 적합하도록 구체적으로 설계되었습니다.
Paul Hsieh는 한때 빠른 해시를 만들었습니다 . 그는 소스 코드와 설명을 나열합니다. 그러나 이미 이겼습니다. :)
Java는 이 간단한 곱하기 및 추가 알고리즘을 사용합니다.
String 객체의 해시 코드는 다음과 같이 계산됩니다.
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
int 산술을 사용 하면 문자열
s[i]
의 i 번째 문자는 문자열n
의 길이이며^
지수를 나타냅니다. 빈 문자열의 해시 값은 0입니다.
아마도 훨씬 더 좋은 것들이 있지만 이것은 상당히 널리 퍼져 있으며 속도와 독창성 사이의 좋은 절충안 인 것 같습니다.
우선 왜 자신 만의 해싱을 구현해야합니까? 대부분의 작업에서는 구현이 가능하다고 가정 할 때 (자신의 교육을 위해이 작업을 수행하지 않는 한) 표준 라이브러리의 데이터 구조로 좋은 결과를 얻을 수 있습니다.
실제 해싱 알고리즘이 진행되는 한 개인적으로 가장 좋아하는 것은 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;
}
*
and ^
: h = (h * 16777619) ^ p[i]
==> 의 순서 h = (h ^ p[i]) * 16777619