Quantcast
Channel: Yet Another Math Programming Consultant
Viewing all articles
Browse latest Browse all 804

Numbrix puzzle via MIP

$
0
0

The puzzle of numbrix [1,2] is simple to state.


  • We have a 9x9 board with 81 cells. 
  • We need to fill each cell with a unique value between 1 and 81.
  • There are some prefilled cells.
  • We should be able to start with the cell containing value 1 and then following a path (only going one up, down, left or right) to the next values and thus arriving at the cell with value 81.  





Example

Below is an example of an input (the "given" cells) and a solution. The colors may help to see that we have a path from the cell with value 1 to the cell with value 81.


Of course, we want to see if we can create a MIP model for this.

Like in Sudoku models we use a binary variable \(\color{darkred}x_{i,j,k} \in \{0,1\}\) define by \[\color{darkred}x_{i,j,k} = \begin{cases} 1 & \text{if cell $(i,j)$ has value $k$} \\ 0 & \text{otherwise}\end{cases}\] This introduces a large number of binary variables (\(9 \times 9 \times 81=6,561\)), but it makes the model much simpler for use with a MIP model. This is really the big decision: once you have main variables defined, things follow from there.

We need to formulate just three constraints:

  1. Each cell can contain just one value. \[\sum_k \color{darkred}x_{i,j,k} = 1 \>\> \forall i,j\]
  2. Each value should occur exactly once. \[\sum_{i,j}\color{darkred}x_{i,j,k} = 1 \>\> \forall k\]
  3. We need: if \(\color{darkred}x_{i,j,k} = 1\) then one of its neighbors should contain value \(k+1\). A neighbor is defined as going one up, down, left or right. This can be formulated as an implication \[\color{darkred}x_{i,j,k} = 1 \Rightarrow  \color{darkred}x_{i-1,j,k+1}+\color{darkred}x_{i+1,j,k+1}+\color{darkred}x_{i,j-1,k+1}+\color{darkred}x_{i,j+1,k+1} = 1\] Or, as inequality: \[\color{darkred}x_{i-1,j,k+1}+\color{darkred}x_{i+1,j,k+1}+\color{darkred}x_{i,j-1,k+1}+\color{darkred}x_{i,j+1,k+1} \ge \color{darkred}x_{i,j,k}\]

This is all! 

Note that I don't explicitly form a path in the model. Just the constraint about "next k should be a neighbor" is enough. So, our complete model looks like:

Model
\[\begin{align} & \sum_k \color{darkred}x_{i,j,k} = 1 && \forall i,j \\ & \sum_{i,j}\color{darkred}x_{i,j,k} = 1 && \forall k \\ & \color{darkred}x_{i-1,j,k+1}+\color{darkred}x_{i+1,j,k+1}+\color{darkred}x_{i,j-1,k+1}+\color{darkred}x_{i,j+1,k+1} \ge \color{darkred}x_{i,j,k} && \forall i,j, \forall k\lt 81 \end{align}\]


In the last constraint, I am careful about \(k\) outside the board, but less so about \(i\) and \(j\). The reason is, that with GAMS, when addressing using leads/lags outside of the domain, a zero is returned. That is exactly what we need here. The GAMS model can look like:


sets
  i
'rows'    /i1*i9/
  j
'columns'/j1*j9/
  k
'values'  /1*81/
;

table board(i,j)
       
j1 j2 j3 j4 j5 j6 j7 j8 j9
 
i1
 
i2       25 20 13 14 15  8 41
 
i3       26                40
 
i4        1                39
 
i5       30                38
 
i6       67                37
 
i7       70                50
 
i8       73 72 79 60 61 52 53
 
i9
;


binaryvariable x(i,j,k);
variable dummy;

equations
   onevalue(i,j)
'each cell has one value (between 1 and 81)'
   unique(k)    
'each value must be in exactly one cell'
   next(i,j,k)  
'x(i,j,k)=1 => a neighbor is k+1'
   obj          
'dummy'
;

obj.. dummy =e= 0;
onevalue(i,j)..
sum(k, x(i,j,k)) =e= 1;
unique(k)..    
sum((i,j),x(i,j,k)) =e= 1;
next(i,j,k+1).. x(i-1,j,k+1)+x(i+1,j,k+1)+x(i,j-1,k+1)+x(i,j+1,k+1) =g= x(i,j,k);

x.fx(i,j,k)$(board(i,j)=k.val) = 1;

model m /all/;
option mip=cplex;
solve m minimizing dummy using mip;
display x.l;

parameter result(i,j);
result(i,j) =
sum(k, k.val*x.l(i,j,k));
option result:0;
display result;



This model is solved completely in preprocessing. Cplex needs 0 iterations and 0 nodes. The output looks like:

----     46 PARAMETER result  

j1 j2 j3 j4 j5 j6 j7 j8 j9

i1 23222112111094243
i2 24252013141584144
i3 27261918171674045
i4 281234563946
i5 293031323334353847
i6 686766656463363748
i7 697071808162515049
i8 747372796061525354
i9 757677785958575655


 Notes:

  • We don't need an optcr (gap) setting: we are looking for a feasible solution.
  • The k+1 in next(i,j,k+1) means that we skip the last k when generating this equation.
  • We could have defined equation next in terms of \(k-1\) and \(k\). Then you would not need the k+1 in next(i,j,k+1). I.e., next(i,j,k).. x(i-1,j,k)+x(i+1,j,k)+x(i,j-1,k)+x(i,j+1,k) =g= x(i,j,k-1); 
  • We were lucky with addressing \(i\pm1, j\pm 1\). GAMS returning zero there when outside the domain is exactly what we want.
  • Interestingly when solving as an RMIP (Relaxed MIP), Cplex will need to do a few iterations. It will still find the integer solution. I suspect this is just by accident.
  • I think this model looks quite nice!


References


  1. https://parade.com/numbrix/
  2. https://www.youtube.com/watch?v=FypFESO6rVo


Viewing all articles
Browse latest Browse all 804

Trending Articles