# convert single index v_i to i,j
<- function(v_i) {
M_ind = 9 # dim of sudoku
i = ifelse(v_i%%i,v_i%%i,i)
i_m return(c(i_m,ceiling(v_i/i)))
}
R Programming
Sudoku
Helper functions
M_ind
converts between single matrix index and x,y indices
Getting the 3x3 “boxes” of the sudoku:
# create boxes
<- matrix(
boxes sapply(list(1:3,4:6,7:9),rep,3,each=3),
9,byrow = TRUE
)
Logic
Checking if the number at x,y is unique in its row, column and box.
<- function(X,Y,board,boxes) {
is_valid <- boxes[X,Y]
box if (any(duplicated(board[X,],incomparables = 0))) {
return(FALSE)
else if (any(duplicated(board[,Y],incomparables = 0))) {
} return(FALSE)
else if (any(duplicated(board[boxes==box],incomparables = 0))){
} return(FALSE)
else {
} return(TRUE)
} }
Now the depth first search algorithm.
<- function(sudoku) {
solve_sudoku = which(sudoku == 0) # obtain empty pos
empty = length(empty) + 1 # steps
S = 1 # Step
N while (N < S) {
= M_ind(empty[N]) # row; col index of empty pos
i while (TRUE) {
= sudoku[empty[N]] + 1
sudoku[empty[N]] = is_valid(X=i[1],Y=i[2],board=sudoku,boxes = boxes)
check if(sudoku[empty[N]] > 9){
= 0 # reset
sudoku[empty[N]] = N - 1 # go back
N break
}if(check) {
= N + 1 # proceed
N break
}
}
}return(sudoku)
}
Testing
Lets test the implementation on three example sudoku with increasing difficulty
6 | 5 | 2 | 9 | |||||
6 | 8 | |||||||
9 | 2 | 7 | ||||||
2 | 9 | 3 | 1 | |||||
1 | 7 | 5 | 3 | 4 | ||||
4 | 9 | 8 | 2 | |||||
6 | 7 | 2 | ||||||
3 | 9 | |||||||
4 | 6 | 5 | 9 |
1 | 5 | 8 | ||||||
2 | 8 | 3 | 7 | |||||
4 | 2 | |||||||
1 | ||||||||
5 | 4 | |||||||
8 | 3 | 9 | 5 | |||||
9 | 5 | 3 | 4 | |||||
7 | 6 | |||||||
2 |
5 | 3 | |||||||
8 | 2 | |||||||
7 | 1 | 5 | ||||||
4 | 5 | 3 | ||||||
1 | 7 | 6 | ||||||
3 | 2 | 8 | ||||||
6 | 5 | 9 | ||||||
4 | 3 | |||||||
9 | 7 |
The left one is rather easy, the middle one medium and the right one is made by the Finnish Mathematician Inkala and is very hard to solve.
Lets run it:
6 | 5 | 2 | 7 | 3 | 8 | 9 | 4 | 1 |
4 | 7 | 1 | 9 | 6 | 5 | 2 | 3 | 8 |
8 | 9 | 3 | 2 | 1 | 4 | 5 | 7 | 6 |
2 | 4 | 9 | 3 | 8 | 1 | 7 | 6 | 5 |
1 | 8 | 7 | 6 | 5 | 2 | 3 | 9 | 4 |
5 | 3 | 6 | 4 | 7 | 9 | 8 | 1 | 2 |
9 | 6 | 8 | 5 | 4 | 7 | 1 | 2 | 3 |
3 | 2 | 5 | 1 | 9 | 6 | 4 | 8 | 7 |
7 | 1 | 4 | 8 | 2 | 3 | 6 | 5 | 9 |
4 | 9 | 1 | 7 | 6 | 5 | 8 | 3 | 2 |
2 | 8 | 5 | 1 | 9 | 3 | 6 | 7 | 4 |
3 | 7 | 6 | 4 | 2 | 8 | 1 | 9 | 5 |
5 | 1 | 7 | 6 | 3 | 4 | 2 | 8 | 9 |
6 | 2 | 9 | 8 | 5 | 1 | 7 | 4 | 3 |
8 | 3 | 4 | 9 | 7 | 2 | 5 | 6 | 1 |
9 | 5 | 8 | 3 | 1 | 6 | 4 | 2 | 7 |
1 | 4 | 3 | 2 | 8 | 7 | 9 | 5 | 6 |
7 | 6 | 2 | 5 | 4 | 9 | 3 | 1 | 8 |
1 | 4 | 5 | 3 | 2 | 7 | 6 | 9 | 8 |
8 | 3 | 9 | 6 | 5 | 4 | 1 | 2 | 7 |
6 | 7 | 2 | 9 | 1 | 8 | 5 | 4 | 3 |
4 | 9 | 6 | 1 | 8 | 5 | 3 | 7 | 2 |
2 | 1 | 8 | 4 | 7 | 3 | 9 | 5 | 6 |
7 | 5 | 3 | 2 | 9 | 6 | 4 | 8 | 1 |
3 | 6 | 7 | 5 | 4 | 2 | 8 | 1 | 9 |
9 | 8 | 4 | 7 | 6 | 1 | 2 | 3 | 5 |
5 | 2 | 1 | 8 | 3 | 9 | 7 | 6 | 4 |
we get the solution to all three lvls of sudoku!
How long did this approach take to come up with the solutions?
::microbenchmark(
microbenchmarksolve_sudoku(easy),
solve_sudoku(medium),
solve_sudoku(Inkala),
times = 1
)
Unit: milliseconds
expr min lq mean median uq max neval
solve_sudoku(easy) 42.17106 42.17106 42.17106 42.17106 42.17106 42.17106 1
solve_sudoku(medium) 154.17445 154.17445 154.17445 154.17445 154.17445 154.17445 1
solve_sudoku(Inkala) 2733.38598 2733.38598 2733.38598 2733.38598 2733.38598 2733.38598 1
Thats it!