Sudoku Solving Algorithm

DFS with backtracking and various constraint propagation techniques.

Introduction

The Problem

Sudoku is a logic-based number placement puzzle consisting of a 9×9 grid divided into nine 3×3 subgrids. The puzzle begins with some cells pre-filled with numbers. To solve the puzzle, each row, column, and 3×3 subgrid must contain all digits from 1 to 9 without repetition.

Objective

The goal was to develop a Python-based Sudoku solver capable of handling puzzles ranging from very easy to hard, with a focus on efficiency and optimization.

Algorithm Description

I implemented constraint propagation through a backtracking Depth-First Search (DFS) algorithm. To reduce the branching factor, the algorithm uses the minimum-remaining values (MRV) heuristic by prioritizing sudoku cells with the fewest possible options.

Examples of constraint propagation techniques used in the solver

Core Algorithm

  • Depth-first search with backtracking
  • Forward checking to detect invalid states early

Constraint Propagation Techniques

  • Arc-consistency algorithm to reduce variable domains before search
  • Naked singles (when a cell has only one possible value)
  • Hidden singles (when only one cell in a unit can contain a specific value)
  • Naked sets (pairs, triples, quads)

Selection Heuristics

  • Minimum Remaining Values (MRV) to choose which cell to fill next
  • Randomized selection among equal candidates to avoid getting stuck

Implementation Details

The algorithm's strength lies in its extensive use of constraint propagation before and during the search process. By significantly reducing the domain of variables before initiating DFS, the search space becomes much more manageable.

I was inspired by Deep Blue's chess-specific knowledge and attempted to incorporate as many sudoku-specific constraints as possible. Unfortunately, time constraints prevented implementing more advanced techniques like the Phistomefel Ring, which remains a goal for future work.

Results and Performance

The algorithm currently solves:

  • Hard puzzles in approximately 200-300ms on average
  • What previously took minutes now completes in tenths of a second

The biggest performance improvement came from the arc-consistency implementation of iteratively checking for naked singles, which made solving hard puzzles feasible. The MRV heuristic also decreased solution time by reducing the branching factor of the search tree.

Optimizations and Complexity

The time complexity of the DFS algorithm is O(b^m), where b is the branching factor and m is the maximum depth. Through various optimizations and heuristics, I was able to:

  • Significantly reduce the branching factor using MRV
  • Decrease the number of nodes in the search tree with sudoku-specific constraints
  • Detect invalid states early with forward checking

In terms of programming, the code was not fully optimized due to my inexperience. Future improvements could include using NumPy to vectorize array operations, which could potentially reduce execution time further.

Reflections and Future Work

While my solver doesn't match the ~20ms performance of some professional solvers, the improvement from minutes to hundreds of milliseconds represents significant progress. The experience demonstrated the power of combining general search algorithms with domain-specific knowledge.

For future enhancements, I plan to:

  • Experiment with the "Dancing Links" algorithm for more efficient constraint satisfaction
  • Implement additional interesting sudoku-specific techniques like "x-wing" and the Phistomefel Ring
  • Optimize code execution with NumPy vectorization
  • Explore more advanced search algorithms and heuristics

References

  • Hoduku (2008). HoDoKu: Solving Techniques - Singles (Hidden Single, Naked Single, Full House). hodoku.sourceforge.net
  • Mastering Sudoku (2024). Phistomefel Ring Explained & How To Solve Sudokus With It. masteringsudoku.com
  • SudokuWiki.org (2005). Naked Candidates. sudokuwiki.org