Killer Sudoku [1] is a variant of Sudoku where an additional constraint is that cells belonging to a cage (the colored regions in the picture) add up to a given value.
As in standard Sudokus, we need to fill each cell with a number \(1,\dots,9\), and values in each row, column and \((3 \times 3)\) box must be unique. In this puzzle we don't see that some cells already have given values: we need to fill-in all the cells. The solution for the above puzzle is:
Using the standard decision variables for Sudoku puzzles: \[x_{i,j,k} = \begin{cases} 1 & \text{if cell $(i,j)$ has value $k$} \\ 0 & \text{otherwise}\end{cases}\] we can solve this easily with a MIP model. In the model below we use two data structures:
There is no objective in this model: we are just looking for a feasible integer solution.
The GAMS model can look like:
For more information on how I populated the set amap see [2]. The results are:
Sudokus are typically solved in the presolver: the MIP solver does not need to do any Simplex iterations or Branch-and-Bound nodes. We see the same thing here. The Cplex solver log shows:
CBC is also able to solve this without much sweat:
I am not sure why the feasibility pump was invoked here. I suspect CBC's presolver did not remove all variables and equations.
We can easily prove this solution is unique, by adding a cut that forbids this solution, and resolving. I added to the model:
equation cut;
cut.. sum((i,j,k),(2*x.l(i,j,k)-1)*x(i,j,k)) =l= sum((i,j,k),x.l(i,j,k))-1;
Indeed this solution is unique: adding the cut made the model infeasible.
A different approach would be to add the cages to the amap set (and the set a) and then rewrite uniqueness constraint as:
I prefer the first approach, as this is a stronger formulation (it certainly makes life easier for the presolver: with the second approach the model was not completely eliminated by the presolver).
For our sample problem this constraint cunique does not change the solution. This is good news as we already established the solution was unique. Adding this constraint confirmed our solution already obeyed this rule.
| Killer Sudoku (from [1]) |
| Solution (from [1]) |
- a mapping \(\mathit{amap}(a,i,j)\) between area \(a\) (an area is a row, column or box) and cells \((i,j)\): \(\mathit{amap}(a,i,j)\) is \(\mathit{true}\) if cell \((i,j)\) belongs to area \(a\),
- a similar mapping \(\mathit{cmap}(c,i,j)\) between a cage \(c\) and cell numbers \((i,j)\).
| Killer Sudoku model |
|---|
| \[\begin{align} & \sum_k \color{DarkRed} x_{i,j,k} = 1 && \forall i,j && \text{One value per cell}\\ & \sum_{i,j| \color{DarkBlue}{\mathit{amap}}(a,i,j)} \color{DarkRed} x_{i,j,k} = 1 && \forall a,k && \text{Unique values in each area} \\ & \sum_{i,j,k| \color{DarkBlue}{\mathit{cmap}}(c,i,j)} \color{DarkBlue} k \cdot \color{DarkRed} x_{i,j,k} = \color{DarkBlue} {\mathit{CageSum}}_c && \forall c && \text{Cell values add up to cage sum} \\ & \color{DarkRed} x_{i,j,k} \in \{0,1\} \\ \end{align}\] |
There is no objective in this model: we are just looking for a feasible integer solution.
Implementation
The GAMS model can look like:
set a 'area'/i1*i9,j1*j9,b1*b9/ i(a) 'rows'/i1*i9/ j(a) 'columns'/j1*j9/ b(a) 'boxes'/b1*b9/ amap(a,i,j) 'mapping area<->cells'-> k 'values'/1*9/ ; * * populate amap * rows, columns, and boxes a.k.a nonets * amap(i,i,j) = yes; amap(j,i,j) = yes; amap(b,i,j) = ord(b) = 3*ceil(ord(i)/3) + ceil(ord(j)/3) - 3; display amap; * * cages * sets c 'cages'/cage1*cage29/ cmap(c,i,j) 'mapping cage<->cells'-> ; table cageno(i,j) 'cage numbering' j1 j2 j3 j4 j5 j6 j7 j8 j9 i1 1 1 2 2 2 3 4 5 6 i2 7 7 8 8 3 3 4 5 6 i3 7 7 9 9 3 10 11 11 6 i4 12 13 13 9 14 10 11 15 6 i5 12 16 16 17 14 10 15 15 18 i6 19 16 20 17 14 21 22 22 18 i7 19 20 20 17 23 21 21 24 24 i8 19 25 26 23 23 27 27 24 24 i9 19 25 26 23 28 28 28 29 29 ; cmap(c,i,j) = ord(c)=cageno(i,j); parameter cagesum(c) / cage1 3, cage11 20, cage21 20 cage2 15, cage12 6, cage22 6 cage3 22, cage13 14, cage23 10 cage4 4, cage14 17, cage24 14 cage5 16, cage15 17, cage25 8 cage6 15, cage16 13, cage26 16 cage7 25, cage17 20, cage27 15 cage8 17, cage18 12, cage28 13 cage9 9, cage19 27, cage29 17 cage10 8, cage20 6 /; binaryvariable x(i,j,k); variable z 'obj'; equation cellvalue(i,j) 'one value per cell' unique(a,k) 'unique values in each area' cagesums(c) 'summation of cage cells' obj 'dummy objective' ; cellvalue(i,j).. sum(k, x(i,j,k)) =e= 1; unique(a,k).. sum(amap(a,i,j),x(i,j,k)) =e= 1; cagesums(c).. sum((cmap(c,i,j),k), k.val*x(i,j,k)) =e= cagesum(c); obj.. z =e= 0; model killer /all/; solve killer using mip minimizing z; parameter v(i,j) 'values'; v(i,j) = sum(k, k.val*x.l(i,j,k)); option v:0; display v; |
For more information on how I populated the set amap see [2]. The results are:
---- 73 PARAMETER v values
j1 j2 j3 j4 j5 j6 j7 j8 j9
i1 215647398
i2 368952174
i3 794381652
i4 586274931
i5 142593867
i6 973816425
i7 821739546
i8 659428713
i9 437165289
Sudokus are typically solved in the presolver: the MIP solver does not need to do any Simplex iterations or Branch-and-Bound nodes. We see the same thing here. The Cplex solver log shows:
MIP Presolve eliminated 347 rows and 724 columns. MIP Presolve modified 885 coefficients. Aggregator did 6 substitutions. All rows and columns eliminated. Presolve time = 0.03 sec. (5.05 ticks) Proven optimal solution. MIP Solution: 0.000000 (0 iterations, 0 nodes) |
CBC is also able to solve this without much sweat:
Calling CBC main solution routine... No integer variables out of 560 objects (560 integer) have costs branch on satisfied N create fake objective Y random cost Y Integer solution of 0 found by feasibility pump after 0 iterations and 0 nodes (0.51 seconds) Search completed - best objective 0, took 0 iterations and 0 nodes (0.54 seconds) |
I am not sure why the feasibility pump was invoked here. I suspect CBC's presolver did not remove all variables and equations.
Multiple solutions?
We can easily prove this solution is unique, by adding a cut that forbids this solution, and resolving. I added to the model:
equation cut;
cut.. sum((i,j,k),(2*x.l(i,j,k)-1)*x(i,j,k)) =l= sum((i,j,k),x.l(i,j,k))-1;
Indeed this solution is unique: adding the cut made the model infeasible.
Unique values in a cage
It is not clear to me, if we need to add a constraint that says: all values within a cage have to be unique. Adding this constraint is not difficult: \[\sum_{i,j|\mathit{cmap}(c,i,j)} x_{i,j,k} \le 1 \> \>\forall c,k\] or in GAMS:
cunique(c,k).. sum(cmap(c,i,j),x(i,j,k)) =l= 1;
A different approach would be to add the cages to the amap set (and the set a) and then rewrite uniqueness constraint as:
unique(a,k).. sum(amap(a,i,j),x(i,j,k)) =l= 1;
I prefer the first approach, as this is a stronger formulation (it certainly makes life easier for the presolver: with the second approach the model was not completely eliminated by the presolver).
For our sample problem this constraint cunique does not change the solution. This is good news as we already established the solution was unique. Adding this constraint confirmed our solution already obeyed this rule.
References
- Killer Sudoku, https://en.wikipedia.org/wiki/Killer_sudoku
- MIP Modeling: from Sudoku to KenKen via Logarithms, https://yetanothermathprogrammingconsultant.blogspot.com/2016/10/mip-modeling-from-sudoku-to-kenken.html