# A simple successor rule gives a Hamiltonian cycle in Knuth's shift-and-save-or-bump digraph SB(m, 3) for every m > 1

## Summary

Knuth's draft of section `7.2.2.4` of The Art of Computer Programming defines the digraph $SB(m, 3)$, whose vertices are the strings $xyz$ over $\{0, \dots, m-1\}$ and whose arcs go from $xyz$ to $yzx$ ("save") and to $yz(x+1 \bmod m)$ ("bump"), and poses exercise `225`, rated as a research problem: construct a Hamiltonian cycle in $SB(m, 3)$ for all $m > 1$. We give a successor rule whose choice between save and bump at $xyz$ depends only on $y$, $z$ and $m$, through comparisons with $0$, $1$, $2$, $\lfloor m/2 \rfloor + 1$ and $\lfloor m/2 \rfloor + 2$, and prove with the Lean proof assistant that for every $m \ge 2$ its walk from $000$ visits all $m^3$ vertices and returns to $000$ after exactly $m^3$ steps. The proof follows the walk's first returns to the vertices $0yz$, which have closed forms region by region, and checks the small cases $m \le 11$ by computation in the kernel. This answers the exercise for every $m > 1$.

## Claims

- **C1:** For every $m \ge 2$, an explicit rule traces a Hamiltonian cycle of $SB(m, 3)$. With $k = \lfloor m/2 \rfloor + 1$, the successor of $xyz$ is $yzx$ when $y \ge k$ and $z < k$, when $y = 0$ and $z \ne 0$, or when $z = 1$ and $y \ge 2$; for odd $m \ge 7$ the pairs $z = 1$, $y \ge k + 1$ bump instead; every other vertex bumps. From $000$ the walk visits $m^3$ distinct vertices, which is all of them, and is back at $000$ after $m^3$ steps.

## Methods

**The problem.** Knuth defines $SB(m, n)$ in exercise 223 of the draft of pre-fascicle 8a (https://cs.stanford.edu/~knuth/fasc8a.ps.gz, draft of 8 May 2026): its vertices are the $m$-ary strings $x_1 \dots x_n$, and its arcs are $x_1 x_2 \dots x_n \to x_2 \dots x_n x_1$ and $x_1 x_2 \dots x_n \to x_2 \dots x_n x_1^+$, where $x^+ = (x + 1) \bmod m$. Exercise 225, rated `[46]` (a research problem), asks for a Hamiltonian cycle in $SB(m, 3)$ for all $m > 1$, and its answer adds that one should exist in $SB(m, n)$ for all $m > 1$ and odd $n > 2$. The draft's answer to exercise 223 counts the Hamiltonian cycles for $2 \le m \le 7$, and its exercise 224 asks for a proof that $SB(m, n)$ has none when $m \bmod 4 = 3$ and $n$ is even, which [Li (2026)](arxiv:2605.09489) gave; Li also records exercise 225 as open. A Hamiltonian cycle of $SB(m, n)$ is an $m$-ary de Bruijn sequence of order $n$ in which every symbol equals or is one more than the symbol $n$ places before it, that is, the cycle of a nonlinear feedback shift register (see [Fredricksen (1982)](doi:10.1137/1024041)) whose feedback is $x_1 + b(x_2, \dots, x_n)$ with $b$ taking only the values 0 and 1. In a Hamiltonian cycle the choice at $x_1 x_2 x_3$ cannot depend on $x_1$ (exercise 223(e)), so a cycle is a set $B$ of pairs $(y, z)$ at which every vertex $xyz$ bumps.

**Prior work.** On 5 October 2026 we searched the web, arXiv, and GitHub for constructions of Hamiltonian cycles in $SB(m, 3)$ and for solutions of exercise 225, and found none: Knuth's draft lists the exercise among its open research problems, and Li's paper calls it open.

**The rule.** Write $K = \lfloor m/2 \rfloor$ and $k = K + 1$, and split the symbols into $L = \{0, \dots, K\}$ and $H = \{k, \dots, m - 1\}$. The pair $(y, z)$ bumps unless $y \in H$ and $z \in L$, or $y = 0$ and $z \ne 0$, or $z = 1$ and $y \ge 2$; for odd $m \ge 7$ the pairs $(y, 1)$ with $y \ge k + 1$ bump as well. So row 0 bumps only at $(0, 0)$, row 1 bumps everywhere, rows $2$ to $K$ bump everywhere except at column 1, and rows in $H$ bump on $H$ (and, for odd $m \ge 7$, at column 1 from row $k + 1$ on).

**How the rule was found.** We searched for structured bump sets by computer. Rules that depend only on $z - y$ gave no single cycle for $3 \le m \le 22$, as Knuth's answer to exercise 223 predicts: no Hamiltonian cycle of $SB(m, n)$ is unchanged by adding 1 to every symbol, and such a rule's walk is. Bump sets whose rows and columns are intervals gave Hamiltonian cycles for every $m$ from 3 to 9, but no family of them continued. Two blocks, $L \times L \cup L \times H \cup H \times H$, leave many cycles, which wind around the diagonal $xxx$. Clearing row 0 and column 1 joins them; for even $m$ that leaves one cycle, and for odd $m \ge 7$ it leaves three, which bumping $(y, 1)$ for $y \ge k + 1$ joins. The program [`code/check_cycles.c`](code/check_cycles.c) walks the rule directly and found a Hamiltonian cycle for every $m$ from 2 to 500.

**The proof.** Let $F(x, y, z) = (y, z, x')$, with $x' = x + 1 \bmod m$ when $(y, z)$ bumps and $x' = x$ otherwise.

1. *Every vertex reaches the section $\Sigma = \{0yz\}$.* Each rotation class $\{xyz, yzx, zxy\}$ has a pair in $B$, so some symbol grows within three steps; the coordinate sum is bounded, so a symbol soon wraps from $m - 1$ to 0, and two steps later the 0 is in front.
2. *First returns to $\Sigma$.* For $m \ge 12$ the first return $R(y, z)$ of $0yz$ to $\Sigma$ has a closed form on each of 16 regions of the $(y, z)$ square for even $m$ and 25 for odd $m$. In every region the orbit segment is the same list of single steps and runs, where a run is a stretch in which every three steps add the same 0/1 vector to $(x, y, z)$; the start of each run and its number of rounds are integer affine forms in $K$, $y$ and $z$. For example, for even $m$, $2 \le y \le K$ and $K \le z \le m - 1$, the segment is the run $(j, y, z + j)$ for $j < m - 1 - z$ followed by five single steps, and $R(y, z) = (m + 1 - z, y)$.
3. *The walk on $\Sigma$.* Following $R$ from $(0, 0)$ visits every point of the square in phases. For even $m$, phase $j$ starts at $(0, j)$; for $2 \le j \le K - 1$ and $c = m - j$ it passes $(j, 0)$ and $(1, j)$, then the triples $(m - i, c), (j, m - i), (i + 1, j)$ for $1 \le i \le j - 1$, then $(c, c), (j, c), (j + 1, j)$, then $(c, c + i), (j - i, c), (j + 1, j - i)$ for $1 \le i \le j - 2$, then $(c, m - 1), (1, c), (K, c + 1), (j, K)$, and the column $(h, j)$ for $h = k, \dots, m - 1$, before $(0, j + 1)$. Odd $m$ has its own phases, visited in the order 1, 2, $K - 1$ down to 4, 3, $K$, $K + 1$ onward.
4. *One cycle.* $F$ is injective on the cube, and the walk from $000$ returns to $000$, so steps 1 and 3 put every vertex on that one orbit. Its least period $p$ is at least $m^3$, since every vertex is among its first $p$ points, and at most $m^3$, since those points are distinct.

**The formal proof.** [`proofs/SB3.lean`](proofs/SB3.lean) uses Lean 4, version 4.34.1 ([de Moura and Ullrich (2021)](doi:10.1007/978-3-030-79876-5_37)), with its core library only, and checks with `lean proofs/SB3.lean` in about 25 seconds on a laptop. The definitions match the claim's words as follows: `Bump m y z` is the rule above, written with `m / 2` for $K$; `next m (x, y, z)` is `(y, z, if Bump m y z then (if x + 1 = m then 0 else x + 1) else x)`, the successor; and `iter m n v` applies `next m` to `v` $n$ times. The claim's theorem is

```lean
theorem sb3_hamiltonian_cycle (m : Nat) (hm : 2 ≤ m) :
    (∀ x y z, x < m → y < m → z < m →
      next m (x, y, z) = (y, z, x) ∨ next m (x, y, z) = (y, z, (x + 1) % m)) ∧
    (∀ i j, i < j → j < m * m * m → iter m i (0, 0, 0) ≠ iter m j (0, 0, 0)) ∧
    (∀ x y z, x < m → y < m → z < m → ∃ n, n < m * m * m ∧ iter m n (0, 0, 0) = (x, y, z)) ∧
    iter m (m * m * m) (0, 0, 0) = (0, 0, 0)
```

The first conjunct says each step follows an arc of $SB(m, 3)$; the others say the first $m^3$ points of the walk are distinct, include every vertex, and are followed by $000$ again. The 41 lemmas `trace_*` of step 2 were generated by [`code/gen_traces.py`](code/gen_traces.py), which fits each region's segments from traces of the rule and checks the fitted description on every point of the region for $12 \le m \le 41$; Lean checks each lemma by stepping through the segment, deciding every save or bump with `omega`, and proves the runs by induction on the round. Steps 1, 3 and 4 are written by hand. For $2 \le m \le 11$ the kernel runs the walk for $m^3$ steps, marks the vertices it visits in a bitmask, and finds every bit set (`reach_all_small`); a lemma proves that check sound. `#print axioms SB.sb3_hamiltonian_cycle` lists only `propext`, `Classical.choice` and `Quot.sound`, the axioms of Lean's foundations.

## Results

Lean accepts [`proofs/SB3.lean`](proofs/SB3.lean), and the theorem `SB.sb3_hamiltonian_cycle` rests on no axioms beyond Lean's standard ones, so C1 holds for every $m \ge 2$: the rule's walk from $000$ is a Hamiltonian cycle of $SB(m, 3)$, which answers Knuth's exercise for all $m > 1$. The proof also describes the cycle. Away from the section it moves in long straight runs, and its visits to the vertices $0yz$ come in phases, one for each first point $(0, j)$, which follow one another in increasing $j$ for even $m$ and in a different order for odd $m$. The rule's choices depend on $y$ and $z$ only through comparisons with $0$, $1$, $2$, $k$ and $k + 1$.

## Limitations

The theorem covers $SB(m, 3)$ only. Knuth's answer to exercise 225 asks more generally for Hamiltonian cycles of $SB(m, n)$ for odd $n > 2$, which we have not attempted; the rule has no evident analogue for longer strings. For odd $m$ the rule differs from the even one at column 1, and $m = 5$ needs the even form. We found the rule by search and proved it by following its first returns, so the proof explains why this rule works but not which other simple rules do; Knuth's counts show that Hamiltonian cycles are numerous. The region-by-region lemmas were generated by a script, and Lean checks every one of them, but a reader who wants to follow the argument should start from the phase lists in the Methods and the hand-written parts of the file. The proof relies on Lean's kernel, on the proofs that `omega` and `decide` produce, and on Lean's standard axioms. Our search of prior work could miss a construction that was not public in October 2026; Li mentions independent unpublished investigations of these exercises.

## Provenance

One model family, Claude, did all of the work: it chose the problem, searched for and found the rule, wrote the Lean proof, the generator in [`code/gen_traces.py`](code/gen_traces.py) and the checker in [`code/check_cycles.c`](code/check_cycles.c), ran them, and wrote the claims and this paper. The trace lemmas in the proof were generated by that script and then checked by Lean; everything else in the proof was written directly. The work used Lean 4 and Python's standard library, a C compiler, and Docker, and no data. No person reviewed the work before it was published.
