Tại sao XOR là cách mặc định để kết hợp băm?


145

Giả sử bạn có hai băm H(A)H(B)và bạn muốn kết hợp chúng. Tôi đã đọc rằng một cách tốt để kết hợp hai băm là với XORchúng, ví dụ XOR( H(A), H(B) ).

Giải thích tốt nhất mà tôi tìm thấy được đề cập ngắn gọn ở đây về các nguyên tắc hàm băm này :

XOR hai số có kết quả phân phối ngẫu nhiên gần đúng trong một số khác vẫn có phân phối gần như ngẫu nhiên *, nhưng hiện tại phụ thuộc vào hai giá trị.
...
* Tại mỗi bit của hai số kết hợp, 0 là đầu ra nếu hai bit bằng nhau, khác a 1. Nói cách khác, trong 50% kết hợp, 1 sẽ là đầu ra. Vì vậy, nếu hai bit đầu vào, mỗi bit có khoảng 50-50 cơ hội là 0 hoặc 1, thì bit đầu ra cũng vậy.

Bạn có thể giải thích trực giác và / hoặc toán học đằng sau lý do tại sao XOR nên là hoạt động mặc định để kết hợp các hàm băm (chứ không phải OR hoặc AND, v.v.) không?


20
Tôi nghĩ bạn vừa làm;)
Massa

22
lưu ý rằng XOR có thể hoặc không phải là cách "kết hợp" băm "tốt", tùy thuộc vào những gì bạn muốn trong "kết hợp". XOR là giao hoán: XOR (H (A), H (B)) bằng XOR (H (B), H (A)). Điều này có nghĩa là XOR không phải là một cách thích hợp để tạo ra một loại hàm băm của một chuỗi các giá trị được sắp xếp, vì nó không nắm bắt được thứ tự.
Thomas Pornin

6
Bên cạnh vấn đề với thứ tự (nhận xét ở trên), có vấn đề với các giá trị bằng nhau. XOR (H (1), H (1)) = 0 (cho bất kỳ chức năng H), XOR (H (2), H (2)) = 0, v.v. Với mọi N: XOR (H (N), H (N)) = 0. Các giá trị bằng nhau xảy ra khá thường xuyên trong các ứng dụng thực, điều đó có nghĩa là kết quả của XOR sẽ là 0 quá thường xuyên để được coi là băm tốt.
Andrei Galatyn

Bạn sử dụng gì cho chuỗi giá trị được sắp xếp? Giả sử tôi muốn tạo một hàm băm dấu thời gian hoặc chỉ mục. (MSB ít quan trọng hơn LSB). Xin lỗi nếu chủ đề này là 1 năm tuổi.
Alexis

Câu trả lời:


120

Giả sử các đầu vào ngẫu nhiên (1 bit) thống nhất, phân phối xác suất đầu ra của hàm AND là 75% 0và 25% 1. Ngược lại, OR là 25% 0và 75% 1.

Hàm XOR là 50% 0và 50% 1, do đó, rất tốt để kết hợp các phân phối xác suất thống nhất.

Điều này có thể được nhìn thấy bằng cách viết ra các bảng sự thật:

 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

Bài tập: Có bao nhiêu hàm logic của hai đầu vào 1 bit abcó phân phối đầu ra thống nhất này? Tại sao XOR phù hợp nhất cho mục đích được nêu trong câu hỏi của bạn?


24
Trả lời bài tập: từ 16 thao tác XXX b khác nhau có thể có, các thao tác (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)sau có 50% -50% phân phối 0 và 1, giả sử a và b có 50% -50% phân phối 0 và 1: a, b, !a, !b, a % b, a == bngược lại của XOR (THIẾT BỊ) cũng có thể đã được sử dụng ...
Massa

7
Greg, đây là một câu trả lời tuyệt vời. Bóng đèn bật sáng cho tôi sau khi tôi thấy câu trả lời ban đầu của bạn và viết ra bảng sự thật của riêng tôi. Tôi đã xem xét câu trả lời của @ Massa về cách có 6 thao tác phù hợp để duy trì phân phối. Và trong khi a, b, !a, !bsẽ có cùng phân phối với đầu vào tương ứng của chúng, bạn sẽ mất entropy của đầu vào khác. Đó là, XOR phù hợp nhất cho mục đích kết hợp băm vì chúng tôi muốn thu thập entropy từ cả a và b.
Nate Murray

1
Dưới đây là một bài viết giải thích rằng việc kết hợp băm an toàn trong đó mỗi hàm chỉ được gọi một lần là không thể mà không xuất ra ít bit hơn tổng số bit trong mỗi giá trị băm. Điều này cho thấy câu trả lời này là không chính xác.
Tamás Szelei

3
@Massa Tôi chưa bao giờ thấy% được sử dụng cho XOR hoặc không bằng.
Buge

7
Như Yakk chỉ ra , XOR có thể nguy hiểm vì nó tạo ra số không cho các giá trị giống hệt nhau. Điều này có nghĩa (a,a)(b,b)cả hai đều tạo ra số không, trong nhiều trường hợp (hầu hết?) Làm tăng đáng kể khả năng va chạm trong cấu trúc dữ liệu dựa trên hàm băm.
Drew Noakes

170

xorlà một chức năng mặc định nguy hiểm để sử dụng khi băm. Nó là tốt hơn andor, nhưng điều đó không nói nhiều.

xorlà đối xứng, do đó thứ tự của các phần tử bị mất. Vì vậy, "bad"băm sẽ kết hợp giống như "dab".

xor ánh xạ các giá trị giống hệt nhau thành 0 và bạn nên tránh ánh xạ các giá trị "chung" thành 0:

Vì vậy, (a,a)được ánh xạ về 0 và (b,b)cũng được ánh xạ tới 0. Vì các cặp như vậy hầu như luôn luôn phổ biến hơn so với ngẫu nhiên có thể ngụ ý, bạn kết thúc với nhiều va chạm ở mức 0 so với bình thường.

Với hai vấn đề này, xorcuối cùng trở thành một công cụ băm có vẻ ngoài một nửa trên bề mặt, nhưng không phải sau khi kiểm tra thêm.

Trên phần cứng hiện đại, việc bổ sung thường nhanh như vậy xor(có thể sử dụng nhiều năng lượng hơn để thực hiện việc này, phải thừa nhận). Thêm bảng chân lý tương tự như xortrên bit trong câu hỏi, nhưng nó cũng gửi một bit đến bit tiếp theo khi cả hai giá trị là 1. Điều này có nghĩa là nó xóa ít thông tin hơn.

Vì vậy, hash(a) + hash(b)tốt hơn là hash(a) xor hash(b)trong đó nếu a==b, kết quả là hash(a)<<1thay vì 0.

Điều này vẫn đối xứng; Vì vậy, "bad""dab"nhận được kết quả tương tự vẫn là một vấn đề. Chúng ta có thể phá vỡ tính đối xứng này với chi phí khiêm tốn:

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

aka hash(a)*3 + hash(b). (tính toán hash(a)một lần và lưu trữ được khuyến nghị nếu bạn sử dụng giải pháp thay đổi). Bất kỳ hằng số lẻ nào thay vì 3sẽ ánh xạ một ksố nguyên không dấu "-bit" cho chính nó, vì ánh xạ trên các số nguyên không dấu là modulo toán học 2^kcho một số k, và bất kỳ hằng số lẻ nào cũng tương đối nguyên tố 2^k.

Đối với một phiên bản thậm chí fancier, chúng tôi có thể kiểm tra boost::hash_combine, đó là hiệu quả:

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

ở đây chúng ta thêm vào một số phiên bản thay đổi của seedvới một hằng số (về cơ bản 0là s và s ngẫu nhiên 1- đặc biệt nó là tỷ lệ nghịch của tỷ lệ vàng dưới dạng phân số điểm cố định 32 bit) với một số bổ sung và xor. Phá vỡ đối xứng này, và giới thiệu một số "tiếng ồn" nếu các giá trị băm đến nghèo (ví dụ, hãy tưởng tượng mỗi băm thành phần tỉ số 0 - tay cầm ở trên nó tốt, tạo ra một bôi nhọ của 10. S sau mỗi lần kết hợp của tôi ngây thơ 3*hash(a)+hash(b)đơn giản là kết quả đầu ra một 0trong trường hợp).

(Đối với những người không quen thuộc với C / C ++, a size_tlà một giá trị nguyên không dấu, đủ lớn để mô tả kích thước của bất kỳ đối tượng nào trong bộ nhớ. Trên hệ thống 64 bit, thường là số nguyên không dấu 64 bit. Trên hệ thống 32 bit , số nguyên không dấu 32 bit.)


Câu trả lời tốt đẹp Yakk. Thuật toán này có hoạt động tốt như nhau trên cả hệ thống 32 bit và 64 bit không? Cảm ơn.
Dave

1
@dave thêm nhiều bit vào 0x9e3779b9.
Yakk - Adam Nevraumont

10
OK, để hoàn thành ... đây là hằng số 64 bit chính xác đầy đủ (được tính bằng số nhân dài và số dài không dấu): 0x9e3779b97f4a7c16. Điều thú vị là nó vẫn còn. Thực hiện lại phép tính tương tự bằng PI thay vì Tỷ lệ vàng tạo ra: 0x517cc1b727220a95 là số lẻ, thay vì chẵn, do đó có thể "nguyên tố" hơn so với hằng số khác. Tôi đã sử dụng: std :: cout << std :: hex << (dài chưa ký) ((1.0L / 3.14159265358979323846264338327950288419716939937510L) * (powl (2.0L, 64.0L))) với cout.precision (num_limits <long double> :: max_digits10); Cảm ơn một lần nữa Yakk.
Dave

2
@ Lưu quy tắc tỷ lệ vàng nghịch đảo cho các trường hợp này là số lẻ đầu tiên bằng hoặc lớn hơn phép tính bạn đang thực hiện. Vì vậy, chỉ cần thêm 1. Đây là một số quan trọng vì chuỗi N * tỷ lệ, sửa đổi kích thước tối đa (2 ^ 64 ở đây) đặt giá trị tiếp theo trong chuỗi chính xác ở tỷ lệ đó ở giữa 'khoảng cách' lớn nhất trong số. Tìm kiếm trên web cho "băm Fibonacci" để biết thêm thông tin.
Scott Carey

1
@ Lưu đúng số sẽ là 0,9E3779B97F4A7C15F39 ... Xem liên kết . Bạn có thể bị quy tắc làm tròn (thậm chí tốt cho kế toán viên) hoặc đơn giản, nếu bạn bắt đầu với hằng số sqrt (5) theo nghĩa đen, khi bạn trừ đi 1, bạn loại bỏ bit thứ tự cao, a bit phải bị mất
di chuyển

29

Mặc dù có các đặc tính trộn bit tiện dụng, XOR không phải là một cách tốt để kết hợp các giá trị băm do tính giao hoán của nó. Hãy xem xét điều gì sẽ xảy ra nếu bạn lưu trữ các hoán vị của {1, 2, Nhận, 10} trong bảng băm gồm 10 bộ.

Một lựa chọn tốt hơn nhiều là m * H(A) + H(B), trong đó m là một số lẻ lớn.

Tín dụng: Bộ kết hợp trên là một mẹo từ Bob Jenkins.


2
Đôi khi giao hoán là một điều tốt, nhưng xor là một lựa chọn tệ hại ngay cả sau đó bởi vì tất cả các cặp vật phẩm phù hợp sẽ được băm về không. Một tổng số học là tốt hơn; hàm băm của một cặp vật phẩm phù hợp sẽ chỉ giữ lại 31 bit dữ liệu hữu ích thay vì 32, nhưng điều đó tốt hơn nhiều so với giữ lại số không. Một lựa chọn khác có thể là tính tổng số học dưới dạng a longvà sau đó chuyển phần trên trở lại với phần dưới.
supercat

1
m = 3thực sự là một lựa chọn tốt và rất nhanh trên nhiều hệ thống. Lưu ý rằng đối với bất kỳ mphép nhân số nguyên lẻ nào là modulo 2^32hoặc 2^64do đó không thể đảo ngược để bạn không bị mất bất kỳ bit nào.
StefanKarpinki

Điều gì xảy ra khi bạn vượt ra ngoài MaxInt?
gây rối

2
thay vì bất kỳ số lẻ nào nên chọn số nguyên tố
TermoTux

2
@Infinum không cần thiết khi kết hợp băm.
Marcelo Cantos

17

Xor có thể là cách "mặc định" để kết hợp băm nhưng câu trả lời của Greg Hewgill cũng cho thấy lý do tại sao nó có những cạm bẫy của nó: Xor của hai giá trị băm giống hệt nhau bằng không. Trong cuộc sống thực, có những băm giống hệt nhau là phổ biến hơn người ta có thể mong đợi. Sau đó, bạn có thể thấy rằng trong các trường hợp góc này (không thường xuyên), các giá trị băm kết hợp luôn luôn giống nhau (không). Va chạm băm sẽ nhiều, thường xuyên hơn nhiều so với bạn mong đợi.

Trong một ví dụ giả định, bạn có thể kết hợp mật khẩu băm của người dùng từ các trang web khác nhau mà bạn quản lý. Thật không may, một số lượng lớn người dùng sử dụng lại mật khẩu của họ và một tỷ lệ đáng ngạc nhiên trong số băm kết quả là bằng không!


Tôi hy vọng ví dụ giả định không bao giờ xảy ra, mật khẩu nên được lưu lại.
dùng60561

8

Có một cái gì đó tôi muốn chỉ ra rõ ràng cho những người khác tìm thấy trang này. AND và OR hạn chế đầu ra như BlueRaja - Danny Pflughoe đang cố gắng chỉ ra, nhưng có thể được xác định rõ hơn:

Đầu tiên tôi muốn xác định hai hàm đơn giản tôi sẽ sử dụng để giải thích điều này: Min () và Max ().

Min (A, B) sẽ trả về giá trị nhỏ hơn giữa A và B, ví dụ: Min (1, 5) trả về 1.

Max (A, B) sẽ trả về giá trị lớn hơn giữa A và B, ví dụ: Max (1, 5) trả về 5.

Nếu bạn được cho: C = A AND B

Sau đó, bạn có thể tìm thấy rằng C <= Min(A, B) Chúng tôi biết điều này bởi vì không có gì bạn có thể VÀ với 0 bit của A hoặc B để biến chúng thành 1s. Vì vậy, mỗi bit 0 giữ một bit 0 và mỗi bit có cơ hội trở thành bit 0 (và do đó giá trị nhỏ hơn).

Với: C = A OR B

Điều ngược lại là đúng: C >= Max(A, B)Với điều này, chúng ta thấy hệ quả của hàm AND. Bất kỳ bit nào đã là một số không thể được OR thành số 0, vì vậy nó giữ nguyên một, nhưng mọi bit 0 đều có cơ hội trở thành số một, và do đó số lớn hơn.

Điều này ngụ ý rằng trạng thái của đầu vào áp dụng các hạn chế đối với đầu ra. Nếu bạn VÀ bất cứ điều gì với 90, bạn biết đầu ra sẽ bằng hoặc nhỏ hơn 90 bất kể giá trị khác là gì.

Đối với XOR, không có hạn chế ngụ ý dựa trên các đầu vào. Có những trường hợp đặc biệt mà bạn có thể thấy rằng nếu bạn XOR một byte với 255 hơn bạn nhận được nghịch đảo nhưng bất kỳ byte nào có thể có thể được xuất ra từ đó. Mỗi bit có cơ hội thay đổi trạng thái tùy thuộc vào cùng một bit trong toán hạng khác.


6
Người ta có thể nói rằng đó ORtối đa bit , và ANDbit min .
Paŭlo Ebermann

Paulo Ebermann nói rất rõ. Rất vui được gặp bạn ở đây cũng như Crypto.SE!
Corey Ogburn

Tôi đã tạo một bộ lọc bao gồm cho tôi mọi thứ được mã hóa bằng thẻ , cũng thay đổi các câu hỏi cũ. Bằng cách này tôi đã tìm thấy câu trả lời của bạn ở đây.
Paŭlo Ebermann

3

Nếu bạn XORnhập ngẫu nhiên với đầu vào sai lệch, đầu ra là ngẫu nhiên. Điều tương tự không đúng với ANDhoặc OR. Thí dụ:

00101001 XOR 00000000 = 00101001
00101001 VÀ 00000000 = 00000000
00101001 HOẶC 11111111 = 11111111

Như @Greg Hewgill đề cập, ngay cả khi cả hai yếu tố đầu vào là ngẫu nhiên, sử dụng ANDhoặc ORsẽ dẫn đến đầu ra sai lệch.

Lý do chúng tôi sử dụng XORcho một thứ phức tạp hơn là, không, không cần: XORhoạt động hoàn hảo, và nó cực kỳ ngu ngốc - nhanh chóng.


1

Che 2 cột bên trái và cố gắng tìm ra những gì đầu vào đang sử dụng chỉ là đầu ra.

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

Khi bạn thấy 1 bit, bạn nên biết rằng cả hai đầu vào là 1.

Bây giờ làm tương tự cho XOR

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

XOR không cho đi gì về đầu vào.


0

Các mã nguồn cho các phiên bản khác nhau của hashCode()trong java.util.Arrays là một tài liệu tham khảo tuyệt vời cho, sử dụng chung các thuật toán băm rắn. Chúng dễ dàng được hiểu và dịch sang các ngôn ngữ lập trình khác.

Nói một cách đơn giản, hầu hết các hashCode()triển khai đa thuộc tính đều theo mô hình này:

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;
}

Bạn có thể tìm kiếm Hỏi & Đáp về StackOverflow khác để biết thêm thông tin về phép thuật đằng sau 31và tại sao mã Java sử dụng nó thường xuyên như vậy. Nó là không hoàn hảo, nhưng có đặc điểm hiệu suất chung rất tốt.


2
Hàm băm "multply by 31 và add / tích lũy" mặc định của Java được tải với các xung đột (ví dụ: bất kỳ stringva chạm nào với string + "AA"IIRC) và từ lâu họ đã ước rằng họ không nướng thuật toán đó vào thông số kỹ thuật. Điều đó nói rằng, sử dụng một số lẻ lớn hơn với nhiều bit được đặt và thêm ca hoặc xoay sẽ khắc phục vấn đề đó. 'Hỗn hợp' của MurmurHash3 thực hiện điều này.
Scott Carey

0

XOR không bỏ qua một số đầu vào đôi khi như ORAND .

Nếu bạn lấy AND (X, Y) chẳng hạn và cung cấp đầu vào X sai, thì đầu vào Y không thành vấn đề ... và người ta có thể muốn đầu vào có vấn đề khi kết hợp băm.

Nếu bạn lấy XOR (X, Y) thì CẢ HAI đầu vào LUÔN LUÔN . Sẽ không có giá trị của X khi Y không quan trọng. Nếu X hoặc Y bị thay đổi thì đầu ra sẽ phản ánh điều đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.