Sudoku Solver
The most familiar example of backtracking: try digits in empty cells and back up when a rule breaks.
Worth knowing first
#How does it work?
Sudoku is one of the most familiar examples of backtracking. In an n × n grid every row, column and box must hold the digits 1 to n exactly once. This lesson uses a 4 × 4 puzzle with 2 × 2 boxes so the animation stays readable; the method is the same for 9 × 9.
The algorithm finds the first empty cell and tries the digits 1, 2, … in turn. A digit already in the same row, column or box is skipped. A digit that fits is written in and the search moves to the next empty cell.
If no digit fits a cell, an earlier choice was wrong: the last digit written is erased and the next digit is tried there. The example puzzle has exactly one solution and needs two backtracks on the way.
#Try it yourself
A 4 × 4 Sudoku: every row, column and 2 × 2 box must hold 1-4 exactly once. We fill the empty cells in order and back up at dead ends.
#Step by step
- 01Find the first empty cell; if there is none, the puzzle is solved.
- 02Try the digits 1 to n in that cell.
- 03If a digit respects the row, column and box rules, write it and move to the next empty cell.
- 04If no digit fits, go back: erase the previous cell's digit and try its next digit.
#Example code
The same algorithm in 8 languages. Every program is compiled, run and checked to print the same output.
N, BOX = 4, 2 # a 4 x 4 puzzle with 2 x 2 boxes; the same code solves 9 x 9 with N, BOX = 9, 3
grid = [
[0, 0, 0, 0],
[0, 0, 1, 2],
[2, 0, 0, 4],
[0, 4, 0, 0],
]
def fits(r: int, c: int, d: int) -> bool:
if d in grid[r] or any(grid[k][c] == d for k in range(N)):
return False
br, bc = r - r % BOX, c - c % BOX
return all(grid[br + i][bc + j] != d for i in range(BOX) for j in range(BOX))
def solve() -> bool:
for r in range(N):
for c in range(N):
if grid[r][c] == 0: # the first empty cell
for d in range(1, N + 1):
if fits(r, c, d):
grid[r][c] = d
if solve():
return True
grid[r][c] = 0 # dead end: backtrack
return False
return True # no empty cell left
solve()
for row in grid:
print(" ".join(map(str, row)))#Complexity
| Operation | Time |
|---|---|
| Worst case | O(n^(number of empty cells)) |
| Checking a digit (scan) | O(n) |
| Checking with sets | O(1) |
| Memory | O(n²) |
In theory it is exponential; in practice the rules prune branches so early that 9 × 9 puzzles are solved in milliseconds. Filling the cell with the fewest options first (the MRV heuristic) and keeping sets of used digits per row/column/box speed the search up a lot.
#When to use it?
Sudoku and similar puzzles (KenKen, Kakuro), constraint satisfaction problems, timetables and exam schedules, graph colouring and resource assignment are all solved with the same "try, check, undo if needed" pattern.
#Practice
Log in to answer questions and save your progress.
Easy
Which digit may the solver write in a cell?
- 1One not already in the same row, column or box
- 2Any digit
- 3Only 1
- 4The same digit as the previous cell
What happens when no digit fits a cell?
- 1The previous cell's digit is erased and its next digit is tried
- 2The puzzle counts as solved
- 3A random digit is written
- 4It starts over
How big are the boxes in a 4 × 4 Sudoku?
- 12 × 2
- 24 × 4
- 31 × 4
- 43 × 3
Medium
Which heuristic usually speeds up a Sudoku solver the most?
- 1Filling the cell with the fewest options first (MRV)
- 2Filling cells in random order
- 3Placing all 9s first
- 4Skipping the boxes
Hard
What can be kept to check whether a digit fits in O(1)?
- 1A set (or bit mask) of used digits per row, column and box
- 2A list of all solutions
- 3A sorted list of digits
- 4A stack