# convert single index v_i to i,j
M_ind <- function(v_i) {
i = 9 # dim of sudoku
i_m = ifelse(v_i%%i,v_i%%i,i)
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
boxes <- matrix(
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.
is_valid <- function(X,Y,board,boxes) {
box <- boxes[X,Y]
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.
solve_sudoku <- function(sudoku) {
empty = which(sudoku == 0) # obtain empty pos
S = length(empty) + 1 # steps
N = 1 # Step
while (N < S) {
i = M_ind(empty[N]) # row; col index of empty pos
while (TRUE) {
sudoku[empty[N]] = sudoku[empty[N]] + 1
check = is_valid(X=i[1],Y=i[2],board=sudoku,boxes = boxes)
if(sudoku[empty[N]] > 9){
sudoku[empty[N]] = 0 # reset
N = N - 1 # go back
break
}
if(check) {
N = N + 1 # proceed
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::microbenchmark(
solve_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!