Hash Functions

Imagine building a symbol table for a compiler. It must look up thousands of variable names. Or imagine a cache that must locate a web object among billions of possible URLs. Storing every possible key in an array indexed directly by its value is impossible. The universe of keys is astronomically larger than the memory available. Yet we still want near-instant lookup, insertion, and deletion. The tool that resolves this tension is the hash function. It is a deterministic map that compresses a huge, unstructured key space down to a small range of array indices. Hash functions are the invisible machinery behind nearly every fast lookup system in computing. This includes language runtimes' dictionaries and sets, database indexes, distributed caches, content-addressed storage, routers doing packet classification, and cryptographic fingerprinting. Their design determines whether a hash table behaves like a dream — constant-time operations. Or it degrades into a nightmare of linked-list-like linear scans. Getting hashing right is a surprisingly subtle problem. A function that looks "random enough" by eye can still be adversarially exploitable. This can cause catastrophic collisions. This article isolates the hash function itself. It is treated separately from the collision-resolution schemes (Hash Tables and Direct Addressing, Separate Chaining Collision Resolution, Open Addressing Collision Resolution) that consume its output. We study the two classical deterministic constructions — the division method and the multiplication method. Then we study the theoretical framework that explains why randomized families of hash functions give provable guarantees. These families are known as universal hashing. They offer average-case guarantees even against adversarial inputs. Underlying all analysis is an idealization called the simple uniform hashing assumption. We state this assumption precisely and use it honestly. We flag exactly where it does and does not hold in practice. By the end, you will be able to design a hash function for a given key space. You will be able to reason about why a particular table size is a poor or good choice. You will understand the probabilistic guarantee that universal hashing offers. This is the same guarantee that protects real systems from algorithmic complexity attacks.

Key takeaways
  • A hash function maps keys from a huge universe U to a small range {0, …, m−1} of table slots, enabling compact storage.
  • The division method uses `h(k) = k mod m`; it is fast but sensitive to bad choices of m (avoid powers of 2 with non-random keys).
  • The multiplication method uses the fractional part of `k·A`, is less sensitive to m, and underlies Knuth's multiplicative hashing.
  • Simple uniform hashing is the idealizing assumption that each key hashes independently and uniformly over all slots — real functions only approximate this.
  • Universal hashing picks a function at random from a family where any two distinct keys collide with probability at most 1/m, giving expected-case guarantees against any input, even adversarial ones.
  • Hash functions are the foundation beneath Hash Tables and Direct Addressing and every collision-resolution strategy, and they reappear inside Tries-adjacent structures like hash tries and Bloom filters.
The core insight

A good hash function turns an enormous, structured key space into indices that behave as if chosen independently and uniformly at random, so that collisions become rare accidents rather than exploitable patterns.

The idea

A hash function is a function

h:U→{0,1,…,m−1}h : U \to \{0, 1, \dots, m-1\}

mapping a universe UU of possible keys (integers, strings, records — anything) to a small integer range of size mm, the number of slots in a hash table. Because ∣U∣|U| is typically vastly larger than mm, the pigeonhole principle guarantees that distinct keys will sometimes map to the same slot; this is a collision. Hash functions do not eliminate collisions — that is provably impossible when ∣U∣>m|U| > m — but a good one makes collisions rare and, crucially, unpredictable to anyone trying to trigger them deliberately. Before hashing, keys are usually reduced to non-negative integers via some canonical encoding (e.g., treating a string as a number in a large base, or interpreting bytes as an integer). This article assumes that step is done and focuses on the map from integers to slot indices. Two deterministic constructions dominate practice: the division method, h(k)=k  mh(k) = k \bmod m, and the multiplication method, which uses the fractional part of kAkA for a well-chosen constant AA. Both are cheap to compute — a handful of arithmetic operations — which matters because the hash function is evaluated on every table operation. The theoretical backbone for analyzing these (and any) hash functions is the simple uniform hashing assumption (SUHA): each key is equally likely to hash to any of the mm slots, independently of where other keys hash. Under SUHA, standard hash table analyses show expected O(1)O(1) operations. SUHA is an idealization no fixed deterministic function can truly satisfy against an adversary who knows the function, because for any fixed hh there exists a set of keys that all collide. This gap between idealization and reality motivates universal hashing: instead of committing to one fixed function, the table's implementation picks a function at random, at initialization time, from a carefully designed family H\mathcal{H}. As long as the family is universal — any two distinct keys collide under a randomly chosen h∈Hh \in \mathcal{H} with probability at most 1/m1/m — SUHA-like guarantees hold in expectation over the algorithm's own randomness, for any fixed sequence of keys, even one designed by an adversary who does not know which hh was drawn.

How it works

The division method. Given table size mm, define

h(k)=k  m.h(k) = k \bmod m.

This requires one modulus operation — extremely fast on real hardware. Its Achilles' heel is that it is only as "random-looking" as the low-order structure of mm allows. If m=2pm = 2^p, then h(k)h(k) depends only on the low pp bits of kk, ignoring all higher-order bits entirely; keys that share low bits (common with, e.g., pointers aligned to word boundaries, or IDs padded with trailing zeros) collide systematically. The standard fix is to choose mm to be a prime not close to a power of 2 or 10, which spreads dependence across all bits of typical keys.

The multiplication method. Choose a constant AA with 0<A<10 < A < 1. Define

h(k)=⌊m⋅(kA  1)⌋,h(k) = \lfloor m \cdot (kA \bmod 1) \rfloor,

where kA  1kA \bmod 1 denotes the fractional part of kAkA. Intuitively: multiply the key by AA, throw away the integer part, keep the fractional part (a number in [0,1)[0,1)), and scale it up to the range [0,m)[0, m). This method is much less sensitive to the choice of mm — powers of 2 are fine, which is convenient because computing m⋅xm \cdot x for m=2pm = 2^p is just a bit shift. Knuth recommends

A≈5−12≈0.6180339887…A \approx \frac{\sqrt5 - 1}{2} \approx 0.6180339887\ldots

the reciprocal of the golden ratio, because multiplying by it tends to scatter the resulting fractional parts fairly evenly, avoiding clumping patterns that some values of AA produce with structured input. In practice, on a machine with ww-bit words, this is implemented entirely with integer arithmetic: pick an odd integer constant ss approximating A⋅2wA \cdot 2^w, compute the ww-bit product k⋅sk \cdot s (allowing overflow to 2w2w bits), and extract the top pp bits of the low ww-bit half (for m=2pm = 2^p) as the hash. This avoids floating point entirely and is extremely fast — a single integer multiply and a shift.

Universal hashing. Fix a prime pp larger than any key value. A classical universal family (due to Carter and Wegman) is

H={ha,b(k)=((ak+b)  p)  m  :  a∈{1,…,p−1}, b∈{0,…,p−1}}.\mathcal{H} = \Big\{ h_{a,b}(k) = \big((ak + b) \bmod p\big) \bmod m \;:\; a \in \{1,\dots,p-1\},\ b \in \{0,\dots,p-1\} \Big\}.

To use it: pick aa and bb uniformly at random once, when the table is created (not per key), then use the resulting fixed function ha,bh_{a,b} for every operation on that table instance. The randomness is in which function gets chosen, not in re-randomizing per lookup. ```text // One-time setup when the hash table is constructed: UNIVERSAL-HASH-INIT(m, p): a ← random integer in [1, p-1] b ← random integer in [0, p-1] return (a, b) // defines h_{a,b} for the table's lifetime // Evaluated on every insert / search / delete: UNIVERSAL-HASH(k, a, b, p, m): return ((a k + b) mod p) mod m ``` ```text // Division method DIVISION-HASH(k, m): return k mod m // Multiplication method (Knuth's constant, integer arithmetic on w-bit words) // s is a precomputed odd integer approximating A 2^w, m = 2^p MULTIPLICATION-HASH(k, s, w, p): product ← (k * s) mod 2^w // keep only the low w bits of the product return product >> (w - p) // top p bits of that low half ``` The key structural fact used later in the correctness argument: for ha,b∈Hh_{a,b} \in \mathcal{H} and two distinct keys k1≠k2k_1 \ne k_2 both less than pp, the map k↦(ak+b)  pk \mapsto (ak+b) \bmod p is a bijection on {0,…,p−1}\{0,\dots,p-1\} for each fixed nonzero aa — this is where the collision-probability bound comes from.

Figure
From key universe to table slots: division vs. multiplication vs. universal hashing
Universe U of keys → hash function → m slots Universe U "alice", 40217, 0xFF00, ... h(k) division: k mod m mult.: floor(m·frac(kA)) universal: ((ak+b) mod p) mod m table[0..m-1] 0 1 2 3 4 5 6 Two distinct keys colliding in the same slot key k₁ = "cat" key k₂ = "dog" slot 3 Universal hashing: probability of this collision ≤ 1/m over the random choice of h Fixed h (division or multiplication) has no such guarantee against adversarial keys

Complexity & correctness

Time cost of the hash function itself. Both the division method and the multiplication method compute h(k)h(k) in O(1)O(1) time — one modulus/multiply plus a few shifts, independent of key size (for fixed-width machine integers) or logarithmic in key size for arbitrary-precision keys. This constant-time evaluation is what allows hash table operations (insertion, search, deletion) to be O(1)O(1) on average, since each operation performs a small constant number of hash evaluations plus work proportional to the resulting slot's chain length or probe sequence — topics developed fully in Separate Chaining Collision Resolution and Open Addressing Collision Resolution.

Why SUHA gives O(1)O(1) expected time. Under simple uniform hashing, each of nn keys lands independently and uniformly in one of mm slots. For separate chaining, the expected chain length at any slot is the load factor α=n/m\alpha = n/m, so an unsuccessful search expects to examine Θ(1+α)\Theta(1 + \alpha) elements: Θ(1)\Theta(1) for computing the hash, and Θ(α)\Theta(\alpha) for scanning the expected chain length. Keeping α=O(1)\alpha = O(1) (e.g., resizing the table when α\alpha exceeds a constant threshold) yields expected O(1)O(1) operations. The catch, stated honestly: SUHA is unverifiable and generally false for any fixed hash function, because for any deterministic hh, an adversary who knows hh can compute mm or more keys that all hash to the same slot — simply invert hh on a single target slot value repeatedly. This is not a hypothetical: it is the basis of real denial-of-service attacks against naively hashed web-request parameters and form fields, which is precisely why security-sensitive services randomize their hash seed.

Why universal hashing repairs this. Universal hashing sidesteps the adversary problem by making the function itself the random variable, chosen after (or independent of) the adversary's choice of keys. Formally, a family H\mathcal{H} is universal if for every pair of distinct keys k1≠k2k_1 \ne k_2,

Pr⁡h∼H[h(k1)=h(k2)]≤1m.\Pr_{h \sim \mathcal{H}}\big[h(k_1) = h(k_2)\big] \le \frac{1}{m}.

Proof that the Carter–Wegman family is universal. Fix distinct keys k1≠k2k_1 \neq k_2 in {0,…,p−1}\{0, \ldots, p-1\}, with pp prime and p>p > every key and p≥mp \geq m. Define r1=(ak1+b)  pr_1 = (ak_1 + b) \bmod p and r2=(ak2+b)  pr_2 = (ak_2 + b) \bmod p for aa drawn uniformly from {1,…,p−1}\{1, \ldots, p-1\} and bb uniformly from {0,…,p−1}\{0, \ldots, p-1\}. 1. For any fixed nonzero aa, the map k↦(ak+b)  pk \mapsto (ak + b) \bmod p is injective as bb ranges over Zp\mathbb{Z}_p: solving r1≡ak1+br_1 \equiv ak_1 + b and r2≡ak2+b(modp)r_2 \equiv ak_2 + b \pmod p simultaneously gives r1−r2≡a(k1−k2)(modp)r_1 - r_2 \equiv a(k_1 - k_2) \pmod p. Since pp is prime and k1≠k2(modp)k_1 \ne k_2 \pmod p, (k1−k2)(k_1 - k_2) is invertible mod pp, so for each nonzero aa there is exactly one value of r1−r2r_1 - r_2, hence — ranging over all (a,b)(a,b) pairs uniformly — the pair (r1,r2)(r_1, r_2) is uniformly distributed over all p(p−1)p(p-1) ordered pairs of distinct values in Zp×Zp\mathbb{Z}_p \times \mathbb{Z}_p. 2. So Pr⁡[r1=r2]=0\Pr[r_1 = r_2] = 0 exactly (they are forced distinct whenever a≠0a \neq 0), and (r1,r2)(r_1, r_2) is uniform over the p(p−1)p(p-1) distinct ordered pairs. 3. We care about h(k)=r  mh(k) = r \bmod m, not rr itself. Fixing r1r_1, the number of values r2∈Zp∖{r1}r_2 \in \mathbb{Z}_p \setminus \{r_1\} with r2≡r1(modm)r_2 \equiv r_1 \pmod m is at most ⌈p/m⌉−1≤(p−1)/m\lceil p/m \rceil - 1 \le (p - 1)/m (a standard counting bound on residues mod mm within a range of size pp). Dividing by the p−1p - 1 available choices of r2r_2 gives

Pr⁡[h(k1)=h(k2)]≤(p−1)/mp−1=1m.\Pr[h(k_1) = h(k_2)] \le \frac{(p-1)/m}{p - 1} = \frac{1}{m}.

This establishes universality. ■\blacksquare

Consequence for expected chain length. With a universal family, for any fixed sequence of nn keys (chosen with no knowledge of a,ba, b), and a random h∈Hh \in \mathcal{H} used to build the table, the expected number of collisions any single key experiences with the other n−1n-1 keys is at most (n−1)/m(n-1)/m — by linearity of expectation, summing the ≤1/m\le 1/m pairwise collision probability over all other keys. So expected chain length is O(1+α)O(1 + \alpha), matching the SUHA guarantee, but now proved rigorously for any input, with the randomness coming only from the table's own coin flips, not from any assumption about the keys.

Space. A hash function itself uses O(1)O(1) extra space (a constant number of parameters: mm, or AA/ss, or (a,b,p)(a,b,p)). This is separate from the Θ(m+n)\Theta(m + n) space used by the resulting table with chaining, or Θ(m)\Theta(m) with open addressing.

Honest tradeoffs. The division method is simplest and fastest to reason about but forces careful, often prime, choices of mm. The multiplication method tolerates power-of-two table sizes (convenient for fast modular arithmetic and cache-line alignment) but the quality of scattering depends on the choice of constant AA, and poor choices can still produce visible patterns. Universal hashing gives the strongest theoretical guarantee — worst-case-input resilience — but at the cost of a slightly more expensive hash evaluation (a multiply, an add, and two modular reductions) and the operational requirement of generating and storing fresh random parameters per table instance.

Worked example

Take table size m=8m = 8 and keys representing user IDs: k∈{18,41,22,44,59,32}k \in \{18, 41, 22, 44, 59, 32\}.

Division method, h(k)=k  8h(k) = k \bmod 8:

key kkk  8k \bmod 8slot
1818 = 2·8 + 22
4141 = 5·8 + 11
2222 = 2·8 + 66
4444 = 5·8 + 44
5959 = 7·8 + 33
3232 = 4·8 + 00

With m=8=23m=8=2^3, the hash only depends on k  8k \bmod 8, i.e., the low 3 bits of kk. Here it happens to scatter nicely since the low bits of these particular keys vary. But notice: any key set that's arithmetic with common stride 8 (e.g., IDs 8,16,24,32,…8, 16, 24, 32, \dots — very plausible if IDs are assigned in blocks per shard) would all collide in slot 0. This is the systematic weakness of power-of-two divisors.

Multiplication method, using A=0.6180339887A = 0.6180339887, m=8m = 8: Compute kA  1kA \bmod 1, then multiply by 8 and floor.

key kkkAkAfrac(kAkA)8⋅8 \cdot fracslot = ⌊·⌋
1811.12460.12460.99680
4125.3390.3392.7122
2213.5970.5974.7764
4427.1940.1941.5521
5936.4640.4643.7123
3219.7770.7776.2166

Now the arithmetic-progression weakness is broken: 18 and 32 no longer collide, and the mapping depends on the full magnitude of kk, not just its low bits — because multiplying by an irrational-like constant mixes all the bits of kk into the fractional part.

Universal hashing, with p=101p = 101 (a prime bigger than all keys), m=8m = 8, and a randomly drawn (a,b)=(37,5)(a, b) = (37, 5): ha,b(k)=((37k+5)  101)  8h_{a,b}(k) = ((37k + 5) \bmod 101) \bmod 8.

key kk37k+537k+5   101\bmod\ 101   8\bmod\ 8 (slot)
18671671 − 6·101 = 6565 mod 8 = 1
4115221522 − 15·101 = 77
22819819 − 8·101 = 113
4416331633 − 16·101 = 171
5921882188 − 21·101 = 673
3211891189 − 11·101 = 786

Here 18 and 44 collide (both slot 1), and 22 and 59 collide (both slot 3) — collisions do happen, as guaranteed to happen sometimes even under universality. The point of universal hashing is not to eliminate this instance's collisions; it is that if a different (a,b)(a,b) pair had been drawn, a different pair of keys would likely collide instead, and no fixed adversarial key set can force collisions across all draws of (a,b)(a, b). Rerunning with (a,b)=(63,12)(a,b) = (63, 12) would scatter these same six keys entirely differently.

Common pitfalls & traps

PitfallWhy it happensFix
Using m=2pm = 2^p with the division method on keys with structured low bits`k mod m` depends only on low pp bits when mm is a power of 2Choose mm prime and not close to a power of 2 or 10, or switch to the multiplication method
Treating SUHA as literally true for a fixed, publicly known hash functionSUHA is an idealization; any fixed hh has adversarial worst-case key setsUse universal hashing (randomize per table instance) in adversarial-facing systems
Reusing the same (a,b)(a, b) seed across all instances of a hash table typeDefeats the purpose of universal hashing — an attacker can learn the fixed seed and target itRandomize (a,b)(a,b) per table instance/process, ideally from a secure RNG
Picking a poor multiplicative constant AA (e.g., rational with small denominator)Produces visible periodic patterns instead of scatteringUse Knuth's A≈(5−1)/2A \approx (\sqrt5-1)/2 or another constant with no short repeating binary/decimal pattern
Forgetting that hashing strings/composite keys requires a separate reduction step before applying hhThe division/multiplication methods assume an integer input; naive string-to-int encodings can introduce biasUse a well-mixed integer encoding (e.g., polynomial rolling hash) before applying the table hash function
Confusing hash functions with cryptographic hash functionsBoth "hash," but table hash functions need only speed and statistical spread, not preimage resistanceUse fast non-cryptographic hashes for tables; reserve cryptographic hashes (SHA-256, etc.) for security-critical fingerprinting

Pioneers & history

The division and multiplication methods for hashing were systematized and popularized in Donald Knuth's The Art of Computer Programming, which analyzed their statistical behavior in depth and recommended the golden-ratio constant for multiplicative hashing. The theoretical leap — proving that randomizing over a family of hash functions gives guarantees independent of the input — came from J. Lawrence Carter and Mark N. Wegman, who introduced universal hashing in 1977, founding a rigorous probabilistic theory of hash function design that underlies modern hash table implementations, Bloom filters, and randomized data-structure security.

Origins · 1 minMark N. Wegman and how this was discovered
MN
Mark N. Wegman
born 1946 · IBM Research; co-inventor of universal hashing with J. Lawrence Carter, connecting randomized algorithms to practical data-structure design

Wegman's work with Carter showed that randomness could be engineered directly into the design of a hash function family, rather than merely hoped for in its output — a shift from "does this function look random" to "what is the provable probability of collision over my own random choice."

History
1968
Donald Knuth analyzes division and multiplication hashing methods in early drafts leading to The Art of Computer Programming, Volume 3.
1977
J. Lawrence Carter and Mark N. Wegman introduce universal classes of hash functions, proving expected-case collision bounds independent of the input.
1979
Carter and Wegman formalize universal hashing further, linking it to applications in fingerprinting and equality testing.
2003
Universal hashing is used to design provably robust hash tables resistant to algorithmic complexity attacks in security-sensitive systems.
2011–2012
Publicized denial-of-service vulnerabilities in several mainstream language runtimes' hash table implementations drive widespread adoption of randomized (seeded) hash functions in production systems.

Where this matters

Hash functions are the load-bearing layer beneath Hash Tables and Direct Addressing, and every collision-resolution strategy — Separate Chaining Collision Resolution and Open Addressing Collision Resolution — depends entirely on the quality of the underlying hash function for its average-case performance claims to hold. Language runtimes (Python's `dict`, Java's `HashMap`, Go's `map`) all randomize their internal string hash seed at process startup specifically to defend against algorithmic complexity attacks, a direct engineering application of universal hashing's guarantee. Database systems use hash functions for hash joins, hash partitioning, and hash indexes, where a poor hash function can cause severe data skew across partitions. Distributed systems use hashing for consistent hashing in load balancers and distributed caches (e.g., content delivery networks and key-value stores), where the hash function determines which server or shard owns a given key. Networking hardware uses hash functions for flow classification and load balancing across multiple links. Bloom filters and their relatives, and hash-based variants of Tries (hash tries, hashed compressed tries), rely on well-distributed hash functions to keep false-positive rates and lookup times low. Cryptographic contexts use a stricter cousin of these ideas — hash functions with preimage and collision resistance — but the combinatorial core (mapping a large universe into a small range while controlling collisions) is the same idea this article develops in its simplest, fastest, non-adversarial-by-default form.

Ask the tutor: How does a hash function's design choice affect which collision-resolution scheme — chaining or open addressing — performs better in practice?

Why can't we just make the hash function itself perfectly collision-free?

A collision-free (injective) function from a universe UU into a smaller range {0,…,m−1}\{0,\dots,m-1\} is mathematically impossible whenever ∣U∣>m|U| > m, by the pigeonhole principle — some two keys must map to the same slot. This is true no matter how clever the function is. Hashing therefore accepts collisions as inevitable and instead focuses on two goals: making collisions statistically rare for typical inputs (via good scattering, as in the multiplication method) and making them unpredictable to an adversary who wants to force many keys into one slot (via universal hashing's randomization). Perfect hashing exists only for a known, fixed key set decided in advance, which is a separate, more specialized technique not applicable to dynamic tables receiving arbitrary future keys.

Is universal hashing actually used in real systems, or is it mostly theoretical?

It is genuinely used, though often in adapted forms. Several mainstream language runtimes randomize their string hash function's seed at process startup — effectively drawing one function from a family at random — specifically to prevent algorithmic complexity attacks where an adversary sends inputs engineered to collide under a known, fixed hash function. The Carter–Wegman construction itself, or close relatives, appears in networking hardware for flow hashing, in hash table libraries hardened against denial-of-service, and in randomized data structure research (e.g., analyses of cuckoo hashing and Bloom filters lean on universal or near-universal hash families for their probabilistic guarantees).

How do you choose the table size m in practice?

For the division method, choose mm prime, and ideally not close to a power of 2 or a power of 10, since real-world key sets often have structure aligned to those bases (memory addresses, decimal identifiers). For the multiplication method, mm is commonly chosen as a power of 2, since extracting the top bits of a multiplied word is a fast bit-shift operation, and the multiplication step itself does the work of scattering that the division method would otherwise need a prime for. In both cases, mm is also chosen dynamically as the table resizes to keep the load factor α=n/m\alpha = n/m bounded by a small constant, which is what keeps operations expected O(1)O(1) regardless of which hashing method is used.

What is the difference between a hash function for hash tables and a cryptographic hash function like SHA-256?

Both compress a large input space into a smaller, fixed-size output, but their design goals differ sharply. A table hash function needs speed and good statistical distribution over typical or randomized inputs; it need not resist a determined adversary trying to invert it or find two specific colliding inputs by brute-force computation. A cryptographic hash function must additionally be resistant to preimage attacks (hard to find an input producing a given output), second-preimage attacks, and collision attacks (hard to find any two inputs that collide) even under heavy adversarial computational effort. Cryptographic hashes are far more expensive per evaluation, so using one inside a hot-path hash table would needlessly sacrifice performance; conversely, using a plain table hash function for security-critical fingerprinting would be unsafe.

Does universal hashing eliminate the need to resize the hash table as it grows?

No — universal hashing addresses the distribution of keys across slots, not the total count of keys relative to slots. As more keys are inserted, the load factor α=n/m\alpha = n/m grows regardless of how well-distributed the hashing is, and once α\alpha becomes large, expected chain lengths or probe sequence lengths grow proportionally, degrading performance even under perfect uniform hashing. Resizing (rehashing into a larger table once α\alpha crosses a threshold) remains necessary; universal hashing simply guarantees that, for whatever load factor currently holds, the keys are spread close to evenly across the current table's slots, protecting against adversarial clustering rather than against natural growth.

Why does the proof of universality require p to be prime?

Primality is what makes (k1−k2)(k_1 - k_2) invertible modulo pp whenever k1≠k2k_1 \ne k_2 and both are less than pp. That invertibility is exactly what forces the map a↦a(k1−k2)  pa \mapsto a(k_1-k_2) \bmod p to be a bijection on nonzero residues, which in turn guarantees that the pair (h(k1),h(k2))(h(k_1), h(k_2)) before the final mod-mm reduction is uniformly distributed over all distinct ordered pairs in Zp2\mathbb{Z}_p^2. If pp were composite, some nonzero values of k1−k2k_1 - k_2 would share a common factor with pp, breaking invertibility, and the resulting distribution over (r1,r2)(r_1, r_2) would no longer be uniform — the clean 1/m collision bound would no longer follow from the counting argument.

Can a hash function be "too random," in a way that hurts performance?

Not in terms of collision behavior, but there is a cost tradeoff. Perfectly uniform-looking output typically requires more mixing work (more multiplications, more bit rotations) than a cheap function like plain division, so an extremely elaborate mixing function can slow down every single table operation even though it improves worst-case collision resistance. In most non-adversarial applications, a simple, fast multiplication-method hash is perfectly adequate; the extra cost of full universal hashing or cryptographic-strength mixing is worth paying only when inputs may be adversarial, such as public-facing services parsing untrusted keys.

Before this

Where this leads