[c] 정수 해시 키를 허용하는 정수 해시 함수는 무엇입니까?
정수 해시 키를 허용하는 정수 해시 함수는 무엇입니까?
답변
Knuth의 곱셈 방법 :
hash(i)=i*2654435761 mod 2^32
일반적으로 해시 크기 ( 2^32
예제에서) 의 순서이고 공통 요인이없는 승수를 선택해야 합니다. 이렇게하면 해시 함수가 모든 해시 공간을 균일하게 처리합니다.
편집 :이 해시 함수의 가장 큰 단점은 분할 가능성을 유지한다는 것입니다. 따라서 정수가 모두 2 또는 4로 나눌 수있는 경우 (흔하지 않은 경우) 해시도 마찬가지입니다. 이것은 해시 테이블의 문제입니다. 사용되는 버킷의 1/2 또는 1/4 만 사용하면됩니다.
답변
다음 알고리즘이 매우 좋은 통계 분포를 제공한다는 것을 알았습니다. 각 입력 비트는 약 50 % 확률로 각 출력 비트에 영향을줍니다. 충돌이 없습니다 (각 입력이 다른 출력을 생성 함). 알고리즘은 CPU에 내장 정수 곱셈 단위가없는 경우를 제외하고는 빠릅니다. C 코드는 가정 int
32 비트 (자바 대체이다 >>
으로 >>>
및 삭제 unsigned
)
unsigned int hash(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
매직 넘버는 여러 시간 동안 실행 된 특수 멀티 스레드 테스트 프로그램 을 사용하여 계산되었으며 , 눈사태 효과 (단일 입력 비트가 변경되면 변경되는 출력 비트 수, 평균 거의 16이어야 함), 독립성을 계산합니다. 출력 비트 변경 (출력 비트가 서로 의존해서는 안 됨) 및 입력 비트가 변경 될 경우 각 출력 비트가 변경 될 확률. 계산 된 값은 MurmurHash 에서 사용하는 32 비트 파이널 라이저보다 낫고 AES 를 사용할 때와 거의 비슷 합니다. 약간의 장점은 동일한 상수가 두 번 사용된다는 것입니다 (마지막으로 테스트했을 때 약간 더 빨라졌지만 여전히 사실인지 확실하지 않습니다).
당신은 당신이 대체하는 경우 (해시에서 입력 값을 얻을) 과정을 되돌릴 수 0x45d9f3b
와 함께 0x119de1f3
합니다 ( 역수 ) :
unsigned int unhash(unsigned int x) {
x = ((x >> 16) ^ x) * 0x119de1f3;
x = ((x >> 16) ^ x) * 0x119de1f3;
x = (x >> 16) ^ x;
return x;
}
64 비트 숫자의 경우 가장 빠르지 않을 수도 있지만 다음을 사용하는 것이 좋습니다. 이것은 블로그 기사 Better Bit Mixing (mix 13)을 기반으로 한 것으로 보이는 splitmix64 기반입니다 .
uint64_t hash(uint64_t x) {
x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
자바, 사용을 위해 long
추가, L
교체, 상수를 >>
함께 >>>
제거합니다 unsigned
. 이 경우 반전은 더 복잡합니다.
uint64_t unhash(uint64_t x) {
x = (x ^ (x >> 31) ^ (x >> 62)) * UINT64_C(0x319642b2d24d8ec3);
x = (x ^ (x >> 27) ^ (x >> 54)) * UINT64_C(0x96de1b173f119089);
x = x ^ (x >> 30) ^ (x >> 60);
return x;
}
업데이트 : 다른 (아마도 더 나은) 상수가 나열 되는 Hash Function Prospector 프로젝트 를 살펴볼 수도 있습니다.
답변
데이터가 배포되는 방식에 따라 다릅니다. 간단한 카운터의 경우 가장 간단한 기능
f(i) = i
좋을 것입니다 (최적이라고 생각하지만 증명할 수는 없습니다).
답변
빠르고 좋은 해시 함수는 다음과 같이 품질이 낮은 빠른 순열로 구성 될 수 있습니다.
- 고르지 않은 정수로 곱하기
- 이진 회전
- xorshift
난수 생성을 위해 PCG 로 입증 된 것과 같이 우수한 품질의 해싱 함수를 생성합니다.
이것은 사실 rrxmrrxmsx_0과 murmur hash가 고의로 또는 무의식적으로 사용하는 레시피이기도합니다.
나는 개인적으로
uint64_t xorshift(const uint64_t& n,int i){
return n^(n>>i);
}
uint64_t hash(const uint64_t& n){
uint64_t p = 0x5555555555555555ull; // pattern of alternating 0 and 1
uint64_t c = 17316035218449499591ull;// random uneven integer constant;
return c*xorshift(p*xorshift(n,32),32);
}
충분히 좋다.
좋은 해시 함수는
- 가능하면 정보를 잃어 버리지 않기 위해 투사 적이어야하며 충돌을 최소화합니다.
- 가능한 한 많이 그리고 균등하게 캐스케이드합니다. 즉, 각 입력 비트는 확률 0.5로 모든 출력 비트를 뒤집어 야합니다.
먼저 identity 함수를 살펴 보겠습니다. 1은 충족하지만 2는 충족하지 않습니다. :
입력 비트 n은 100 % (빨간색)의 상관 관계로 출력 비트 n을 결정하고 나머지는 없음이므로 파란색이므로 완벽한 빨간색 선을 제공합니다.
xorshift (n, 32)는 그다지 좋지 않으며 한 줄 반을 산출합니다. 두 번째 응용 프로그램으로 뒤집을 수 있기 때문에 여전히 1. 만족합니다.
부호없는 정수를 사용한 곱셈이 훨씬 더 낫습니다. 더 강하게 계단식으로 연결되고 더 많은 출력 비트를 녹색으로 원하는 0.5 확률로 뒤집습니다. 그것은 1을 만족합니다. 각 고르지 않은 정수에 대해 곱셈 역이 있습니다.
두 가지를 결합하면 다음과 같은 결과가 나옵니다. 두 개의 bijective 함수의 구성이 다른 bijective 함수를 생성하므로 여전히 1을 만족합니다.
곱셈과 xorshift를 두 번째로 적용하면 다음이 생성됩니다.
또는 GHash 와 같은 Galois 필드 곱셈을 사용할 수 있습니다 . 최신 CPU에서 상당히 빨라 졌으며 한 단계에서 우수한 품질을 제공합니다.
uint64_t const inline gfmul(const uint64_t& i,const uint64_t& j){
__m128i I{};I[0]^=i;
__m128i J{};J[0]^=j;
__m128i M{};M[0]^=0xb000000000000000ull;
__m128i X = _mm_clmulepi64_si128(I,J,0);
__m128i A = _mm_clmulepi64_si128(X,M,0);
__m128i B = _mm_clmulepi64_si128(A,M,0);
return A[0]^A[1]^B[1]^X[0]^X[1];
}
답변
이 페이지 에는 일반적으로 괜찮은 경향이있는 간단한 해시 함수가 나열되어 있지만 모든 간단한 해시는 제대로 작동하지 않는 병리학적인 경우가 있습니다.
답변
-
32 비트 곱셈 방법 (매우 빠름) @rafal 참조
#define hash32(x) ((x)*2654435761) #define H_BITS 24 // Hashtable size #define H_SHIFT (32-H_BITS) unsigned hashtab[1<<H_BITS] .... unsigned slot = hash32(x) >> H_SHIFT
-
32 비트 및 64 비트 (좋은 배포) : MurmurHash
- 정수 해시 함수
답변
Eternally Confuzzled의 일부 해시 알고리즘에 대한 멋진 개요가 있습니다 . 눈사태에 빠르게 도달하므로 효율적인 해시 테이블 조회에 사용할 수있는 Bob Jenkins의 한 번에 하나씩 해시를 권장합니다.