Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" This module contains Cython code for a backtracking algorithm to solve Sudoku puzzles.
Once Cython implements closures and the ``yield`` keyword is possible, this can be moved into the ``sage.games.sudoku`` module, as part of the ``Sudoku.backtrack`` method, and this module can be banned. """ from __future__ import print_function
def backtrack_all(n, puzzle): r""" A routine to compute all the solutions to a Sudoku puzzle.
INPUT:
- ``n`` - the size of the puzzle, where the array is an `n^2\times n^2` grid
- ``puzzle`` - a list of the entries of the puzzle (1-based), in row-major order
OUTPUT:
A list of solutions, where each solution is a (1-based) list similar to ``puzzle``.
TESTS:
This is just a cursory test here, since eventually this code will move. See the `backtrack` method of the `Sudoku` class in the `sage.games.sudoku` module for more enlightening examples. ::
sage: from sage.games.sudoku_backtrack import backtrack_all sage: c = [0, 0, 0, 0, 1, 0, 9, 0, 0, 8, 0, 0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 3, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 0, 0, 0, 0, 5, 8, 0, 6, 0, 0, 0, 0, 1, 3, 0, 7, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0] sage: print(backtrack_all(3, c)) [[6, 5, 4, 3, 1, 2, 9, 8, 7, 8, 3, 1, 4, 7, 9, 5, 2, 6, 2, 9, 7, 6, 8, 5, 4, 1, 3, 4, 7, 2, 5, 3, 8, 6, 9, 1, 3, 8, 5, 1, 9, 6, 2, 7, 4, 9, 1, 6, 7, 2, 4, 3, 5, 8, 5, 6, 8, 9, 4, 7, 1, 3, 2, 7, 4, 3, 2, 5, 1, 8, 6, 9, 1, 2, 9, 8, 6, 3, 7, 4, 5]]
ALGORITHM:
We traverse a search tree depth-first. Each level of the tree corresponds to a location in the grid, listed in row-major order. At each location we maintain a list of the symbols which may be used in that location as follows.
A location has "peers", which are the locations in the same row, column or box (sub-grid). As symbols are chosen (or fixed initially) at a location, they become ineligible for use at a peer. We track this in the ``available`` array where at each location each symbol has a count of how many times it has been made ineligible. As this counter transitions between 0 and 1, the number of eligible symbols at a location is tracked in the ``card`` array. When the number of eligible symbols at any location becomes 1, then we know that *must* be the symbol employed in that location. This then allows us to further update the eligible symbols at the peers of that location. When the number of the eligible symbols at any location becomes 0, then we know that we can prune the search tree.
So at each new level of the search tree, we propagate as many fixed symbols as we can, placing them into a two-ended queue (``fixed`` and ``fixed_symbol``) that we process until it is empty or we need to prune. All this recording of ineligible symbols and numbers of eligible symbols has to be unwound as we backup the tree, though.
The notion of propagating singleton cells forward comes from an essay by Peter Norvig [sudoku:norvig]_. """ cdef: # Arrays sizes are n^4, and n^2, with 3n^2-2n-1 for second slot of peers, n = 4 int i, j, count, level, apeer int nsquare, npeers, nboxes int grid_row, grid_col, grid_corner int peers[256][39] int box[256] int available[256][16] int card[256] int hint, symbol, abox int feasible int nfixed[256] int fixed[256][256] int fixed_symbol[256][256]
int process, asymbol, alevel, process_level, process_symbol
# sanity check on size (types) # n is "base" dimension # nsquare is size of a grid # nboxes is total number of entries
# "Peers" of a box are boxes in the same column, row or grid # Like the conflict graph when expressed as a graph coloring problem # location as row and column in square # grids are numbered similarly, in row-major order # Peers' levels in same grid, but not the box itself # Peers' levels in the same row, but not in grid # Peers' levels in same column, but not in grid
# Initialize data structures # Make every symbol available initially for a box # And make set cardinality the size of symbol set
# For non-zero entries of input puzzle # (1) Convert to zero-based indexing # (2) Make a set of size 1 available initially # location as row and column in square # grids are numbered similarly, in row-major order # Limit symbol set at the hint's location to a singleton # Remove hint from all peers' available symbols # Track cardinality as sets adjust
# Start backtracking # restore symbols to peers # move sideways in search tree to next available symbol # fell off the end sideways, backup else: # Remove elements of sets, adjust cardinalities # Descend in search tree if no empty sets created # Can't break early at an empty set # or we will confuse restore that happens immediately # Have a solution to save, stay at this bottom-most level # Once Cython implements closures, a yield can go here else: |