-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path01-Puzzles.Rmd
78 lines (57 loc) · 2.65 KB
/
01-Puzzles.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# Puzzles
Here we have classical examples like Sudokus, Graphic Coloring etc.
## Solve Sudokus using MILP
In this vignettes we will solve Sudoku puzzles using MILP. [Sudoku](https://en.wikipedia.org/wiki/Sudoku) in its most popular form is a constraint satisfaction problem and by setting the objective function to $0$ you transform the optimization problem into a pure constraint satistication problem. In this document we will consider Sudokus in a 9x9 grid with 3x3 sub-matrices.
Of course you can formulate an objective function as well that directs the solver towards solutions maximizing a certain linear function.
### The model
The idea is to introduce a binary variable $x$ with three indexes $i, j, k$ that is $1$ if and only if the number $k$ is in cell $i, j$.
```{r, message=FALSE}
library(rmpk)
library(ROI.plugin.glpk)
n <- 9
model <- optimization_model(ROI_optimizer("glpk"))
# The number k stored in position i,j
x <- model$add_variable("x", i = 1:n, j = 1:n, k = 1:9, type = "binary")
# no objective
model$set_objective(0)
# only one number can be assigned per cell
model$add_constraint(sum_expr(x[i, j, k], k = 1:9) == 1, i = 1:n, j = 1:n)
# each number is exactly once in a row
model$add_constraint(sum_expr(x[i, j, k], j = 1:n) == 1, i = 1:n, k = 1:9)
# each number is exactly once in a column
model$add_constraint(sum_expr(x[i, j, k], i = 1:n) == 1, j = 1:n, k = 1:9)
# each 3x3 square must have all numbers
model$add_constraint(sum_expr(x[i, j, k], i = 1:3 + sx, j = 1:3 + sy) == 1,
sx = seq(0, n - 3, 3), sy = seq(0, n - 3, 3), k = 1:9)
model
```
### Solve the model
We will use `glpk` to solve the above model. Note that we haven't fixed any numbers to specific values. That means that the solver will find a valid sudoku without any prior hints.
```{r}
model$optimize()
library(dplyr)
# the following dplyr statement plots a 9x9 matrix
model$get_variable_value(x[i,j,k]) %>%
filter(value > 0) %>%
select(i, j, k) %>%
tidyr::spread(j, k) %>%
select(-i)
```
If you want to solve a concrete sudoku you can fix certain cells to specific values. For example here we solve a sudoku that has the sequence from 1 to 9 in the first 3x3 matrix fixed.
```{r}
model$add_constraint(x[1, 1, 1] == 1)
model$add_constraint(x[1, 2, 2] == 1)
model$add_constraint(x[1, 3, 3] == 1)
model$add_constraint(x[2, 1, 4] == 1)
model$add_constraint(x[2, 2, 5] == 1)
model$add_constraint(x[2, 3, 6] == 1)
model$add_constraint(x[3, 1, 7] == 1)
model$add_constraint(x[3, 2, 8] == 1)
model$add_constraint(x[3, 3, 9] == 1)
model$optimize()
model$get_variable_value(x[i,j,k]) %>%
filter(value > 0) %>%
select(i, j, k) %>%
tidyr::spread(j, k) %>%
select(-i)
```