Initializing help system before first use

Solving Sudoku


Type: Programming
Rating: 4 (medium-difficult)
Description: Place numbers from 1 to 9 into a 9x9 grid such that no number repeats in any row, in any column, and in any 3x3 sub-grid.
File(s): sudoku.py


sudoku.py
#
# Example using the Xpress Python interface
#
# Sudoku: place numbers from 1 to 9 into a 9x9 grid such that no
# number repeats in any row, in any column, and in any 3x3 sub-grid.
#
# More generally, replace 3 with q and 9 with q^2
#

from __future__ import print_function

import xpress as xp

# We model this problem as an assignment problem where certain
# conditions must be met for all numbers in the columns, rows, and
# sub-grids.
#
# These subgrids are lists of tuples with the coordinates of each
# subgrid. In a 9x9 sudoku, for instance, subgrids[0,1] has the 9
# elements in the top square, i.e., the following:
#
#  ___ ___ ___
# |   |###|   |
# |   |###|   |
# |___|###|___|
# |   |   |   |
# |   |   |   |
# |___|___|___|
# |   |   |   |
# |   |   |   |
# |___|___|___|
#
# while subgrids[2,2] has the 9 elements in the bottom right square.

# The input is a starting grid where the unknown numbers are replaced by zero

q = 3

starting_grid = \
 [[8, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 3, 6, 0, 0, 0, 0, 0],
  [0, 7, 0, 0, 9, 0, 2, 0, 0],
  [0, 5, 0, 0, 0, 7, 0, 0, 0],
  [0, 0, 0, 0, 4, 5, 7, 0, 0],
  [0, 0, 0, 1, 0, 0, 0, 3, 0],
  [0, 0, 1, 0, 0, 0, 0, 6, 8],
  [0, 0, 8, 5, 0, 0, 0, 1, 0],
  [0, 9, 0, 0, 0, 0, 4, 0, 0]]

# Solve a 16x16 sudoku: Just uncomment the following lines.
#
# q = 4
#
# starting_grid = \
#  [[ 0, 0,12, 0, 0, 2, 0, 0, 0, 7, 3, 0,13,15, 0, 0],
#   [15, 0, 0, 0, 0, 3, 0, 0, 9, 0, 0, 0,12, 0, 0,10],
#   [ 0, 0, 0, 0, 9, 0, 6, 0, 0, 0,12, 0, 0, 0, 2, 5],
#   [ 6,11, 1, 0, 0,10, 5, 0, 0, 2, 0,15, 0, 0, 0, 0],
#   [ 4, 6, 3, 0, 0, 0,13,14, 0, 0, 0, 0, 0, 7, 0, 0],
#   [ 0,15,11, 0, 7, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1, 0],
#   [ 0, 1, 0,10,15, 0, 0, 0,11, 3,14, 0, 6, 0, 0, 0],
#   [13, 0, 8, 7, 0, 5, 0, 0, 0, 1, 9,12, 0, 0, 0, 0],
#   [ 0, 0, 0, 6, 3, 7,15, 4, 0, 0, 0, 0, 0,14, 0, 0],
#   [ 0, 8, 0, 0, 0, 0, 0, 0, 0,11, 7, 0, 4, 0, 0, 0],
#   [ 0, 0, 0, 0, 0, 0, 0, 0,13, 0, 0, 6, 9, 0, 3, 0],
#   [ 0, 0, 0, 0, 2, 8,14, 0, 3, 0, 0,10, 0, 0,13, 7],
#   [ 0, 0, 0, 8, 0, 0, 0, 7,10, 0, 0, 0, 0, 0, 5, 1],
#   [ 0, 4,10, 1, 6, 0, 0, 0, 0,12, 0,14, 7, 3, 9,15],
#   [ 3, 0,15, 0, 0, 0, 0, 8, 0, 0, 1, 0,14,12, 0, 0],
#   [ 2, 0, 0, 9,12, 0, 0, 1, 0, 0, 0, 0, 0, 6, 8, 0]]

n = q**2  # the size must be the square of the size of the subgrids
N = range(n)

x = {(i, j, k): xp.var(vartype=xp.binary, name='x{0}_{1}_{2}'.format(i, j, k))
     for i in N for j in N for k in N}

# define all q^2 subgrids
subgrids = {(h, l): [(i, j) for i in range(q*h, q*h + q)
            for j in range(q*l, q*l + q)]
            for h in range(q)
            for l in range(q)}

vertical = [xp.Sum(x[i, j, k] for i in N) == 1 for j in N for k in N]
horizontal = [xp.Sum(x[i, j, k] for j in N) == 1 for i in N for k in N]
subgrid = [xp.Sum(x[i, j, k] for (i, j) in subgrids[h, l]) == 1
           for (h, l) in subgrids.keys() for k in N]

# Assign exactly one number to each cell

assign = [xp.Sum(x[i, j, k] for k in N) == 1 for i in N for j in N]

# Fix those variables that are non-zero in the input grid

init = [x[i, j, k] == 1 for k in N for i in N for j in N
        if starting_grid[i][j] == k+1]

p = xp.problem()

p.addVariable(x)
p.addConstraint(vertical, horizontal, subgrid, assign, init)

# we don't need an objective function, as long as a solution is found

p.solve()

print('Solution:')

for i in N:
    for j in N:
        l = [k for k in N if p.getSolution(x[i, j, k]) >= 0.5]
        assert(len(l) == 1)
        print('{0:2d}'.format(1 + l[0]), end='', sep='')
    print('')

© 2001-2019 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.