提问人:stats_noob 提问时间:9/6/2023 最后编辑:Konrad Rudolphstats_noob 更新时间:9/16/2023 访问量:445
手工解决数独
Solving a Sudoku by Hand
问:
假设我有以下数独:
problem <- matrix(c(
5, 3, 0, 0, 7, 0, 0, 0, 0,
6, 0, 0, 1, 9, 5, 0, 0, 0,
0, 9, 8, 0, 0, 0, 0, 6, 0,
8, 0, 0, 0, 6, 0, 0, 0, 3,
4, 0, 0, 8, 0, 3, 0, 0, 1,
7, 0, 0, 0, 2, 0, 0, 0 ,6,
0 ,6 ,0 ,0 ,0 ,0 ,2 ,8 ,0,
0 ,0 ,0 ,4 ,1 ,9 ,0 ,0 ,5,
0 ,0 ,0 ,0 ,8 ,0 ,0 ,7 ,9
), nrow = 9)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 5 6 0 8 4 7 0 0 0
[2,] 3 0 9 0 0 0 6 0 0
[3,] 0 0 8 0 0 0 0 0 0
[4,] 0 1 0 0 8 0 0 4 0
[5,] 7 9 0 6 0 2 0 1 8
[6,] 0 5 0 0 3 0 0 9 0
[7,] 0 0 0 0 0 0 2 0 0
[8,] 0 0 6 0 0 0 8 0 7
[9,] 0 0 0 3 1 6 0 5 9
我正在尝试手动编写一个程序(例如回溯)来解决这个数独。
目前,我想到了以下两个可能有用的想法:
1) 对于给定的行或给定的列 - 哪些数字是有效的选择?
以下代码查看第一列中哪些可能的数字是有效选项:
y = 1:9
setdiff(y, problem[1,])
[1] 1 2 3 9
2) 在任何时候,给定的行或列是否会导致违规?(即同一数字不止一次 - 不包括 0)
#TRUE = no violation, FALSE = violation
check_vector <- function(v) {
for (i in 1:9) {
if (sum(v == i) > 1) {
return(FALSE)
}
}
return(TRUE)
}
# no violation
v1 = c(5, 3, 0, 0, 7, 0, 0, 0, 0)
# violation (3,3)
v2 = c(5, 3, 3, 0, 7, 0, 0, 0, 0)
> check_vector(v1)
[1] TRUE
> check_vector(v2)
[1] FALSE
我的问题:我不确定如何一起使用这些函数来回溯数独并填写所有数字。有人可以告诉我如何做到这一点吗?
谢谢!
注意:如果可能的话,我希望最终答案使用我已经编写的代码
答:
6赞
ThomasIsCoding
9/6/2023
#1
如果你想在不使用其他包的情况下解决它,你可以尝试下面的代码,它有点使用“回溯”的想法(但不完全相同)。
法典
请注意,下面的代码只是一个实现,例如,不够优化。您可能会在那里找到一些提示,并根据您的口味进一步优化它。
sudoku <- function(m) {
# check valid values to fill in the given position of matrix
checker <- function(mat, i, j) {
iblk <- 3 * (ceiling(i / 3) - 1) + (1:3)
jblk <- 3 * (ceiling(j / 3) - 1) + (1:3)
u <- unique(c(mat[i, ], mat[, j], mat[iblk, jblk]))
setdiff(1:9, u)
}
# help to generate all possible matrices
helper <- function(m, idx) {
i <- (idx - 1) %/% 9 + 1
j <- (idx - 1) %% 9 + 1
if (m[i, j] == 0) {
u <- checker(m, i, j)
lapply(u, \(x) {
m[i, j] <- x
m
})
} else {
list(m)
}
}
# initial value
lst <- list(m)
cnt <- 1
repeat {
lst <- unlist(
lapply(
lst,
helper,
idx = cnt
),
recursive = FALSE
)
if (cnt == length(m)) {
return(lst[[1]])
}
cnt <- cnt + 1
}
}
输出
> (solution <- sudoku(problem))
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 5 6 1 8 4 7 9 2 3
[2,] 3 7 9 5 2 1 6 8 4
[3,] 4 2 8 9 6 3 1 7 5
[4,] 6 1 3 7 8 9 5 4 2
[5,] 7 9 4 6 5 2 3 1 8
[6,] 8 5 2 1 3 4 7 9 6
[7,] 9 3 5 4 7 8 2 6 1
[8,] 1 4 6 2 9 5 8 3 7
[9,] 2 8 7 3 1 6 4 5 9
评论
0赞
asd-tm
9/6/2023
我很遗憾地告诉你,不幸的是,一些解决方案是错误的(根据数独规则,OP没有完全列出)。所有数字在九个 3x3 象限中的每一个象限中都必须是唯一的(例如,problems[1:3, 1:3] 或 problems[4:6, 7:9])。和。示例问题只有一个有效的解决方案。solutions[[1]][1, 3] == solutions[[1]][3, 1]
0赞
ThomasIsCoding
9/6/2023
@asd-tm啊哈,谢谢你的提醒!我忽略了函数中的象限约束。现已修复!checker
1赞
asd-tm
9/6/2023
凉!+1.代码的优点是,如果问题有,它能够找到 >1 解决方案。干杯!
5赞
asd-tm
9/6/2023
#2
我会提出以下解决方案:
repeat {
possible <- rep(1:9, each = 9^2) |>
array(dim = c(9,9,9))
for(j in 1:9) {
for(i in 1:9) {
if (!is.na(problem[i,j]) && problem[i,j] != 0) {
possible[i,j,-problem[i,j]] <- NA
possible[-i,j,problem[i,j]] <- NA
possible[i,-j,problem[i,j]] <- NA
possible[(floor((i-1)/3)*3 + 1):(floor((i-1)/3)*3 + 3),
(floor((j-1)/3)*3 + 1):(floor((j-1)/3)*3 + 3),
problem[i,j]] <- NA
possible[i,j,problem[i,j]] <- problem[i,j]
}
}
}
collapsed_problem <- matrix(rep(NA, 81) |> as.numeric(), nrow = 9)
for(j in 1:9) {
for(i in 1:9) {
if (table(possible[i, j, ]) |> length() == 1) {
collapsed_problem[i, j] <- table(possible[i, j, ]) |> attr("dimnames") |> unlist() |> as.numeric()
}
for(k in 1:9) {
if (table(possible[(floor((i-1)/3)*3+1):(floor((i-1)/3)*3+3),
(floor((j-1)/3)*3+1):(floor((j-1)/3)*3+3),
k] ) == 1) {
offs <- which(
possible[(floor((i-1)/3)*3+1):(floor((i-1)/3)*3+3),
(floor((j-1)/3)*3+1):(floor((j-1)/3)*3+3),
k] == k,
arr.ind = T)
collapsed_problem[floor((i-1)/3)*3 + offs[1], floor((j-1)/3)*3 + offs[2]] <- k
}
}
}
}
print(collapsed_problem)
if (sum(problem, na.rm = T) == sum(collapsed_problem, na.rm = T) ||
!any(is.na(collapsed_problem))
) {
problem <- collapsed_problem
break
}
problem <- collapsed_problem
}
此代码将迭代返回以下结果(我决定使用 NA 而不是 0,因为 NA 假定单元格中没有值,而 0 是特定值):
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 5 6 NA 8 4 7 NA NA NA
[2,] 3 NA 9 NA NA NA 6 8 NA
[3,] NA NA 8 NA 6 3 NA NA NA
[4,] NA 1 NA NA 8 NA NA 4 NA
[5,] 7 9 NA 6 5 2 NA 1 8
[6,] 8 5 NA NA 3 NA 7 9 NA
[7,] NA NA 5 NA NA 8 2 NA 1
[8,] NA NA 6 NA NA NA 8 3 7
[9,] NA NA NA 3 1 6 4 5 9
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 5 6 NA 8 4 7 NA 2 NA
[2,] 3 NA 9 NA 2 NA 6 8 NA
[3,] NA NA 8 9 6 3 NA 7 NA
[4,] 6 1 NA 7 8 9 NA 4 NA
[5,] 7 9 NA 6 5 2 3 1 8
[6,] 8 5 NA NA 3 NA 7 9 NA
[7,] NA 3 5 NA NA 8 2 6 1
[8,] 1 NA 6 NA NA NA 8 3 7
[9,] 2 8 NA 3 1 6 4 5 9
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 5 6 1 8 4 7 9 2 3
[2,] 3 7 9 NA 2 NA 6 8 NA
[3,] 4 2 8 9 6 3 NA 7 NA
[4,] 6 1 3 7 8 9 5 4 NA
[5,] 7 9 4 6 5 2 3 1 8
[6,] 8 5 NA NA 3 NA 7 9 6
[7,] 9 3 5 4 7 8 2 6 1
[8,] 1 4 6 2 9 NA 8 3 7
[9,] 2 8 7 3 1 6 4 5 9
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 5 6 1 8 4 7 9 2 3
[2,] 3 7 9 NA 2 NA 6 8 4
[3,] 4 2 8 9 6 3 1 7 5
[4,] 6 1 3 7 8 9 5 4 2
[5,] 7 9 4 6 5 2 3 1 8
[6,] 8 5 2 1 3 4 7 9 6
[7,] 9 3 5 4 7 8 2 6 1
[8,] 1 4 6 2 9 5 8 3 7
[9,] 2 8 7 3 1 6 4 5 9
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 5 6 1 8 4 7 9 2 3
[2,] 3 7 9 5 2 1 6 8 4
[3,] 4 2 8 9 6 3 1 7 5
[4,] 6 1 3 7 8 9 5 4 2
[5,] 7 9 4 6 5 2 3 1 8
[6,] 8 5 2 1 3 4 7 9 6
[7,] 9 3 5 4 7 8 2 6 1
[8,] 1 4 6 2 9 5 8 3 7
[9,] 2 8 7 3 1 6 4 5 9
并最终解决问题。
评论
1赞
ThomasIsCoding
9/6/2023
不错的解决方案,+1!获得一个解决方案非常高效,而且,典型的“回溯”编码风格,干杯!
下一个:数独中的获胜者检查器
评论
sudoku::solveSudoku(problem)
check_vector
check_vector