xor
là một chức năng mặc định nguy hiểm để sử dụng khi băm. Nó là tốt hơn and
và or
, nhưng điều đó không nói nhiều.
xor
là đố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, xor
cuố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ư xor
trê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)<<1
thay vì 0.
Điều này vẫn đối xứng; Vì vậy, "bad"
và "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ì 3
sẽ ánh xạ một k
số 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^k
cho 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 seed
với một hằng số (về cơ bản 0
là 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 1
và 0
. 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 0
trong trường hợp).
(Đối với những người không quen thuộc với C / C ++, a size_t
là 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.)