Tower Of Hanoi¶
Problem¶
Move n disks from a source peg to a target peg using an auxiliary peg. Only one disk may be moved at a time, and a larger disk may never be placed on top of a smaller one.
Approach¶
Classic recursion: move n-1 disks from source to auxiliary, move the largest disk from source to target, then move n-1 disks from auxiliary to target. Base case: n == 0 (do nothing).
When to Use¶
Pure recursive decomposition — the canonical example. Demonstrates how recursion breaks problems into identical sub-problems. Also: understanding call stack depth and O(2^n) explosion.
Complexity¶
| Time | O(2^n) — number of moves |
| Space | O(n) — recursion depth |
Implementation¶
tower_of_hanoi ¶
Tower of Hanoi — move n disks between pegs.
Problem
Move n disks from a source peg to a target peg using an auxiliary peg. Only one disk may be moved at a time, and a larger disk may never be placed on top of a smaller one.
Approach
Classic recursion: move n-1 disks from source to auxiliary, move the largest disk from source to target, then move n-1 disks from auxiliary to target. Base case: n == 0 (do nothing).
When to use
Pure recursive decomposition — the canonical example. Demonstrates how recursion breaks problems into identical sub-problems. Also: understanding call stack depth and O(2^n) explosion.
Complexity
Time: O(2^n) — number of moves Space: O(n) — recursion depth
tower_of_hanoi ¶
tower_of_hanoi(
n: int,
source: str = "A",
target: str = "C",
auxiliary: str = "B",
) -> list[tuple[str, str]]
Solve Tower of Hanoi for n disks, returning moves as (from, to) tuples.
tower_of_hanoi(1) [('A', 'C')] tower_of_hanoi(2) [('A', 'B'), ('A', 'C'), ('B', 'C')]
Source code in src/algo/recursion/tower_of_hanoi.py
"""Tests for the Tower of Hanoi problem."""
from hypothesis import given
from hypothesis import strategies as st
from algo.recursion.tower_of_hanoi import tower_of_hanoi
class TestTowerOfHanoi:
def test_one_disk(self) -> None:
assert tower_of_hanoi(1) == [("A", "C")]
def test_two_disks(self) -> None:
assert tower_of_hanoi(2) == [("A", "B"), ("A", "C"), ("B", "C")]
def test_three_disks(self) -> None:
moves = tower_of_hanoi(3)
assert len(moves) == 7
assert moves[0] == ("A", "C")
assert moves[-1] == ("A", "C")
def test_zero_disks(self) -> None:
assert tower_of_hanoi(0) == []
def test_custom_peg_names(self) -> None:
moves = tower_of_hanoi(1, source="X", target="Z", auxiliary="Y")
assert moves == [("X", "Z")]
@given(n=st.integers(min_value=1, max_value=10))
def test_move_count_is_2n_minus_1(self, n: int) -> None:
assert len(tower_of_hanoi(n)) == 2**n - 1
@given(n=st.integers(min_value=1, max_value=8))
def test_moves_are_valid(self, n: int) -> None:
"""Simulate moves and verify no larger disk is placed on a smaller one."""
moves = tower_of_hanoi(n)
pegs: dict[str, list[int]] = {
"A": list(range(n, 0, -1)),
"B": [],
"C": [],
}
for src, tgt in moves:
disk = pegs[src].pop()
if pegs[tgt] and pegs[tgt][-1] < disk:
msg = f"Invalid move: disk {disk} onto disk {pegs[tgt][-1]}"
raise AssertionError(msg)
pegs[tgt].append(disk)
# All disks should be on target peg
assert pegs["A"] == []
assert pegs["B"] == []
assert pegs["C"] == list(range(n, 0, -1))
Implement it yourself
Run: just challenge recursion tower_of_hanoi
Then implement the functions to make all tests pass.
Use just study recursion for watch mode.
Reveal Solution
tower_of_hanoi ¶
Tower of Hanoi — move n disks between pegs.
Problem
Move n disks from a source peg to a target peg using an auxiliary peg. Only one disk may be moved at a time, and a larger disk may never be placed on top of a smaller one.
Approach
Classic recursion: move n-1 disks from source to auxiliary, move the largest disk from source to target, then move n-1 disks from auxiliary to target. Base case: n == 0 (do nothing).
When to use
Pure recursive decomposition — the canonical example. Demonstrates how recursion breaks problems into identical sub-problems. Also: understanding call stack depth and O(2^n) explosion.
Complexity
Time: O(2^n) — number of moves Space: O(n) — recursion depth
tower_of_hanoi ¶
tower_of_hanoi(
n: int,
source: str = "A",
target: str = "C",
auxiliary: str = "B",
) -> list[tuple[str, str]]
Solve Tower of Hanoi for n disks, returning moves as (from, to) tuples.
tower_of_hanoi(1) [('A', 'C')] tower_of_hanoi(2) [('A', 'B'), ('A', 'C'), ('B', 'C')]