Algorithm Search⌘K

Proofs: Cuckoo hashing

These are the human proofs of Cuckoo hashing, at full length.

Cuckoo hashing keeps its invariant and implements a dictionary full

This proof shows: correctness, termination.

We write P(T)P(T) for the set of pairs in the cells of T1T_1 and T2T_2. The invariant is (C1) each pair (k,v)∈P(T)(k, v) \in P(T) is in T1[h1(k)]T_1[h_1(k)] or T2[h2(k)]T_2[h_2(k)]; (C2) no key occurs in two cells; (C3) n=∣P(T)∣n = |P(T)|; (C4) r≥(1+ε)nr \ge (1 + \varepsilon) n.

Theorem. Start from empty tables with r≥1r \ge 1 and 0<ε≤10 < \varepsilon \le 1 and apply any sequence of CUCKOO-INSERT, CUCKOO-SEARCH and CUCKOO-DELETE. Each operation that ends leaves (C1) to (C4) true, and P(T)P(T) equal to the map that the same sequence gives on an abstract dictionary. Each search returns the value of its key in that map, or NIL. If the hash functions of line L16 are drawn independently from a distribution under which a fixed set of at most r/(1+ε)r / (1 + \varepsilon) keys can be placed with probability at least q>0q > 0, then each insert ends with probability 1.

Proof. Empty tables satisfy (C1) to (C4) with n=0n = 0, and they hold the empty map.

Step 1 (the two cells are enough). Assume (C1). A pair with key kk is in T1[h1(k)]T_1[h_1(k)] or T2[h2(k)]T_2[h_2(k)]. So a test of these two cells finds every pair with key kk, and if both tests fail, kk is not a key.

Step 2 (search). By Step 1 and (C2), lines L1 and L2 return the value of the only pair with key kk, if there is one, and line L3 returns NIL otherwise. Search changes nothing.

Step 3 (delete). By Step 1 and (C2), lines L13 and L14 clear the only cell with key kk, if there is one, and lower nn by 1. The other pairs do not move, so (C1) and (C2) hold, (C3) holds, and (C4) holds because nn decreased. The map loses exactly kk.

Step 4 (the eviction walk). Consider lines L6 to L11 started with a pair x0x_0 whose key is not a key of P(T)P(T), on tables that satisfy (C1) and (C2). Let Q=P(T)∪{x0}Q = P(T) \cup \{x_0\}. We claim that before each of lines L8 and L10:

(W) xx is a pair of QQ, and P(T)=Q∖{x}P(T) = Q \setminus \{x\}. Each pair of P(T)P(T) is in one of its two cells, and the keys of QQ are distinct.

At line L6, x=x0x = x_0 and P(T)=Q∖{x0}P(T) = Q \setminus \{x_0\}, so (W) holds. Line L8 swaps xx with the content cc of T1[h1(key(x))]T_1[h_1(\text{key}(x))]. After the swap, the old xx is in its own first cell, and the new xx is cc. If cc is NIL, then P(T)=QP(T) = Q, each pair is in one of its cells, and line L9 adds 1 to nn. If cc is a pair, it was in P(T)P(T), it is now out of the tables, and P(T)=Q∖{c}P(T) = Q \setminus \{c\}, so (W) holds again before line L10. Line L10 is the same with T2T_2 and h2h_2. The keys of QQ do not change in a swap, so they stay distinct. Hence if the walk returns at line L9 or L11, then P(T)=QP(T) = Q, (C1) and (C2) hold, and nn has grown by 1. If it does not return after MaxLoop rounds, then by (W) the tables hold Q∖{x}Q \setminus \{x\}, each pair in one of its cells.

Step 5 (rehash). Line L15 sets S=P(T)∪XS = P(T) \cup X. Line L16 empties the tables and sets n=0n = 0. Line L17 runs the walk for each pair of SS in turn. The keys of SS are distinct, so each walk starts with a key that is not yet in the tables, and Step 4 applies. If all walks return, then P(T)=SP(T) = S, with (C1) and (C2), and n=∣S∣n = |S| by (C3). If one walk fails, line L17 goes back to line L16 and starts again from the same SS. So when REHASH returns, P(T)=SP(T) = S and (C1) to (C3) hold.

Step 6 (insert). If line L4 finds key kk, it replaces the value in place. No pair moves and the map is the old map with kk mapped to vv. Else kk is not a key, by Step 1. Line L5 keeps P(T)P(T) by Step 5, and after it r≥(1+ε)(n+1)r \ge (1 + \varepsilon)(n + 1): either the test failed, or rr was doubled from a value r0≥(1+ε)nr_0 \ge (1 + \varepsilon) n. If n≥1n \ge 1, then 2r0≥2(1+ε)n≥(1+ε)(n+1)2 r_0 \ge 2(1 + \varepsilon) n \ge (1 + \varepsilon)(n + 1). If n=0n = 0, then 2r0≥2≥1+ε2 r_0 \ge 2 \ge 1 + \varepsilon, because r0≥1r_0 \ge 1 and ε≤1\varepsilon \le 1. By Step 4 with x0=(k,v)x_0 = (k, v), either the walk places all of Q=P(T)∪{(k,v)}Q = P(T) \cup \{(k, v)\}, or it leaves one pair xx outside with P(T)=Q∖{x}P(T) = Q \setminus \{x\}. In the second case, line L12 calls REHASH with X={x}X = \{x\}, and by Step 5 the result is P(T)=QP(T) = Q. In both cases n=∣Q∣n = |Q| and r≥(1+ε)nr \ge (1 + \varepsilon) n, so (C1) to (C4) hold and the map is the old map plus (k,v)(k, v).

Step 7 (termination). Each walk has at most MaxLoop rounds, so it ends. REHASH repeats line L16 until one draw of h1,h2h_1, h_2 places all of SS. The set SS has at most r/(1+ε)r / (1 + \varepsilon) keys by (C4), so each draw succeeds with probability at least qq, independently of the earlier draws. The probability that the first tt draws all fail is at most (1−q)t(1 - q)^t, which tends to 0. So REHASH ends with probability 1, after an expected number of at most 1/q1/q draws.

By induction on the number of operations, using Steps 2, 3 and 6, the theorem holds. ■\blacksquare

Back to Cuckoo hashing