XOR이 해시를 결합하는 기본 방법 인 이유는 무엇입니까?


145

두 개의 해시가 H(A)있고 H(B)이를 결합하려고 한다고 가정하십시오 . 나는 두 개의 해시를 결합하는 좋은 방법이 XOR그들에게 있다는 것을 읽었습니다 XOR( H(A), H(B) ).

내가 찾은 가장 좋은 설명은 다음 해시 함수 지침 에 간략하게 설명되어 있습니다 .

대수 분포가 거의없는 두 숫자를 XOR하면 대수 분포가 다른 수는 여전히 발생하지만 두 값에 따라 달라집니다.
...
* 결합 할 두 숫자의 각 비트에서 두 비트가 같으면 0이, 그렇지 않으면 1이 출력됩니다. 즉, 조합의 50 %에서 1이 출력됩니다. 따라서 두 개의 입력 비트가 각각 대략 50 또는 50의 확률로 0 또는 1이면 출력 비트도 마찬가지입니다.

XOR이 OR 또는 AND 등이 아닌 해시 함수를 결합하기위한 기본 연산이어야하는 이유에 대한 직관 및 / 또는 수학을 설명 할 수 있습니까?


20
방금 당신이 한 것 같아요;)
Massa

22
XOR은 "combination"에서 원하는 것에 따라 해시를 "결합"하는 "좋은"방법 일 수도 있고 아닐 수도 있습니다. XOR은 교환 형입니다. XOR (H (A), H (B))는 XOR (H (B), H (A))와 같습니다. 즉, XOR은 순서를 캡처하지 않기 때문에 순서가 지정된 값 시퀀스의 해시를 작성하는 적절한 방법이 아닙니다.
Thomas Pornin

6
순서 문제 (위의 주석) 외에도 동일한 값에 문제가 있습니다. XOR (H (1), H (1)) = 0 (임의의 함수 H), XOR (H (2), H (2)) = 0 등. N의 경우 : XOR (H (N), H (N)) = 0. 동일한 값은 실제 응용 프로그램에서 자주 발생하므로 XOR의 결과가 너무 자주 0이되어 좋은 해시로 간주 될 수 있습니다.
Andrei Galatyn

순서대로 정렬 된 값에 무엇을 사용합니까? 타임 스탬프 또는 인덱스의 해시를 만들고 싶다고 가정 해 봅시다. (MSB는 LSB보다 덜 중요합니다). 이 스레드가 1 년 된 경우 죄송합니다.
Alexis

답변:


120

균일하게 랜덤 한 (1 비트) 입력을 가정하면 AND 함수 출력 확률 분포는 75 % 0및 25 % 1입니다. 반대로, OR은 25 % 0및 75 % 1입니다.

XOR 함수는 50 % 0및 50 % 1이므로 균일 한 확률 분포를 결합하는 데 좋습니다.

이것은 진리표를 작성하여 볼 수 있습니다.

 a | b | a AND b
---+---+--------
 0 | 0 |    0
 0 | 1 |    0
 1 | 0 |    0
 1 | 1 |    1

 a | b | a OR b
---+---+--------
 0 | 0 |    0
 0 | 1 |    1
 1 | 0 |    1
 1 | 1 |    1

 a | b | a XOR b
---+---+--------
 0 | 0 |    0
 0 | 1 |    1
 1 | 0 |    1
 1 | 1 |    0

운동 :이 1 비트 입력 얼마나 많은 논리적 기능 ab이 균일 한 출력 분포를 가지고? XOR이 귀하의 질문에 명시된 목적에 가장 적합한 이유는 무엇입니까?


24
연습에 대한 대답 : 16 개의 가능한 다른 XXX b 연산 (0, a & b, a > b, a, a < b, b, a % b, a | b, !a & !b, a == b, !b, a >= b, !a, a <= b, !a | !b, 1)에서 a와 b가 0과 1의 50 % -50 % 분포를 가정하면 다음은 50과 50 %의 분포를 갖습니다. a, b, !a, !b, a % b, a == b즉, 반대 XOR (EQUIV)도 사용할 수있었습니다.
Massa

7
그렉, 대단한 답변입니다. 내가 당신의 원래의 대답을보고 내 자신의 진리표를 쓴 후에 전구가 나를 위해 계속되었습니다. 배포판을 유지하기 위해 6 가지 적절한 작업이 있는지에 대한 @Massa의 대답을 고려했습니다. 그리고 a, b, !a, !b각 입력과 동일한 분포를 가지지 만 다른 입력의 엔트로피를 잃게됩니다. 즉, XOR은 a와 b 모두에서 엔트로피를 캡처하려고하기 때문에 해시 결합 목적에 가장 적합합니다.
네이트 머레이

1
다음은 각 해시 값의 비트 수보다 적은 비트를 출력하지 않으면 각 함수가 한 번만 호출되는 경우 해시를 안전하게 결합하는 것이 불가능하다는 설명입니다. 이것은이 답변이 올바르지 않다는 것을 암시합니다.
Tamás Szelei

3
@Massa 나는 XOR에 사용되거나 동등하지 않은 %를 본 적이 없다.
Buge

7
마찬가지로 Yakk 지적 이 동일한 값을 제로로 만들어, XOR이 위험 할 수있다. 이 방법 (a,a)(b,b)많은 (대부분?)의 경우 크게 해시 기반 데이터 구조에서의 충돌 가능성을 증가 생산 모두 제로.
Drew Noakes

170

xor해싱 할 때 사용할 위험한 기본 함수입니다. andand 보다 낫지 만 or많은 것을 말하지 않습니다.

xor대칭이므로 요소의 순서가 손실됩니다. 그래서 "bad"의지 해시와 같은 결합 "dab".

xor 쌍으로 동일한 값을 0에 매핑하므로 "공통"값을 0에 매핑하지 않아야합니다.

따라서 (a,a)0에 매핑되고 0에 (b,b)매핑됩니다. 이러한 쌍은 거의 임의성이 암시하는 것보다 거의 항상 흔하기 때문에 0보다 훨씬 많은 충돌이 발생합니다.

이 두 가지 문제 xor로 인해 표면에서 절반 정도 괜찮은 해시 결합기가 만들어졌지만 추가 검사 후에는 그렇지 않습니다.

최신 하드웨어에서는 일반적으로 거의 빠른 속도로 추가 xor합니다 (아마도 더 많은 전력을 사용하여이를 끌 수 있습니다). 덧셈의 ​​진리표는 xor문제의 비트 와 유사 하지만 두 값이 모두 1 일 때 다음 비트로 비트를 보냅니다. 이는 정보가 덜 지워짐을 의미합니다.

따라서 if hash(a) + hash(b)보다 결과가 0 대신에 더 낫습니다 .hash(a) xor hash(b)a==bhash(a)<<1

이것은 대칭으로 유지됩니다. 그래서 "bad""dab"같은 결과를 얻는 것은 문제가 남아있다. 적당한 비용으로이 대칭을 깨뜨릴 수 있습니다 :

hash(a)<<1 + hash(a) + hash(b)

일명 hash(a)*3 + hash(b). ( hash(a)시프트 솔루션을 사용하는 경우 한 번 계산 하고 저장하는 것이 좋습니다). 부호없는 정수에 대한 맵 은 일부 에 대해 수학적인 모듈러스이고 , 홀수 상수는 비교적 소수이기 때문에 대신에 홀수 상수 대신 3" k-비트"부호없는 정수를 자신에 매핑 합니다 .2^kk2^k

더 멋진 버전의 경우 다음을 boost::hash_combine효과적으로 검사 할 수 있습니다 .

size_t hash_combine( size_t lhs, size_t rhs ) {
  lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
  return lhs;
}

여기에 우리 seed는 상수 (기본적으로 임의 0의 s와 1s입니다-특히 32 비트 고정 소수점 분수와 같은 황금 비율의 역수)를 가진 일부 버전과 xor를 추가합니다. 이 휴식은 대칭 및 수신 해시 값이 있다면 소개합니다은 일부는 "노이즈", 즉 0으로 모든 구성 요소 해시를 상상 (가난한 - 위의 손잡이는 잘의 얼룩을 생성 1하고 0. 각 결합 후이야 내 순진 3*hash(a)+hash(b)단순히 출력 0의를 그 경우).

(C / C ++에 익숙하지 않은 사용자의 경우 a size_t는 메모리에있는 오브젝트의 크기를 설명하기에 충분히 큰 부호없는 정수 값입니다. 64 비트 시스템에서는 일반적으로 64 비트 부호없는 정수입니다. 32 비트 시스템에서 , 32 비트 부호없는 정수)


좋은 답변 야크. 이 알고리즘은 32 비트 및 64 비트 시스템에서 동일하게 작동합니까? 감사.
Dave

1
@dave는에 비트를 더 추가합니다 0x9e3779b9.
Yakk-Adam Nevraumont

10
자, 완료되었습니다 ... 여기에 완전 정밀도 64 비트 상수 (긴 배가 있고 부호없는 긴 long으로 계산 됨)는 0x9e3779b97f4a7c16입니다. 흥미롭게도 여전히 짝수입니다. Golden Ratio 대신 PI를 사용하여 동일한 계산을 다시 수행하면 0x517cc1b727220a95가 생성됩니다. 이는 짝수 대신 홀수이므로 다른 상수보다 "더 많은 소수"일 수 있습니다. 나는 다음을 사용했다 : std :: cout << std :: hex << (부호없는 long long) ((1.0L / 3.14159265358979323846264338327950288419716939937510L) * (powl (2.0L, 64.0L))) << std :: endl; cout.precision (numeric_limits <long double> :: max_digits10); 다시 Yakk 감사합니다.
Dave

2
@이 경우의 역 골든 비율 규칙을 작성하면 수행하는 계산과 같거나 큰 첫 번째 홀수 입니다. 따라서 1을 더하면됩니다. N *의 순서, 최대 크기의 mod (여기서는 2 ^ 64) 순서에서 다음 값을 가장 큰 '갭'의 중간에있는 비율에 정확하게 위치시키기 때문에 중요한 숫자입니다. 번호. 자세한 정보는 웹에서 "피보나치 해싱"을 검색하십시오.
Scott Carey

1
@Dave 올바른 숫자는 0.9E3779B97F4A7C15F39입니다 ... 링크 참조 . 당신은 1에서 1까지 빼면 리터럴 sqrt (5) 상수로 시작한다면 간단히 고르지 못한 규칙을 겪을 수 있습니다. 비트가 손실되었을 것입니다.
Migle

29

편리한 비트 믹싱 속성에도 불구하고 XOR은 정류 성으로 인해 해시를 결합하는 좋은 방법 이 아닙니다 . {1, 2,…, 10}의 순열을 10- 튜플의 해시 테이블에 저장하면 어떻게 될지 고려하십시오.

m 이 큰 홀수 m * H(A) + H(B)인 곳 이 훨씬 더 나은 선택입니다 .

크레딧 : 위의 결합기는 Bob Jenkins의 팁이었습니다.


2
때때로 교환 법칙은 좋은 일이지만, XOR 형편없는 선택 에도 다음 일치 항목의 모든 쌍 제로로 해시 얻을 것이다 때문이다. 산술 합계가 더 좋습니다. 일치하는 항목 쌍의 해시는 32 개가 아닌 31 비트의 유용한 데이터 만 유지하지만 0을 유지하는 것보다 훨씬 낫습니다. 다른 옵션은 산술 합계를 a로 계산 long한 다음 상단 부분을 하단 부분과 다시 병합하는 것입니다.
supercat

1
m = 3실제로 많은 시스템에서 좋은 선택이며 매우 빠릅니다. 홀수 m정수 곱셈은 모듈로 2^32이거나 2^64뒤집을 수 없으므로 비트가 손실되지 않습니다.
StefanKarpinski

MaxInt를 넘어 서면 어떻게됩니까?
파괴적인

2
홀수 대신에 소수를 선택해야합니다
TermoTux

2
해시를 결합 할 때 필요하지 않은 @Infinum.
Marcelo Cantos

17

Xor는 해시를 결합하는 "기본"방법 일 수 있지만 Greg Hewgill의 답변은 그 함정이있는 이유를 보여줍니다. 두 개의 동일한 해시 값의 xor는 0입니다. 실제로는 예상했던 것보다 동일한 해시가 더 일반적입니다. 그런 경우가 많지 않은 코너 사례에서 결과 결합 해시는 항상 동일하다는 것을 알 수 있습니다. 해시 충돌은 예상보다 훨씬 더 자주 발생합니다.

고안된 예에서는 관리하는 다른 웹 사이트의 사용자의 해시 비밀번호를 결합 할 수 있습니다. 불행히도 많은 사용자가 자신의 암호를 재사용하고 결과 해시의 놀라운 비율은 0입니다!


나는 그 예가 결코 일어나지 않기를 바랍니다. 비밀번호는 소금에 절 여야합니다.
user60561

8

이 페이지를 찾는 다른 사람들에게 명시 적으로 지적하고 싶은 것이 있습니다. AND 및 OR BlueRaja와 같은 출력 제한-Danny Pflughoe가 지적하려고하지만 더 잘 정의 할 수 있습니다.

먼저 Min ()과 Max ()라는 두 가지 간단한 함수를 정의하고 싶습니다.

Min (A, B)는 A와 B 사이에서 작은 값을 반환합니다 (예 : Min (1, 5)는 1을 반환 함).

Max (A, B)는 A와 B 사이에서 더 큰 값을 반환합니다 (예 : Max (1, 5)는 5를 반환 함).

당신이 주어진 경우 : C = A AND B

그런 다음 C <= Min(A, B)A 또는 B의 0 비트로 AND를 1로 만들 수있는 것이 없기 때문에 이것을 알 수 있습니다. 따라서 모든 0 비트는 0 비트를 유지하며 모든 1 비트는 0 비트가 될 가능성이 있습니다 (따라서 더 작은 값).

와: C = A OR B

반대의 경우도 마찬가지입니다.이를 C >= Max(A, B)통해 AND 함수에 대한 결과를 볼 수 있습니다. 이미 1 인 비트는 0으로 OR 될 수 없으므로 1로 유지되지만 모든 0 비트는 1이 될 가능성이 있으므로 더 큰 숫자가됩니다.

이는 입력 상태가 출력에 제한을 적용 함을 의미합니다. AND를 90으로 설정하면 다른 값이 무엇이든 출력이 90 이하임을 알 수 있습니다.

XOR의 경우 입력을 기반으로 함축 된 제한이 없습니다. 255의 바이트를 XOR하면 역수보다 바이트를 얻을 수 있지만 그로부터 가능한 바이트를 출력 할 수있는 특별한 경우가 있습니다. 모든 비트는 다른 피연산자의 동일한 비트에 따라 상태를 변경할 수 있습니다.


6
하나는 말할 수 OR있다 비트 최대AND입니다 비트 분 .
Paŭlo Ebermann

Paulo Ebermann은 매우 잘 말했습니다. Crypto.SE뿐만 아니라 당신을 만나서 반갑습니다!
코리 오그 번

cryptography 태그가 붙은 모든 것을 포함 하는 필터만들었으며 이전 질문으로 변경했습니다. 이 방법으로 나는 당신의 대답을 여기에서 찾았습니다.
Paŭlo Ebermann

3

당신이 경우 XOR바이어스 입력을 임의의 입력, 출력은 랜덤입니다. AND또는에 대해서도 마찬가지입니다 OR. 예:

00101001 XOR 00000000 = 00101001
00101001 및 00000000 = 00000000
00101001 또는 11111111 = 11111111

마찬가지로 @Greg Hewgill이 경우에도 언급 모두 입력을 사용하여 랜덤 AND또는 OR바이어스 출력 될 것이다.

우리가 XOR더 복잡한 것을 사용하는 이유는 , XOR완벽하게 작동하고 엄청나게 빠르기 때문입니다.


1

왼쪽 2 열을 덮고 입력 만 출력을 사용하여 무엇을 해결하려고 노력하십시오.

 a | b | a AND b
---+---+--------
 0 | 0 |    0
 0 | 1 |    0
 1 | 0 |    0
 1 | 1 |    1

1 비트를 보았을 때 두 입력이 모두 1이라는 것을 알아 내야했습니다.

이제 XOR에 대해 동일한 작업을 수행하십시오.

 a | b | a XOR b
---+---+--------
 0 | 0 |    0
 0 | 1 |    1
 1 | 0 |    1
 1 | 1 |    0

XOR은 입력에 대해 아무 것도주지 않습니다.


0

다양한 버전에 대한 소스 코드 hashCode()java.util.Arrays는 고체 통용 해싱 알고리즘에 대한 좋은 참고이다. 그들은 쉽게 이해하고 다른 프로그래밍 언어로 번역됩니다.

대략적으로 말하면, 대부분의 다중 속성 hashCode()구현은 다음 패턴을 따릅니다.

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

이면의 마술 31과 Java 코드가 왜 그렇게 자주 사용하는지 에 대한 자세한 내용은 다른 StackOverflow Q & A를 검색 할 수 있습니다 . 불완전하지만 일반적인 성능 특성이 매우 우수합니다.


2
자바의 기본 "multply 31 추가하여이 / 축적"해시 충돌로로드 (예를 들어, 어떤 string충돌 string + "AA"IIRC) 그리고 그들은 오래 전에 그들이 사양으로, 그 알고리즘에 구운하지 않았다 바랬다. 즉, 더 많은 비트 세트로 큰 홀수를 사용하고 시프트 또는 회전을 추가하면 문제가 해결됩니다. MurmurHash3의 'mix'가이를 수행합니다.
Scott Carey

0

XOR 은 때때로 ORAND와 같은 일부 입력을 무시하지 않습니다 .

당신이 가지고가는 경우에 AND (X, Y) 예를 들어, 사료 입력 X 거짓, 다음 입력을 Y 중요하지 않습니다 ... 하나는 아마 해시를 결합 할 때 입력이 중요 할 것입니다.

XOR (X, Y) 를 취하면 BOTH 입력은 항상 중요합니다. Y가 중요하지 않은 X의 값은 없습니다. X 또는 Y가 변경되면 출력에 반영됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.