Answers · Making it fast enough
How does a hash function turn a word into an array index?
In shortA hash function reads the word as bytes, mixes them into one large number, then takes the remainder after dividing by the table size to get an index from zero to size minus one.
A hash function turns the word into bytes, mixes those bytes into a single large whole number, and then reduces that number modulo the table size. The result is a whole number between and , where is the number of slots, so it can be used directly as an array index. Nothing about this requires a stored list of every word and its position: the index is recomputed from the bytes each time the word is looked up. The remainder is the part that makes the answer bounded, and it is also the part that makes collisions unavoidable.
- the key, read as bytes
- the large whole number computed from those bytes
- the number of positions in the table
- the position, from zero to one less than m
From bytes to a number
Take the word cat. In ASCII its bytes are 99, 97 and 116. A toy hash that treats the bytes as digits in base 31 computes . Since , that is . With a table of slots, , so cat goes to index 262. With a table of slots, , so the same hash goes to index 6. The hash number did not change; only the table size changed.
The second step is just division with remainder. Any whole number, however many digits it has, leaves a remainder in the range to when divided by . That is why the method works for a word of any length without a lookup table: the hash can be enormous, but the remainder is guaranteed to be a legal index. When is a power of two, the remainder keeps only the lowest bits of the hash, which is why the mixing step has to affect those bits and not just the early bytes.
The natural guess is that a good hash must encode every word uniquely, or that somewhere there is a table mapping each word to its slot. Neither is what happens. A hash is a function from a much larger set of possible keys to a much smaller set of positions, so it cannot be one to one. With eight lower-case letters there are possible words. In a table of one million slots, that is possible words per slot on average. The count is not a defect of a particular hash; it is arithmetic. A hash that looked perfect would have to be storing the word somewhere, and the whole point is to avoid that.
Collisions show up even for the modest words. A bad hash that adds the bytes gives listen, silent, enlist and tinsel the same sum: 108 + 105 + 115 + 116 + 101 + 110 = 655. With all four get index 655. A mixing hash multiplies and combines each byte so that changing the order changes the result, and the same four words scatter. The difference is not the remainder; it is whether the large number depends on all the bytes in their order.
Why collisions arrive early
Collisions are not a late-stage problem. In a table of one million slots, the chance that no two of keys share a slot is roughly . Setting that to one half gives . With , , whose square root is about 1,177. At , , and is about 0.5. So a table that is only 0.1177 percent full already has an even chance of a collision.
Knowing the index is therefore only the first half of a lookup. Arriving at slot 262 does not prove that the word there is cat, because another word may have hashed to 262. The stored key must be compared in full once the slot is reached. The hash narrows the search to one slot; the comparison confirms the item.
The method also has limits that follow from the same arithmetic. A hash gives an index, not an identity, so it cannot answer whether a key is present without the key and a comparison. If the table grows too full, or if the hash is poor and clusters keys into a narrow band, the slot holds a crowd and the constant-time hope fails. If the table size changes, every index may change, because the remainder is taken modulo a different . And if two keys have the same bytes, no hash can tell them apart; it only sees bytes.
Worth keeping
- A hash of a word is computed from its bytes, so the same word gives the same index in the same table without any per-key index table.
- Taking a remainder modulo m always lands in 0 to m-1, and if m is a power of two it keeps only the lowest bits of the hash.
- Because there are more possible keys than positions, some two keys must share an index, so a lookup must compare the full key after reaching the slot.
Where this goes next
This question is one step of Finding It Instantly, a course that works through its subject from the beginning. The page that teaches this part of it is Turning a Key Into an Address, and it reads the whole way through whether or not you have an account.
What an account changes is the practice: problems standing under every idea, marked with a reason rather than a tick, in a copy of the course that is yours.
Read that pageThe whole courseOther questions people ask about this
- How can you check whether something is in a set without storing every item?Record a few hashed marks per item in a bitmap; all marks set means probably present, any mark clear means certainly absent.
- Why can a hash table find something without scanning everything else?A hash table does not search its contents faster. It computes where the item must sit from the key itself, so the number of stored items never enters the calculation.
- Why can't a hash table give me the next key in order?Because a good hash deliberately scatters neighbouring keys, so the table keeps no record of which key comes after which; finding the next key means reading every entry.
- Why does a hash table need to grow before it is completely full?Because what makes lookup fast is how full the table is, not how big it is: near full, a lookup turns into a scan well before the last slot is taken.
- Why doesn't breadth-first search give the cheapest path once the links have different costs?Breadth-first search counts links rather than costs, so it can return a route with fewer links that is more expensive than a longer route.
- Why is O(2n) treated the same as O(n)?Because it describes how cost grows rather than exact cost, doubling every cost does not change the growth class.