Skip to content
Part IA Lent Term

Hash Functions

Definition and Role

A hash function h:U→{0,1,…,m−1}h: U \to \{0, 1, \ldots, m-1\} maps keys from the universe UU to mm slots in a hash table. A good hash function satisfies:

  1. Fast to compute: Θ(1)\Theta(1) time per call. If the hash function itself is slow, the O(1)O(1) promise of hash tables is hollow (hidden large constant factors).
  2. Uniform distribution: each of the mm slots is equally likely for a random key. This is the simple uniform hashing assumption, which makes expected-case analysis possible.
  3. Deterministic: the same key always maps to the same slot (unless using universal hashing where the function is chosen at random at runtime).

Handling Non-Integer Keys

Hash functions ultimately produce integers. For non-integer keys (strings, objects), the key must first be converted to an integer. A common approach for strings: treat each character as a digit in a large base (e.g. 256 for ASCII), producing a polynomial:

h(s)=(∑i=0∣s∣−1s[i]⋅B∣s∣−1−i) mod mh(s) = \left( \sum_{i=0}^{|s|-1} s[i] \cdot B^{|s|-1-i} \right) \bmod m

The choice of base BB and modulus mm affects distribution quality.

Division Method

h(k)=k mod mh(k) = k \bmod m

This is fast (one modular division), but the choice of mm is critical:

  • Avoid powers of 2 (e.g. m=2pm = 2^p): then h(k)h(k) depends only on the lowest pp bits of kk, ignoring higher-order bits. This produces poor distribution if keys share low-bit patterns.
  • Prefer mm as a prime number not too close to a power of 2. A prime modulus ensures all bits of kk influence the hash value.

Example: For m=10m = 10, h(23)=3h(23) = 3, h(47)=7h(47) = 7, h(13)=3h(13) = 3 (collision). For m=11m = 11 (prime): h(23)=1h(23) = 1, h(47)=3h(47) = 3, h(13)=2h(13) = 2 (no collision).

Multiplication Method

h(k)=⌊m(kA mod 1)⌋h(k) = \lfloor m (kA \bmod 1) \rfloor

where AA is a constant in (0,1)(0, 1). Knuth recommends A≈(5−1)/2≈0.6180339887A \approx (\sqrt{5} - 1)/2 \approx 0.6180339887 (the golden ratio conjugate).

The idea: multiply kk by AA, extract the fractional part (kA mod 1)(kA \bmod 1), and scale by mm. The fractional part distributes keys uniformly across [0,1)[0, 1) regardless of patterns in kk. The method works for any mm, which is convenient when resizing tables.

Range Reduction

Modern hash functions typically output a 32-bit or 64-bit unsigned integer (e.g. 0..2^32-1). This raw output is too large to use as a table index. Range reduction converts it:

index=hraw(k) mod m\text{index} = h_{\text{raw}}(k) \bmod m

This is fast but introduces slight bias unless mm divides the hash output range. In practice, the bias is negligible for well-designed hash functions.

Cryptographic vs Non-Cryptographic Hashing

Cryptographic hash functions (SHA-256, BLAKE3) provide strong guarantees: collision resistance, preimage resistance. They are slow by design and not needed for hash tables.

Non-cryptographic hash functions (MurmurHash, FNV-1a, xxHash) prioritise speed and reasonable distribution. These are the appropriate choice for in-memory data structures. The Cambridge course focuses on the latter.

Low-Entropy Inputs

If keys have low entropy (e.g. single letters, or words differing by one character like ROT, TOT, POT), even a good hash function cannot produce a uniform distribution: there are only 26 possible inputs, so at most 26 distinct hash values. For such inputs, choose domain-specific hash functions that maximise spread.

Summary

MethodFormulaProsCons
Divisionk mod mk \bmod mSimple, fastNeed prime mm
Multiplication⌊m(kA mod 1)⌋\lfloor m(kA \bmod 1) \rfloorWorks for any mmSlightly slower
Range reductionhraw(k) mod mh_{\text{raw}}(k) \bmod mDecouples hash from table sizeMinor bias
Polynomial (strings)∑s[i]⋅Bi mod m\sum s[i] \cdot B^i \bmod mGood for stringsParameter-sensitive
Desired propertyUniform distribution over [0,m−1][0, m-1]