Hash Functions and Unordered Maps
Usually the hash function you already have is enough.
Predicting an Exclusive Or Result
An unordered map needs a way to turn each key into a hash value. Usually the hash function you already have is enough. Sometimes it is not: the key type may be your own, or you may want a new hash that is derived from parts of the key that already have hash functions. This article studies one approach the source describes for that situation, which is to build a new hash function by combining existing hash functions. The source calls this approach simple and often very effective. Before we look at hashes at all, try a plain integer operation, because the combining step is built on it.
What do you think happens?
In C++, what does the expression 5 ^ 3 evaluate to? Make a guess before you read the answer.
Reveal answer
6
The caret ^ is the exclusive or operator. It gives 1 in each bit position where the two operands differ. In binary, 5 is 101 and 3 is 011. The bits differ in the first and second positions, so the result is 110, which is 6.
Building a Hash From Existing Hashes
Suppose you have two existing hash functions, h_1 and h_2, and a key k. Each function looks at the same key and produces its own hash value. A combined hash takes those two outputs and merges them into one new value using exclusive or. The final result therefore depends on both outputs, and you do not have to design a hashing scheme from scratch.
Here the symbol $\oplus$ stands for exclusive or. The combined value is the new hash for the key, and it is the value that the unordered map would use to decide where the key-value pair goes.
How Exclusive Or Combines Bits
The source names exclusive or, written with the caret, as the tool for combining hash values. In C++ the operator is written as ^. The mechanism is easiest to see at the bit level. Exclusive or compares two bits in the same position and gives 1 only when they differ. When the bits match, the result bit is 0.
6- h1 and h2 stand in for two hash results. Integers keep the output fixed so you can check it by hand.
- The ^ operator performs exclusive or on the two integers, bit by bit.
- 5 is 101 and 3 is 011 in binary. They differ in two positions, so the result is 110, which prints as 6.
- 1Write 5 in binary as 101.
- 2Write 3 in binary as 011.
- 3Compare bit by bit: position 1 gives 1 and 0 (differ, result 1), position 2 gives 0 and 1 (differ, result 1), position 3 gives 1 and 1 (match, result 0).
- 4The result is 110 in binary, which is 6 in decimal.
Tracing a Combined Hash Through Two Cases
Now apply the operation to a combined hash. Consider a combined hash built as h(a) XOR h(b), where a and b are two pieces of a key. Trace what happens to the result when the two pieces hash to different values, and then when they hash to the same value.
Combining the hashes of two key pieces
A key has two pieces, a and b. Suppose h(a) = 5 and h(b) = 3. Then suppose h(a) = 5 and h(b) = 5. Find the combined hash in each case.
- 1Case 1: different values
Compute h(a) XOR h(b) = 5 XOR 3. From the earlier code, this is 6.
- 2Case 2: identical values
Compute h(a) XOR h(b) = 5 XOR 5. Every bit of 5 matches itself, so every result bit is 0, and the combined hash is 0.
- 3Compare the outcomes
The two pieces produced different combined hashes in Case 1 and an identical combined hash of 0 in Case 2, even though both keys have the same two pieces in structure.
The combined hash is 6 when the piece hashes differ and 0 when they are identical.
Where the Combination Breaks Down
The source says combining hash functions is often very effective, and the word often is doing real work. Two properties of exclusive or explain where the approach can fail.
The first property is that exclusive or is commutative: $x \oplus y = y \oplus x$. Combining the same two hash values in either order gives the same result. So a pair of pieces (a, b) and the pair (b, a) produce identical combined hashes. A map cannot tell those two keys apart by their hash values alone.
The second property is that any value exclusive ored with itself is 0: $x \oplus x = 0$. If both pieces of a key have the same hash value, the combined hash is always 0, no matter what the key contains. A map that uses this combination would place every such key in the same bucket.
Mistakes When Combining Hashes
Common mistake Assuming the combined hash preserves the order of the key's parts
What it looks likeh(a) ^ h(b) and h(b) ^ h(a) are both 6 when h(a) is 5 and h(b) is 3.
Why it failsExclusive or is commutative, so swapping the two inputs does not change the result.
Do this insteadIf order matters for equality, combine the parts in a way that is not symmetric, or test whether swapped keys collide.
Common mistake Expecting distinct keys to always get distinct combined hashes
What it looks likeTwo pieces with equal hash values always produce a combined hash of 0.
Why it failsAny value exclusive ored with itself is 0, so equal pieces cancel out completely.
Do this insteadCheck realistic keys for repeated or matching parts, and test the combined hash on them before relying on it.
Common mistake Treating the source's often very effective as a guarantee rather than a hedged claim
Evaluating a Combined Hash
A key has two pieces, a and b, with h(a) = 9 and h(b) = 9. Predict the combined hash h(a) XOR h(b) before you check. Then explain whether this key would be a good or poor case for the combination, and say what you would test before using it.
Hints
- Recall that any value exclusive ored with itself is 0.
- Ask whether the two pieces are distinguishable by the combined value alone.
Show that h(a) XOR h(b) equals h(b) XOR h(a) by computing both for h(a) = 12 and h(b) = 10. Then state whether a map using this combination could tell apart the key (a, b) from the key (b, a).
Hints
- Write 12 and 10 in binary and compare bit by bit.
- Commutativity means the order of the operands does not change the result.
Key Takeaways
- A combined hash builds a new hash function from existing ones by applying exclusive or to their results, written as h_1(k) XOR h_2(k).
- Exclusive or gives 1 in each bit position where the two inputs differ, so the combined value depends on both inputs.
- Because exclusive or is commutative, swapping the two parts of a key produces the same combined hash.
- Because any value exclusive ored with itself is 0, equal hash values for the two parts of a key always give a combined hash of 0.
- The source calls the approach often very effective, not always effective, so test a combined hash on realistic keys before relying on it.