해시 함수가 소수 모듈을 사용해야하는 이유는 무엇입니까?


335

오래 전에 저는 거래 테이블에서 1.25 달러에 데이터 구조 책을 구입했습니다. 그것에서, 해싱 함수에 대한 설명은 "수학의 본질"때문에 궁극적으로 소수로 수정되어야한다고 말했다.

1.25 달러 책에서 무엇을 기대하십니까?

어쨌든, 나는 수학의 본질을 생각하기 위해 수년을 보냈지 만 여전히 그것을 이해할 수는 없습니다.

소수의 버킷이 있어도 숫자 분포가 더 많은가? 아니면 다른 사람들 이 그것을 받아들이 기 때문에 모두 받아들이 는 오래된 프로그래머의 이야기 입니까?


1
완벽하게 합리적인 질문 : 왜 소수의 버킷이 있어야합니까?
Draemon

1
이 질문은 컴퓨터 과학에 속하기 때문에 주제가 아닌 것으로 보입니다 .
궤도에서 가벼운 레이스

2
cs.stackexchange.com/a/64191/64222 또 다른 잘 설명 된 설명.
Green Tree


다음은 놀라운 증거가있는 다소 관련된 질문에 대한 또 다른 훌륭한 설명입니다 -quora.com/…
AnBisw

답변:


242

일반적으로 간단한 해시 함수는 입력의 "구성 요소 부분"(문자열의 경우 문자)을 취하고 상수의 거듭 제곱을 곱한 다음 정수 유형으로 함께 추가하여 작동합니다. 예를 들어 문자열의 전형적인 (특히 좋지는 않지만) 해시는 다음과 같습니다.

(first char) + k * (second char) + k^2 * (third char) + ...

그런 다음 첫 번째 문자가 모두 같은 문자열 묶음이 공급되면 적어도 정수 유형이 오버플로 될 때까지 결과는 모두 동일한 모듈로 k가됩니다.

[예를 들어, Java의 문자열 hashCode는 이와 매우 유사합니다. k = 31로 문자를 역순으로 수행합니다. 따라서 같은 방식으로 끝나는 문자열간에 모듈로 31과 눈에 띄는 관계를 얻었고, 끝 근처를 제외하고는 같은 문자열 사이에서 모듈로 2 ^ 32의 눈에 띄는 관계를 얻습니다. 해시 테이블 동작을 심각하게 망칠 수는 없습니다.]

해시 테이블은 버킷 수에 대한 해시 계수를 가져와 작동합니다.

충돌이 해시 테이블의 효율성을 감소시키기 때문에 해시 테이블에서는 가능한 경우 충돌을 일으키지 않는 것이 중요합니다.

이제 누군가가 첫 번째 문자가 모두 같은 것처럼 항목 사이에 관계가있는 해시 테이블에 전체 값을 넣었다고 가정 해보십시오. 이것은 상당히 예측 가능한 사용 패턴이므로 너무 많은 충돌을 일으키기를 원하지 않습니다.

해시에 사용 된 상수와 버킷 수가 coprime 이면 "수학의 특성 때문에"수학 적인 경우 충돌이 최소화되는 경우 가 종종 있습니다. 그들이 coprime 이 아닌 경우충돌이 최소화되지 않은 입력 간에는 매우 간단한 관계가 있습니다. 모든 해시는 공통 계수와 동일한 모듈로 나오므로 공통 계수의 모듈로 값을 갖는 버킷의 1 / n 번째에 해당합니다. n 배 많은 충돌이 발생하는데, 여기서 n은 공통 요소입니다. n은 2 이상이므로 상당히 간단한 유스 케이스가 일반적인 경우보다 최소 두 배의 충돌을 생성하는 것은 허용되지 않습니다. 일부 사용자가 배포판을 버킷으로 나누려는 경우 간단한 예측 가능한 사용법이 아니라 이상한 사고가 되길 원합니다.

이제 해시 테이블 구현은 분명히 항목에 넣은 항목을 제어 할 수 없습니다. 그들은 그들이 관련되는 것을 막을 수 없습니다. 따라서 할 일은 상수와 버킷 수를 동일하게 유지하는 것입니다. 이렇게하면 작은 공통 요소와 관련하여 버킷의 계수를 결정하기 위해 "마지막"구성 요소에만 의존하지 않습니다. 내가 아는 한, 그들은 이것을 달성하기 위해 소수가 될 필요는 없으며 단지 coprime입니다.

그러나 해시 함수와 해시 테이블이 독립적으로 작성된 경우 해시 테이블은 해시 함수의 작동 방식을 모릅니다. 작은 요인으로 상수를 사용하고있을 수 있습니다. 운이 좋으면 완전히 다르게 작동하고 비선형적일 수 있습니다. 해시가 충분하면 모든 버킷 수는 괜찮습니다. 그러나 편집증 해시 테이블은 좋은 해시 기능을 가정 할 수 없으므로 소수의 버킷을 사용해야합니다. 마찬가지로 편집증 해시 함수는 큰 소수 상수를 사용해야하므로 누군가 상수와 공통 요소를 갖는 여러 버킷을 사용할 가능성을 줄입니다.

실제로 버킷 수로 2의 거듭 제곱을 사용하는 것이 일반적이라고 생각합니다. 이 방법은 편리하며 적절한 크기의 소수를 검색하거나 미리 선택하지 않아도됩니다. 따라서 승수도 사용하지 않는 해시 함수에 의존합니다. 일반적으로 안전한 가정입니다. 그러나 위와 같은 해시 함수를 기반으로 때때로 해시 동작이 잘못 발생할 수 있으며 주요 버킷 수는 더 도움이 될 수 있습니다.

"모든 것이 가장 중요하다"는 원칙을 고수하는 것은 해시 테이블에 대한 적절한 배포를 위해 충분하지만 필요한 조건은 아는 한입니다. 다른 사람이 동일한 규칙을 따랐다 고 가정 할 필요없이 모든 사람이 상호 운용 할 수 있습니다.

[편집 : 소수의 버킷을 사용해야하는 또 다른보다 전문적인 이유가 있습니다. 선형 프로빙과의 충돌을 처리하는 경우입니다. 그런 다음 해시 코드에서 보폭을 계산하고 해당 보폭이 버킷 수의 요인이되면 시작한 곳으로 돌아 가기 전에 (bucket_count / stride) 프로브 만 수행 할 수 있습니다. 가장 피해야 할 경우는 stride = 0입니다. 물론 특수 사례이어야하지만 작은 정수와 같은 특수 케이스 bucket_count / stride도 피하기 위해 bucket_count를 소수로 만들 수 있습니다. stride가 0이 아니라면 제공됩니다.]


참고로 해시 코드에 대한 인자 k의 현명한 선택에 대한 토론이 여기 있습니다 : stackoverflow.com/q/1835976/21499
Hans-Peter Störr

9
이것은 멋진 답변입니다. 더 자세히 설명해 주시겠습니까? "따라서 같은 방식으로 끝나는 문자열 사이에 모듈러스 31과 눈에 띄는 관계를 얻습니다. 끝 근처를 제외하고 같은 문자열 사이에는 모듈로 2 ^ 32에 해당합니다. 이것은 해시 테이블 동작을 심각하게 엉망으로 만들지 않습니다. " 나는 특히 2 ^ 32 부분을 이해하지 못한다
평범한

2
이것에 대해 더 명확하게하기위한 추가 메모 : "모든 해시가 공통 계수로 공통 요소로 나옵니다."-> 해시 함수 hash = 1st char + 2nd char * k + ...를 고려하면 첫 번째 문자가 같은 문자열을 가져 오면 hash % k는이 문자열에서 동일합니다. M이 해시 테이블의 크기이고 g가 M과 k의 gcd 인 경우 (hash % k) % g는 hash % g (g는 k를 나누기 때문에)와 동일하므로 해시 % g도이 문자열에 대해 동일합니다. 이제 (hash % M) % g를 고려하십시오. 이것은 g를 M으로 나누기 때문에 hash % g와 같습니다. 따라서 (hash % M) % g는이 모든 문자열에 대해 동일합니다.
Quark

1
@DanielMcLaury Joshua Bloch Java의 이유설명 했습니다. 인기있는 두 권의 책 (K & R, Dragon book)에서 권장되었으며 영어 사전에서 충돌이 적은 것으로 잘 수행되었습니다. 빠릅니다 ( Horner의 방법 사용 ). K & R조차도 그것이 어디에서 왔는지 기억하지 못합니다. 유사 함수이다 라빈 지문 으로부터 라빈 - 카프 알고리즘 (1981) 그러나 K & R (1978)보다 앞서있다.
bain

1
@SteveJessop, "끝 근처를 제외하고 같은 문자열 사이에서 눈에 띄는 관계 모듈로 2 ^ 32를 설명 할 수 있습니까?" 감사.
Khanna111

29

해시 테이블에서 삽입 / 검색 할 때 가장 먼저해야 할 일은 주어진 키에 대한 해시 코드를 계산 한 다음 hashCode % table_length를 수행하여 hashCode의 크기로 해시 코드를 트리밍하여 올바른 버킷을 찾는 것입니다. 다음은 아마도 당신이 어딘가에서 읽은 2 가지 '설명'입니다

  1. table_length에 2의 거듭 제곱을 사용하면 (hashCode (key) % 2 ^ n)를 찾는 것이 (hashCode (key) & (2 ^ n -1))만큼 간단하고 빠릅니다. 그러나 주어진 키에 대해 hashCode를 계산하는 기능이 좋지 않으면 몇 가지 해시 버킷에서 많은 키를 클러스터링해야합니다.
  2. 그러나 table_length에 소수를 사용하면 약간 바보 같은 hashCode 함수가 있더라도 계산 된 hashCode가 다른 해시 버킷에 매핑 될 수 있습니다.

그리고 여기 증거가 있습니다.

hashCode 함수가 다른 {x, 2x, 3x, 4x, 5x, 6x ...} 중에서 다음과 같은 해시 코드를 생성한다고 가정하면,이 모든 것이 m 개의 버킷으로 클러스터링됩니다. 여기서 m = table_length / GreatestCommonFactor (table_length, x). (이것을 확인 / 파생하는 것은 사소한 일입니다). 이제 클러스터링을 피하기 위해 다음 중 하나를 수행 할 수 있습니다.

{x, 2x, 3x, 4x, 5x, 6x ...}와 같이 다른 hashCode의 배수 인 hashCode를 너무 많이 생성하지 않도록하십시오. 그러나 hashTable이 있어야하는 경우 다소 어려울 수 있습니다. 수백만 개의 항목. 또는 단순히 GreatestCommonFactor (table_length, x)를 1로 설정하여 즉, x와 table_length를 함께 사용하여 m을 table_length와 동일하게 만듭니다. 그리고 x가 임의의 숫자 일 수 있다면 table_length가 소수인지 확인하십시오.

에서-http: //srinvis.blogspot.com/2006/07/hash-table-lengths-and-prime-numbers.html


11

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/

사진과 함께 매우 명확한 설명.

편집 : 요약으로, 소수를 선택한 소수에 곱하고 모두 더할 때 고유 한 값을 얻을 가능성이 가장 높으므로 소수가 사용됩니다. 예를 들어 문자열이 주어지면 각 문자 값에 소수를 곱한 다음 모두 더하면 해시 값이됩니다.

더 좋은 질문은 왜 정확히 숫자 31입니까?


5
비록 요약이 도움이 될 것이라고 생각하지만, 사이트가 죽은 경우를 대비하여 일부 내용이 여기에 저장됩니다.
토마스 오웬스

2
이 기사는 그 이유를 설명하지는 않지만 "연구자들은 31의 소수를 사용하면 키에 더 나은 분포를 제공하고 충돌이 줄어든다는 사실을 발견했습니다. 아무도 왜 그런지 알지 못합니다 ..." .
theschmitzer

> 더 좋은 질문은 왜 정확히 숫자 31입니까? 왜 숫자 31이 사용되는지를 의미한다면, 당신이 가리키는 기사는 이유를 알려줍니다. 즉, 다중화하기 쉽고 cos 테스트는 그것이 사용하기 가장 좋은 것임을 보여줍니다. 내가 본 다른 인기있는 승수는 33이며 속도 문제가 (최소한 초기에는) 중요한 요소라는 이론에 가중치를 부여합니다. 당신이 의미하는 바에 따르면, 시험에서 더 좋게 만드는 것은 31에 관한 것입니다. 그런 지 모르겠습니다.
sgmoore

정확하게 곱셈기로 사용될 수있는 유일한 이유는 곱하기가 쉽기 때문입니다. (33을 곱셈기로 사용한 것을 보았을 때, 최근 10 년 전이었고 아마도 해싱에 대한 많은 분석이 이루어지기 전에 가능하다는 것을 의미하지는 않습니다).
sgmoore

3
@SteveJessop 31 번 숫자는 CPU에 의해 (x * 32) -1 연산으로 쉽게 최적화되는데, *32이 비트 연산 은 간단한 비트 시프트이거나 더 빠른 주소 스케일 팩터 (예 : lea eax,eax*8; leax, eax,eax*4x86 / x64)입니다. 그래서 *31소수의 곱셈에 대한 좋은 후보입니다. 이것은 몇 년 전 거의 사실이었습니다. 이제 최신 CPU 아키텍처는 거의 즉각적인 곱셈을 갖습니다. 나눗셈은 항상 느립니다 ...
Arnaud Bouchez

10

tl; dr

index[hash(input)%2]가능한 모든 해시의 절반과 값 범위에 충돌이 발생합니다. index[hash(input)%prime]가능한 모든 해시의 <2의 충돌이 발생합니다. 제수를 테이블 크기로 고정하면 숫자가 테이블보다 클 수 없습니다.


1
2는 소수입니다
Ganesh Chowdhary Sadanala

8

다항식 모듈로 P를 사용하는 일반적인 해시 함수에 고유 한 값을 얻을 가능성이 높으므로 프라임이 사용됩니다. 예를 들어 길이 <= N의 문자열에 이러한 해시 함수를 사용하면 충돌이 발생합니다. 이것은 2 개의 다른 다항식이 동일한 값의 모듈로 P를 생성한다는 것을 의미합니다.이 다항식의 차이는 다시 같은 정도 N (또는 그 이하)의 다항식입니다. 그것은 N 뿌리를 넘지 않습니다 (이 주장은 필드 => 소수에 대한 다항식에 대해서만 참이므로 수학의 본질 자체입니다). 따라서 N이 P보다 훨씬 작 으면 충돌이 없을 것입니다. 그 후, 실험은 37이 길이가 5-10 인 문자열의 해시 테이블에 대한 충돌을 피하기에 충분히 크며 계산에 사용하기에 충분히 작음을 보여줄 수 있습니다.


1
설명은 이제 명백해 보이지만 A.Shen "프로그래밍 : 정리 및 문제"(러시아어)의 책을 읽은 후 나에게 도달했습니다. Rabin 알고리즘에 대한 토론을 참조하십시오. 영어 번역이 있는지 확실하지 않습니다.
TT_

5

다른 관점을 제공하기 위해이 사이트가 있습니다.

http://www.codexon.com/posts/hash-functions-the-modulo-prime-myth

소수의 버킷으로 반올림하는 대신 가능한 많은 버킷을 사용해야한다고 주장합니다. 합리적인 가능성처럼 보입니다. 직관적으로, 더 많은 수의 버킷이 더 나은 방법을 확실히 알 수 있지만 수학적 주장을 할 수는 없습니다.


버킷 수가 많을수록 충돌이 줄어 듭니다. 비둘기 구멍 원리를 참조하십시오.
알 수 없음

11
@ 알 수 없음 : 나는 그것이 사실이라고 생각하지 않습니다. 내가 틀렸다면 나를 수정하십시오. 그러나 비둘기 구멍 원리를 해시 테이블에 적용하면 빈보다 많은 요소가있는 경우 충돌의 양이나 밀도에 대한 결론을 도출하지 않으면 충돌이 발생한다고 주장 할 수 있다고 생각합니다. 그러나 여전히 많은 수의 쓰레기통이 올바른 경로라고 생각합니다.
Falaina

충돌이 모든 의도와 목적에 무작위로 있다고 가정하면 생일 역설에 의해 더 큰 공간 (버킷)이 충돌 가능성을 줄입니다.
알 수 없음

1
@Unknown 당신은 충돌도 해시 함수 자체에 의존한다는 것을 놓쳤다. 따라서 has 기능이 실제로 나쁜 경우, 크기를 아무리 크게 늘리더라도 여전히 많은 충돌이있을 수 있습니다
Suraj Chandran

원래 기사는 사라진 것 같지만 원래 저자와의 토론을 포함하여 여기에 통찰력있는 의견이 있습니다. news.ycombinator.com/item?id=650487
Adrian McCarthy

3

소수는 고유 한 숫자입니다. 소수가 다른 소수를 가진 소수의 결과는 소수가 그것을 구성하는 데 사용된다는 사실 때문에 고유 한 가능성이 가장 높다는 점에서 독창적입니다. 이 속성은 해싱 함수에서 사용됩니다.

"Samuel"이라는 문자열이 주어지면 각 구성 숫자 또는 문자에 소수를 곱하고 합산하여 고유 한 해시를 생성 할 수 있습니다. 이것이 프라임이 사용되는 이유입니다.

그러나 소수를 사용하는 것은 오래된 기술입니다. 여기에서 핵심은 충분히 고유 한 키를 생성 할 수있는 한 다른 해싱 기술로도 이동할 수 있다는 것을 이해하는 것입니다. http://www.azillionmonkeys.com/qed/hash.html 에 대한이 주제에 대한 자세한 내용을 보려면 여기로 이동하십시오

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/


1
hahahah .... 실제로 2 소수의 곱은 소수와 다른 숫자의 곱보다 '고유 한'기회를 갖지 않습니까?
HasaniH

@Beska 여기에 "독창성"나는 "비 고유성은":) 같은 방식으로 정의되어야한다고 생각하므로, 재귀 적으로 정의된다
TT_

3

해시 함수의 선택에 따라 다릅니다.

많은 해시 함수는 데이터의 다양한 요소를 기계의 워드 크기에 해당하는 2의 거듭 제곱을 모듈로 곱하여 여러 요소를 결합합니다 (계산은 오버플로를 허용하여 계수가 자유 롭습니다).

데이터 요소의 승수와 해시 테이블의 크기 사이에 공통 요소를 원하지 않습니다. 데이터 요소를 변경해도 전체 테이블에 데이터가 분산되지 않을 수 있기 때문입니다. 테이블 크기에 대해 소수를 선택하면 공통 요인이 거의 없습니다.

반면에, 이러한 요소는 일반적으로 홀수 소수로 구성되므로 해시 테이블에 2의 거듭 제곱을 사용하여 안전해야합니다 (예 : Eclipse가 Java hashCode () 메소드를 생성 할 때 31을 사용함).


2

테이블 크기 (또는 모듈로의 숫자)가 T = (B * C)라고 가정하십시오. 이제 입력의 해시가 N이 정수일 수있는 (N * A * B)와 같으면 출력이 잘 분산되지 않습니다. n이 C, 2C, 3C 등이 될 때마다 출력이 반복되기 시작합니다. 즉, 출력은 C 위치에만 배포됩니다. 여기서 C는 (T / HCF (table-size, hash))입니다.

이 문제는 HCF 1을 만들어 제거 할 수 있습니다. 소수는 매우 좋습니다.

또 다른 흥미로운 점은 T가 2 ^ N 일 때입니다. 이것들은 모든 하위 N 비트의 입력 해시와 정확히 동일한 출력을 제공합니다. 모든 숫자가 2의 거듭 제곱으로 표현 될 수 있으므로 T로 어떤 수의 모듈로를 취하면> = N 인 2 개의 형식 번호의 모든 거듭 제곱을 빼서 입력에 따라 항상 특정 패턴의 수를 줄입니다. . 이것은 또한 나쁜 선택입니다.

마찬가지로, 10 ^ N 인 T도 비슷한 이유로 인해 나쁩니다 (2 진 대신 숫자의 10 진수 표기 패턴).

따라서 소수는 더 나은 분산 결과를 제공하는 경향이 있으므로 테이블 크기에 적합한 선택입니다.


2

내 다른 답변에서 복사 https://stackoverflow.com/a/43126969/917428 . 자세한 내용과 예는 참조하십시오.

컴퓨터가 기본 2에서 작동한다는 사실과 관련이 있다고 생각합니다. 기본 10에서 동일한 기능이 어떻게 작동하는지 생각해보십시오.

  • 8 % 10 = 8
  • 18 % 10 = 8
  • 87865378 % 10 = 8

숫자가 무엇이든 중요하지 않습니다. 8로 끝나는 한 모듈로 10은 8입니다.

충분히 크고 2의 제곱수가 아닌 숫자를 선택하면 해시 함수가 실제로 일부 입력 비트가 아니라 모든 입력 비트의 함수인지 확인할 수 있습니다.


1

Steve Jessop의 답변에 뭔가를 추가하고 싶습니다 (평판이 충분하지 않아서 언급 할 수 없습니다). 그러나 나는 유용한 자료를 발견했다. 그의 대답은 매우 도움이되지만 실수를 범했습니다. 버킷 크기는 2의 제곱이 아니어야합니다. 나는 토마스 코멘 (Thomas Cormen), Charles Leisersen 등의 "Introduction to Algorithm"책에서 263 페이지의 인용 만 할 것입니다.

나누기 방법을 사용할 때 일반적으로 특정 값 m을 피합니다. 예를 들어, m = 2 ^ p이면 h (k)는 k의 p 최하위 비트 일 뿐이므로 m은 2의 거듭 제곱이되어서는 안됩니다. 모든 하위 p- 비트 패턴이 똑같이 가능하다는 것을 알지 못하면 키의 모든 비트에 의존하도록 해시 함수를 설계하는 것이 좋습니다. 연습 11.3-3에서 보여 주듯이 k가 기수 2 ^ p로 해석되는 문자열 인 경우 m = 2 ^ p-1을 선택하면 k의 문자를 치환해도 해시 값이 변경되지 않으므로 선택이 좋지 않을 수 있습니다.

도움이 되길 바랍니다.


0

해시 함수의 경우 일반적으로 colisions를 최소화하는 것뿐만 아니라 몇 바이트를 변경하면서 동일한 해시를 유지하는 것을 불가능하게하는 것이 중요합니다.

: 당신이 방정식이 말 (x + y*z) % key = x과를 0<x<key하고 0<z<key. key가 소수이면 n * y = key는 N의 모든 n에 대해 true이고 다른 모든 수에 대해서는 false입니다.

key가 주요한 예가 아닌 예 : x = 1, z = 2 및 key = 8 key / z = 4는 여전히 자연수이므로 4는 방정식의 해가되고이 경우에는 (n / 2) * y = key는 N의 모든 n에 대해 참입니다. 8이 소수가 아니기 때문에 방정식의 해가 실제로 두 배가되었습니다.

공격자가 이미 8이 방정식에 대한 가능한 솔루션이라는 것을 알고 있다면 파일을 8에서 4로 변경하여 동일한 해시를 얻을 수 있습니다.


0

나는 위에있는 인기있는 답변 중 일부에 링크 된 인기있는 wordpress 웹 사이트를 읽었습니다. 내가 이해 한 것으로부터, 나는 내가 한 간단한 관찰을 나누고 싶다.

이 기사의 모든 세부 정보는 here 에서 찾을 수 있지만 다음 사항을 충족한다고 가정합니다.

  • 소수를 사용하면 고유 한 가치 의 "최고의 기회"가됩니다

일반적인 해시 맵 구현에서는 두 가지가 고유해야합니다.

  • 키의 고유 해시 코드
  • 실제 을 저장하는 고유 인덱스

고유 인덱스는 어떻게 얻습니까? 내부 컨테이너의 초기 크기도 중요합니다. 그래서 기본적으로 소수는 고유 객체를 생성하고 내부 컨테이너 내에서 색인을 찾는 고유 한 숫자를 생성하는 독특한 특성을 가지고 있기 때문에 관여합니다.

예:

키 = "키"

값 = "값" uniqueId = "k" * 31 ^ 2 + "e" * 31 ^ 1` + "y"

고유 한 ID로 매핑

이제 우리 는 우리의 가치를위한 독특한 위치 를 원합니다.

uniqueId % internalContainerSize == uniqueLocationForValue가정 internalContainerSize은 또한 프라임입니다.

나는 이것이 단순화 된 것을 알고 있지만 일반적인 아이디어를 얻고 싶습니다.


0

주요 전력 계수에 관한 "수학의 본질"은 유한 필드 의 하나의 빌딩 블록이라는 것 입니다 . 다른 두 빌딩 블록은 덧셈과 곱셈 연산입니다. 프라임 모듈러스의 특별한 속성은 모듈러스에 대한 "정규"덧셈과 곱셈 연산으로 유한 필드를 형성한다는 것입니다. 이것은 모든 곱셈이 다른 ​​정수 모듈로 프라임에 매핑되므로 모든 덧셈도 마찬가지입니다.

프라임 모듈러스는 다음과 같은 이유로 유리합니다.

  • 그들은 2 차 해싱에서 2 차 승수를 선택할 때 최대의 자유를 제공합니다. 0을 제외한 모든 승수는 모든 요소를 ​​정확히 한 번만 방문하게됩니다.
  • 모든 해시가 계수보다 작 으면 전혀 충돌이 없습니다
  • 랜덤 프라임은 두 계수의 거듭 제곱보다 더 잘 혼합되며 서브셋뿐만 아니라 모든 비트의 정보를 압축합니다.

그러나 그들은 큰 단점을 가지고 있으며, 현대의 CPU에서도 정수 분할이 필요합니다 (~ 15-40). 약 절반의 계산으로 해시가 매우 잘 혼합되도록 할 수 있습니다. 두 번의 곱셈과 xorshift 연산은 프라임 모 ud 러스보다 더 잘 혼합됩니다. 그런 다음 해시 테이블 크기와 해시 축소가 가장 빠른 것을 사용하여 2 테이블 크기의 거듭 제곱에 대해 총 7 개의 작업을 수행하고 임의 크기에 대해 약 9 개의 작업을 수행 할 수 있습니다.

나는 최근에 가장 빠른 해시 테이블 구현 을 많이 보았고 대부분은 프라임 모듈러스를 사용하지 않습니다.


0

이 질문은 해시 테이블이 2의 거듭 제곱이 아닌 소수 크기의 배열을 사용해야하는 더 적절한 질문과 병합되었습니다. 해시 함수 자체에는 많은 좋은 답변이 있지만 관련 질문에 대해서는 보안에 중요한 해시 테이블이 왜 glibc와 같이 소수 크기의 배열을 사용하십시오. 아직 없습니다.

일반적으로 2 테이블의 힘은 훨씬 빠릅니다. 비싸고 h % n => h & bitmask, 여기서 비트 마스크는 clz크기 n의 ( "카운트 선행 0")을 통해 계산 될 수있다 . 모듈로 함수는 정수보다 약 50 배 느린 정수 나누기를해야합니다 and. Lemire의 https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ 과 같은 모듈로를 피하는 몇 가지 트릭이 있지만 일반적으로 빠른 해시 테이블은 전원을 사용합니다 보안 해시 테이블은 소수를 사용합니다.

왜 그래?

이 경우 보안은 충돌 해결 전략에 대한 공격으로 정의되며, 대부분의 해시 테이블에서 충돌 목록이 선형으로 검색됩니다. 또는 빠른 주소 지정 테이블을 사용하면 테이블에서 직접 선형 검색을 수행 할 수 있습니다. 따라서 2 개의 테이블을 가지고 있고 일부 JSON 인터페이스가 제공하는 키 목록의 크기 또는 순서와 같이 테이블에 대한 내부 지식이 있으면 올바른 비트 수가 사용됩니다. 비트 마스크에있는 것의 수입니다. 일반적으로 10 비트보다 낮습니다. 그리고 5-10 비트의 경우 가장 강력하고 느린 해시 함수로도 강제 충돌을 일으키는 것은 사소한 일입니다. 더 이상 32 비트 또는 64 비트 해시 함수의 완전한 보안을 얻지 못합니다. 요점은 중얼 거리는 소리 나 시파 쉬 같은 괴물이 아닌 빠른 작은 해시 함수를 사용하는 것입니다.

따라서 DNS 리졸버, 프로그래밍 언어와 같은 해시 테이블에 외부 인터페이스를 제공하면 ... 그런 서비스를 DOS로 사용하려는 학대자들을 돌보고 싶습니다. 일반적으로 그러한 사람들은 훨씬 쉬운 방법으로 공공 서비스를 종료하는 것이 더 쉽지만 그렇게되었습니다. 그래서 사람들은 돌 보았습니다.

따라서 이러한 충돌 공격을 방지하는 가장 좋은 옵션은

1) 소수 테이블을 사용하려면

  • 32 비트 또는 64 비트는 모두 몇 개가 아닌 버킷을 찾는 데 적합합니다.
  • 해시 테이블 크기 조정 기능은 두 배보다 자연 스럽습니다. 최고의 성장 기능은 피보나치 수열이며 소수는 배가하는 것보다 더 가깝습니다.

2) 2 가지 크기의 빠른 힘과 함께 실제 공격에 대한 더 나은 대책을 사용하십시오.

  • 충돌을 계산하고 탐지 된 공격에 대해 중단 또는 휴면 (확률이 1 % 미만인 충돌 수) 32 비트 해시 테이블이있는 100과 같습니다. 이것은 djb의 dns resolver가하는 일입니다.
  • 충돌 공격이 감지되면 O (n)가 아닌 O (n) 검색을 사용하여 연결된 충돌 목록을 트리로 변환합니다. 이것이 자바와 같은 것입니다.

보다 안전한 해시 함수가 이러한 공격을 방지하는 데 도움이된다는 신화가 널리 퍼져 있습니다. 비트 수가 적은 보안은 없습니다. 이것은 소수 크기의 테이블에서만 작동하지만 느린 해시와 느린 소수 모듈로의 두 가지 가장 느린 방법의 조합을 사용합니다.

해시 테이블의 해시 함수는 기본적으로 작고 (가상화 가능해야 함) 빨라야합니다. 보안은 충돌에서 선형 검색을 막는 것만 가능합니다. 그리고 곱하기를 사용할 때 \ 0와 같은 일부 값에 둔감 한 해시 함수를 사용하지 마십시오.

랜덤 시드를 사용하는 것도 좋은 옵션입니다. 사람들이 먼저 시작했지만, 테이블에 대한 충분한 정보를 가지고도 랜덤 시드조차별로 도움이되지 않으며, 동적 언어는 일반적으로 다른 방법을 통해 시드를 얻는 것이 쉽지 않습니다. 알려진 메모리 위치.


-1
function eratosthenes(n) {

    function getPrime(x) {
        var middle = (x-(x%2))/2;
        var arr_rest = [];
        for(var j=2 ; j<=middle;j++){
            arr_rest.push(x%j);
        }

        if(arr_rest.indexOf(0) == -1) {
            return true
        }else {
            return false
        }

    }
    if(n<2)  {
        return []
    }else if(n==2){
        return [2]
    }else {
        var arr = [2]
        for(var i=3;i<n;i++) {
            if(getPrime(i)){
                arr.push(i)
            }
        }
    }

    return arr;
}

2
솔루션을 설명하기 위해 의견을 추가 할 수 있습니까?
pom421
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.