A Sudoku Problem Formulated As An LP - PuLP v1.4.6 Documentation
A Sudoku Problem Formulated As An LP - PuLP v1.4.6 Documentation
pdf
Saved to Dropbox • Jan 12, 2021 at 5:47 PM
Next topic
pulp.constants
This Page
Show Source
Quick search
Go
Formulation
Instead, we must create 729 individual binary (0-1) problem variables. These represent 9 problem variables per square for each of 81 squares, where the 9
variables each correspond to the number that might be in that square. The binary nature of the variable says whether the existence of that number in that
square is true or false. Therefore, there can clearly be only 1 of the 9 variables for each square as true (1) and the other 8 must be false (0) since only one
number can be placed into any square. This will become more clear.
Solution
The introductory commenting and import statement are entered
"""
The Sudoku Problem Formulation for the PuLP Modeller
In the unique case of the sudoku problem, the row names, column names and variable option values are all the exact same list of numbers (as strings)
from “1” to “9”.
# A list of strings from "1" to "9" is created
Sequence = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
# The Vals, Rows and Cols sequences all follow this form
Vals = Sequence
Rows = Sequence
Cols = Sequence
A list called Boxes is created with 9 elements, each being another list. These 9 lists correspond to each of the 9 boxes, and each of the lists contains tuples
as the elements with the row and column indices for each square in that box. Explicitly entering the values in a similar way to the following would have had
the same effect (but would have been a waste of time):
# The boxes list is created, with the row and column index of each square in each box
Boxes =[]
for i in range(3):
for j in range(3):
Boxes += [[(Rows[3*i+k],Cols[3*j+l]) for k in range(3) for l in range(3)]]
Therefore, Boxes[0] will return a list of tuples containing the locations of each of the 9 squares in the first box.
The prob variable is created to contain the problem data. LpMinimize has the same effect as LpMaximise in this case.
The 729 problem variables are created since the (Vals,Rows,Cols) creates a variable for each combination of value, row and column. An example variable
would be: Choice_4_2_9, and it is defined to be a binary variable (able to take only the integers 1 or 0. If Choice_4_2_9 was 1, it would mean the number
4 was present in the square situated in row 2, column 9. (If it was 0, it would mean there was not a 4 there)
As explained above, the objective function (what we try to change using the problem variables) is simply zero (constant) since we are only concerned with
any variable combination that can satisfy the constraints.
Since there are 9 variables for each square, it is important to specify that only exactly one of them can take the value of “1” (and the rest are “0”).
Therefore, the below code reads: for each of the 81 squares, the sum of all the 9 variables (each representing a value that could be there) relating to that
particular square must equal 1.
# A constraint ensuring that only one value can be in each square is created
for r in Rows:
for c in Cols:
prob += lpSum([choices[v][r][c] for v in Vals]) == 1, ""
These constraints ensure that each number (value) can only occur once in each row, column and box.
# The row, column and box constraints are added for each value
for v in Vals:
for r in Rows:
prob += lpSum([choices[v][r][c] for c in Cols]) == 1,""
for c in Cols:
prob += lpSum([choices[v][r][c] for r in Rows]) == 1,""
for b in Boxes:
prob += lpSum([choices[v][r][c] for (r,c) in b]) == 1,""
The starting numbers are entered as constraints i.e a “5” in row “1” column “1” is true.
The problem is written to an LP file, solved using CPLEX (due to CPLEX’s simple output) and the solution status is printed to the screen
Instead of printing out all 729 of the binary problem variables and their respective values, it is most meaningful to draw the solution in a text file. The code
also puts lines inbetween every third row and column to make the solution easier to read. The sudokuout.txt file is created in the same folder as the .py file.
# The solution is written to the sudokuout.txt file
for r in Rows:
if r == "1" or r == "4" or r == "7":
sudokuout.write("+-------+-------+-------+\n")
for c in Cols:
for v in Vals:
if value(choices[v][r][c])==1:
if c == "9":
sudokuout.write("|\n")
sudokuout.write("+-------+-------+-------+")
sudokuout.close()
We can make our code return all the solutions by editing our code as shown after the prob.writeLP line. Essentially we are just looping over the solve
statement, and each time after a successful solve, adding a constraint that the same solution cannot be used again. When there are no more solutions, our
program ends.
while True:
prob.solve()
# The status of the solution is printed to the screen
print "Status:", LpStatus[prob.status]
# The solution is printed if it was deemed "optimal" i.e met the constraints
if LpStatus[prob.status] == "Optimal":
# The solution is written to the sudokuout.txt file
for r in Rows:
if r == "1" or r == "4" or r == "7":
sudokuout.write("+-------+-------+-------+\n")
for c in Cols:
for v in Vals:
if value(choices[v][r][c])==1:
if c == "1" or c == "4" or c =="7":
sudokuout.write("| ")
sudokuout.write(v + " ")
if c == "9":
sudokuout.write("|\n")
sudokuout.write("+-------+-------+-------+\n\n")
# The constraint is added that the same solution cannot be returned again
prob += lpSum([choices[v][r][c] for v in Vals
for r in Rows
for c in Cols
if value(vars[v][r][c])==1]) <= 80
# If a new optimal solution cannot be found, we end the program
else:
break
sudokuout.close()
The full file using this is available Sudoku2.py. When using this code for sudoku problems with a large number of solutions, it could take a very long time to
solve them all. To create sudoku problems with multiple solutions from unique solution sudoku problem, you can simply delete a starting number constraint.
You may find that deleting several constraints will still lead to a single optimal solution but the removal of one particular constraint leads to a sudden
dramatic increase in the number of solutions.
PuLP documentation by Pulp documentation team is licensed under a Creative Commons Attribution-Share Alike 3.0 New Zealand