Go语言实现的最简单数独解法

认真发呆
认真发呆
发布时间:2026-10-14| 5人浏览过
原创

本文介绍了一种基于Go语言实现的数独自动求解方法。核心思想在于维护每个空白格的“可能值”列表,并通过遍历行、列和宫块不断剔除已存在的数字。当一个格子内仅剩一个可能值时,即可将其确定为最终答案,并以此为基础更新其他相关格子的候选列表,循环迭代直到完成解题。

数独求解的基本思路

数独的规则要求每一行、每一列以及每一个3x3的九宫格内,数字1到9均只能出现一次。要编写一个简单的求解程序,我们可以采用以下逻辑:

  1. 初始化候选表:对于所有初始为空(值为0)的格子,预先填入1-9作为可能的取值;对于已填入数字的格子,其可能值列表仅包含该数字本身。
  2. 排除法推导:遍历整个棋盘,对于每个空白格,检查其所在的行、列和九宫格中已经确定的数字,将这些数字从该格子的“可能值列表”中剔除。
  3. 确定唯一值:如果在剔除过程中,某个格子的可能值列表长度变为1,说明该位置的值是唯一确定的。
  4. 循环迭代:一旦有格子被确定,会引发连锁反应,需要重新对其他受影响的格子进行排除操作。不断循环此过程,直到不再有新的确定值产生为止。

代码实现步骤

第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语言编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。