Go语言实现的最简单数独解法
本文介绍了一种基于Go语言实现的数独自动求解方法。核心思想在于维护每个空白格的“可能值”列表,并通过遍历行、列和宫块不断剔除已存在的数字。当一个格子内仅剩一个可能值时,即可将其确定为最终答案,并以此为基础更新其他相关格子的候选列表,循环迭代直到完成解题。
数独求解的基本思路
数独的规则要求每一行、每一列以及每一个3x3的九宫格内,数字1到9均只能出现一次。要编写一个简单的求解程序,我们可以采用以下逻辑:
- 初始化候选表:对于所有初始为空(值为0)的格子,预先填入1-9作为可能的取值;对于已填入数字的格子,其可能值列表仅包含该数字本身。
- 排除法推导:遍历整个棋盘,对于每个空白格,检查其所在的行、列和九宫格中已经确定的数字,将这些数字从该格子的“可能值列表”中剔除。
- 确定唯一值:如果在剔除过程中,某个格子的可能值列表长度变为1,说明该位置的值是唯一确定的。
- 循环迭代:一旦有格子被确定,会引发连锁反应,需要重新对其他受影响的格子进行排除操作。不断循环此过程,直到不再有新的确定值产生为止。
代码实现步骤
第1步:定义数据结构与全局变量
程序首先定义了一个切片类型 node 来表示整数的可能性列表。使用一个9x9的全局二维切片数组 sudokuMay 来存储每个位置的候选数字。同时,在代码开头预置了一个示例数独题目 Sudoku,其中0代表待填充的空位。
第2步:初始化候选列表 (inited)
主函数入口调用 inited 函数。该函数遍历预设的题目数组:
- 如果当前位置不为0(即原题已有数字),则将该数字加入
sudokuMay对应位置的列表中。 - 如果位置为0,则先向列表中追加1-9的所有数字,然后调用
excludeMay函数,根据同行、同列、同宫的已有数字对候选列表进行初步过滤。
第3步:执行排除与更新 (Subinit & excludeMay)
这是求解的核心循环部分。在 main 函数中,通过一个 for 循环持续调用 Subinit,直到没有数字发生变化(返回的变化计数为0)。
Subinit 负责再次扫描全盘:
- 若格子已确定值,直接锁定。
- 若格子仍为空,再次调用
excludeMay结合当前盘面的最新状态,剔除多余的候选项。
excludeMay 函数具体执行剔除动作,它会分别检查当前格子所在行的所有列、所在列的所有行,以及所在的3x3九宫格内的所有格子。一旦发现任何冲突的数字,便调用 exclude 从候选列表中移除。
第4步:识别确定解 (sure)
为了判断哪些位置已经完全确定,程序中定义了 sure 函数。它检查 sudokuMay 中的每个节点,如果某个节点的列表长度为1,则该节点的值即为最终的数独数字,并将其转换为对应的整数网格返回。主循环依据是否有新确定的数字来判断是否继续迭代。
第5步:完整代码展示
以下是完整的Go语言源文件内容,可以直接复制到名为 soduku.go 的文件中进行编译和运行:
package main
import (
"fmt"
)
type node []int
var sudokuMay [9][9]node
var Sudoku = [9][9]int{
{0, 0, 0, 0, 0, 0, 8, 0, 0},
{0, 8, 2, 4, 0, 0, 0, 0, 0},
{1, 9, 0, 0, 6, 3, 0, 0, 0},
{0, 5, 0, 0, 8, 0, 7, 0, 0},
{6, 7, 8, 2, 0, 9, 1, 4, 3},
{0, 0, 3, 0, 4, 0, 0, 8, 0},
{0, 0, 0, 6, 2, 0, 0, 9, 4},
{0, 0, 0, 0, 0, 5, 6, 1, 0},
{0, 0, 0, 6, 0, 0, 0, 0, 0}}
func main() {
n := inited(Sudoku)
SudokuSure, _ := sure(sudokuMay)
for n > 0 {
n = Subinit(SudokuSure)
// Output(sudokuMay)
// fmt.Println(n)
SudokuSure, _ = sure(sudokuMay)
}
Output(sudokuMay)
fmt.Println(isEnable(sudokuMay))
// test()
}
func isEnable(tn [9][9]node) bool {
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
if len(tn[i][j]) == 0 {
return false
}
}
}
return true
}
func sure(may [9][9]node) (sure [9][9]int, n int) {
n = 0
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
if len(may[i][j]) == 1 {
sure[i][j] = may[i][j][0]
n++
} else {
sure[i][j] = 0
}
}
}
return
}
func test() {
i, j := 1, 3
fmt.Println(Sudoku[i][j])
for k := ((i / 3) * 3); k < ((i/3)*3)+3; k++ {
for l := ((j / 3) * 3); l < ((j/3)*3)+3; l++ {
fmt.Print(Sudoku[k][l])
}
fmt.Println(" ")
}
}
func inited(Sud [9][9]int) (changeCount int) {
tmp := 0
changeCount = 0
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
if Sud[i][j] != 0 {
sudokuMay[i][j] = append(sudokuMay[i][j], Sud[i][j])
} else {
for k := 0; k < 9; k++ {
sudokuMay[i][j] = append(sudokuMay[i][j], k+1)
}
sudokuMay[i][j], tmp = excludeMay(i, j, sudokuMay[i][j], Sud)
changeCount += tmp
}
}
}
return
}
func Subinit(Sud [9][9]int) (changeCount int) {
tmp := 0
changeCount = 0
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
if Sud[i][j] != 0 {
sudokuMay[i][j][0] = Sud[i][j]
} else {
sudokuMay[i][j], tmp = excludeMay(i, j, sudokuMay[i][j], Sud)
changeCount += tmp
}
}
}
return
}
func excludeMay(ti, tj int, t node, S [9][9]int) (rmay node, changeCount int) {
changeCount = 0
var tmpChangeCount int
for i := 0; i < 9; i++ {
if S[i][tj] != 0 {
t, tmpChangeCount = exclude(t, S[i][tj])
changeCount += tmpChangeCount
}
if S[ti][i] != 0 {
t, tmpChangeCount = exclude(t, S[ti][i])
changeCount += tmpChangeCount
}
}
for k := ((ti / 3) * 3); k < ((ti/3)*3)+3; k++ {
for l := ((tj / 3) * 3); l < ((tj/3)*3)+3; l++ {
if S[k][l] != 0 {
t, tmpChangeCount = exclude(t, S[k][l])
changeCount += tmpChangeCount
}
}
}
rmay = t
return
}
func excludeFirstOne(smay node, n int) (rmay node, changeCount int) {
changeCount = 0
rmay = smay
for i := 0; i < len(smay); i++ {
if smay[i] == n {
changeCount++
rmay = append(smay[:i], smay[i+1:]...)
return
}
if i == len(smay)-1 {
return
}
}
return
}
func exclude(smay node, n int) (tmp node, changeCount int) {
var nc int
changeCount = 0
tmp, nc = excludeFirstOne(smay, n)
for nc > 0 {
tmp, nc = excludeFirstOne(tmp, n)
changeCount++
}
return
}
func Output(sudoku [9][9]node) {
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
fmt.Print(sudokuMay[i][j])
}
fmt.Println("")
}
}
运行结果说明
运行上述代码后,控制台会打印出经过处理后的 sudokuMay 结构体状态,并调用 isEnable 函数验证求解是否成功。如果所有格子的候选列表都不为空且符合逻辑约束,将返回 true。对于更复杂的数独题目,这种纯排除法可能无法直接得出全部答案(可能需要配合试错法/回溯法),但对于大多数入门级数独谜题,这种简单的逻辑推导足以解决问题。
以上就是Go语言实现的最简单数独解法的详细内容,更多关于Go语言编程的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













