CO4 Topic 29 of 34
Sudoku and graph colouring
Two backtracking problems with the same shape: give every cell or vertex a value from a small set so that no constraint is broken. Sudoku is graph colouring with nine colours on a special graph.
You will be able to
- State the m-colouring decision problem and the chromatic number
- Solve m-colouring by backtracking and trace the search
- Solve Sudoku by backtracking with row, column and box checks
- Explain why Sudoku is a graph-colouring problem
Read
- Textbook 2, §7.4 Graph colouring
- Textbook 1, Problem 34-3 (graph colouring) and §34.5
- Reference 1, §7.1 Backtracking (Sudoku case study)
Watch
Intuition
Graph colouring: give every vertex a colour so that the two ends of every edge differ. With m colours available, can it be done? The smallest m that works is the chromatic number χ(G). Timetabling is the classic use: courses are vertices, an edge joins two courses that share a student, and colours are exam slots.
Sudoku is the same problem in disguise. Each of the 81 cells is a vertex; two cells are joined if they share a row, a column or a 3 × 3 box; the digits 1–9 are the colours, and some vertices come pre-coloured.
Both are solved the same way: give the next vertex (cell) the first value that does not clash with its already-coloured neighbours, and backtrack when no value fits.
How it works
m-colouring by backtracking. Number the vertices 1 … n. Try colours 1 … m for vertex k; colour c is allowed if no neighbour of k already has c. If some colour works, move on to vertex k + 1; if none does, go back to vertex k − 1 and try its next colour.
Example with m = 3:
graph G {
layout=neato; node [shape=circle, style=filled, fontcolor="#111417"];
A [pos="0,1!", fillcolor="#f2c94c"]; B [pos="1.4,1.8!", fillcolor="#6fcf97"];
C [pos="1.4,0.2!", fillcolor="#56ccf2"]; D [pos="2.8,1!", fillcolor="#f2c94c"];
E [pos="4.2,1!", fillcolor="#6fcf97"];
A -- B; A -- C; B -- C; B -- D; C -- D; D -- E;
}
| vertex | neighbours already coloured | colour |
|---|---|---|
| A | — | 1 (yellow) |
| B | A = 1 | 2 (green) |
| C | A = 1, B = 2 | 3 (blue) |
| D | B = 2, C = 3 | 1 (yellow) |
| E | D = 1 | 2 (green) |
No backtracking was needed here. With only m = 2 colours the triangle A, B, C cannot be coloured, so the search fails at C, backtracks through every choice and reports "no". χ(G) = 3. In total this graph has 12 different 3-colourings.
Sudoku by backtracking. Find the first empty cell, try digits 1 … n that do not appear in its row, column or box, recurse, and clear the cell if the recursion fails. A 4 × 4 example (2 × 2 boxes):
| puzzle | → | solution | ||||||
|---|---|---|---|---|---|---|---|---|
| 1 | · | · | · | 1 | 3 | 2 | 4 | |
| · | · | 3 | · | 4 | 2 | 3 | 1 | |
| · | 4 | · | · | 2 | 4 | 1 | 3 | |
| · | · | · | 2 | 3 | 1 | 4 | 2 |
Python implementation
def m_colouring(adj, m):
"""adj: dict vertex -> neighbours. Return a colouring dict, or None if m colours are not enough."""
order = list(adj)
colour = {}
def go(k):
if k == len(order):
return True
v = order[k]
for c in range(1, m + 1):
if all(colour.get(u) != c for u in adj[v]): # bounding function
colour[v] = c
if go(k + 1):
return True
del colour[v] # backtrack
return False
return dict(colour) if go(0) else None
def solve_sudoku(grid):
"""Fill grid (n x n lists, 0 = empty) in place; return True if solved."""
n = len(grid)
b = int(n ** 0.5) # box size
for r in range(n):
for c in range(n):
if grid[r][c] == 0:
for d in range(1, n + 1):
br, bc = r - r % b, c - c % b
if (d not in grid[r]
and all(grid[i][c] != d for i in range(n))
and all(grid[i][j] != d for i in range(br, br + b) for j in range(bc, bc + b))):
grid[r][c] = d
if solve_sudoku(grid):
return True
grid[r][c] = 0 # backtrack
return False # no digit fits this cell
return True # no empty cell left
if __name__ == "__main__":
adj = {"A": "BC", "B": "ACD", "C": "ABD", "D": "BCE", "E": "D"}
print(m_colouring(adj, 3)) # {'A': 1, 'B': 2, 'C': 3, 'D': 1, 'E': 2}
print(m_colouring(adj, 2)) # None
g = [[1, 0, 0, 0], [0, 0, 3, 0], [0, 4, 0, 0], [0, 0, 0, 2]]
solve_sudoku(g)
print(g) # [[1, 3, 2, 4], [4, 2, 3, 1], [2, 4, 1, 3], [3, 1, 4, 2]]
Complexity
| Worst case | |
|---|---|
| m-colouring, n vertices | O(mⁿ) colourings tried, each check O(n) → O(n · mⁿ) |
| n × n Sudoku with e empty cells | O(nᵉ) |
Deciding whether a graph is 3-colourable is NP-complete (topic 34), so no polynomial algorithm is known. 2-colourability, in contrast, is easy: a graph is 2-colourable exactly when it is bipartite, which BFS checks in O(V + E).
Common mistakes
- Not undoing the colour after a failed recursive call, so later checks see a stale colour.
- Thinking greedy colouring gives χ(G). Colouring each vertex with the smallest free colour, in a fixed order, can use far more colours than needed.
- Forgetting the box constraint in Sudoku, or computing the box corner wrongly (it is r − r mod b, not r / b).
- Confusing "m-colourable" with "needs exactly m colours". A 2-colourable graph is also 3-colourable.
Problems
- Find χ(G) for: a cycle with 5 vertices; a cycle with 6 vertices; the complete graph K₅; a wheel (a 5-cycle plus a centre joined to every vertex).
- Lab: extend
m_colouringto count all m-colourings. Check that the example graph has 12 with m = 3. - Lab: solve a 9 × 9 newspaper Sudoku and count how many cells the search filled (including those later cleared).
- Model an exam timetable as a colouring problem: subjects A–F, where A clashes with B and C, B with D, C with D and E, and E with F. How many slots are needed?
- Improve the Sudoku solver by always choosing the empty cell with the fewest allowed digits. Compare the number of recursive calls.
Quiz
GATE-style practice
Questions in the GATE CSE formats: MCQ (one correct option), MSQ (one or more correct options) and NAT (type a number). Each comes with a full worked solution. These are original practice questions, not past papers; for the real thing, see the official GATE site and its previous question papers.