workshop private

← all creations

Evict

viz · created 2026-10-06

Cuckoo hashing drawn as the two claims it actually makes — every lookup reads exactly two slots at any load factor, and the entire cost of that moved into insert, where a chain of displacements either reaches a free slot or goes round forever. Read the table as a graph, one vertex per slot and one edge per key, and the jam stops being bad luck in the insertion order: it is a component holding more keys than it has slots, which no order places.

algorithmscanvas

Every key has exactly two homes — slot h1(key) in table 1, slot h2(key) in table 2 — and a key is in one of its own two homes or it is nowhere. That single invariant is what makes the lookup a constant:

get(key):  look at t1[h1(key)];  look at t2[h2(key)];  done.

Two reads. At a tenth full and at a hair under the limit. No probe sequence to walk, no tombstones to skip, no clustering to measure — and no average, either, which is the part worth sitting with. Linear probing has a mean cost that is fine and a tail that is not; this has no tail, because the second read is the last read. The header counts it on every frame and the number never moves.

Nothing is free, so the question is where the cost went, and all of it went into insert. Writing a key into an occupied home does not go looking for somewhere else to put the newcomer. It puts the newcomer in and throws the resident out, and the resident goes to its other home, which may also be occupied, which starts the same move again. One insert is a chain of displacements that ends when it reaches a free slot — or when it has been going long enough that we stop and call the table unusable.

Watch that cost in the strip along the bottom: one bar per insert, height in kicks, with the load factor drawn over it as a faint line. For most of the run the bars are nothing — the mean is well under one kick. Then the load line crosses ½ and the bars go vertical. The average was never the story.

The jam is structural, and that is the whole piece

Press graph. The two rows stop being tables and become what they always were: 2m vertices, one per slot. Every key is an edge joining its two homes, and storing the keys means orienting every edge — each key pointing at the one slot it occupies — so that no slot is pointed at twice. A connected component with v slots can hold at most v keys, so each component gets one of three verdicts, and the demo paints them:

The key currently in hand is drawn as a dashed edge, which matters more than it sounds: without counting the key that could not get in, a jammed component still reads as merely saturated. Counting it, the picture tips over to red and the verdict line underneath says 4 slots, 5 keys — impossible. The insert did not fail because the order was unlucky. It failed because of that.

Which also says what the escape is. You cannot reorder your way out of a component that holds more keys than slots; you can only get a different graph, which means different hash functions. That is what a rehash is here, and the saturated preset is built to show it: four keys whose homes form a 4-cycle, a few bystanders, then a fifth key whose two homes are a pair already inside the cycle.

Three rules, because the second is what gets written first

About the wall at ½

Two hashes hold a load factor of ½ and no more. That is an asymptotic statement — as the table grows, the probability that a random cuckoo graph is orientable goes to 1 below ½ and to 0 above it — and the demo is honest about the fact that a table of twelve slots is nowhere near asymptotic. The fluctuations are the same size as the table, so a jam at load 0.5 can genuinely be rescued by a different pair of hash functions.

The rehashes slider is that caveat made operable: it is how many fresh hash pairs a jam may try before the run gives up. At 0 the first jam ends it, near enough to ½. Wind it to 12 and the same key stream reaches load 0.875 — not because cuckoo hashing beats its own threshold, but because at this size you can buy enough lottery tickets to find a rare orientable graph. The readout counts the tickets (hash pairs). On a table with a million slots that search never pays, which is why real implementations resize rather than reroll.

Three hashes instead of two move the same wall to about 0.918.

Reading it

Click or hover any key to pin its lookup: both homes light with a dashed ring, the readout spells out h1 and h2, and the count says 2 (or 3 with a stash). Hovering also picks that key’s component out of the graph and prints its verdict. ←/→ scrub, space plays, g toggles the graph.

Reuse

src/evict.js is a framework-free ES module:

No rendering or timers in the module; the canvas demo is reference code.

Gotchas