递归解数独

本文介绍了如何使用递归和回溯法解决数独问题。通过遍历数独矩阵,记录每行、每列和每个九宫格的已填数字状态,避免重复填充。在递归过程中,检查当前位置能填的数字并更新状态,若无法填入则回溯。算法复杂度为 O(9^{9 imes 9})。
摘要由CSDN通过智能技术生成

我们可以考虑按照「行优先」的顺序依次枚举每一个空白格中填的数字,通过递归 + 回溯的方法枚举所有可能的填法。当递归到最后一个空白格后,如果仍然没有冲突,说明我们找到了答案;在递归的过程中,如果当前的空白格不能填下任何一个数字,那么就进行回溯。

由于每个数字在同一行、同一列、同一个九宫格中只会出现一次,因此我们可以使用 \textit{line}[i]line[i],\textit{column}[j]column[j],\textit{block}[x][y]block[x][y] 分别表示第 ii 行,第 jj 列,第 (x, y)(x,y) 个九宫格中填写数字的情况。在下面给出的三种方法中,我们将会介绍两种不同的表示填写数字情况的方法。

九宫格的范围为 0 \leq x \leq 20≤x≤2 以及 0 \leq y \leq 20≤y≤2。
具体地,第 ii 行第 jj 列的格子位于第 (\lfloor i/3 \rfloor, \lfloor j/3 \rfloor)(⌊i/3⌋,⌊j/3⌋) 个九宫格中,其中 \lfloor u \rfloor⌊u⌋ 表示对 uu 向下取整。

由于这些方法均以递归 + 回溯为基础,算法运行的时间(以及时间复杂度)很大程度取决于给定的输入数据,而我们很难找到一个非常精确的渐进紧界。因此这里只给出一个较为宽松的渐进复杂度上界 O(9^{9 \times 9})O(9 
9×9
 ),即最多有 9 \times 99×9 个空白格,每个格子可以填 [1, 9][1,9] 中的任意整数。

递归
思路

最容易想到的方法是用一个数组记录每个数字是否出现。由于我们可以填写的数字范围为 [1, 9][1,9],而数组的下标从 00 开始,因此在存储时,我们使用一个长度为 99

数独问题可以使用回溯算法来决,不一定需要递归。下面是一个不使用递归数独算法: 1. 初始化一个空的9x9的数独矩阵,并将待填充的位置标记为0。 2. 创建一个栈stack,用于存储待填充的位置。 3. 找到数独矩阵中的一个空格(值为0),将其位置压入栈stack中。 4. 对于栈中的每个待填充位置,从1到9尝试填充数字,判断该位置是否符合数独规则,如果符合,则将该位置的值更新为填充的数字,并将该位置的所有相关位置(同行、同列、同宫)的候选数字集合更新。 5. 如果填充的数字与已有的数字冲突,或者无法填充数字,则回溯到上一个待填充位置,将该位置的值重新标记为0,并将其从栈stack中弹出。 6. 如果栈stack为空,则表示数独已经被成功填充,算法结束。 下面是一个Python代码实现: ```python def solve_sudoku(board): stack = [] for i in range(9): for j in range(9): if board[i][j] == 0: stack.append((i, j)) while stack: i, j = stack[-1] found = False for num in range(board[i][j] + 1, 10): if is_valid(board, i, j, num): board[i][j] = num update_candidates(board, i, j, num) stack.pop() found = True break if not found: board[i][j] = 0 update_candidates(board, i, j, 0) stack.pop() if not stack: return False i, j = stack[-1] return True def is_valid(board, i, j, num): for k in range(9): if board[i][k] == num or board[k][j] == num or board[(i//3)*3+k//3][(j//3)*3+k%3] == num: return False return True def update_candidates(board, i, j, num): for k in range(9): if board[i][k] == 0: board[i][k] = {1,2,3,4,5,6,7,8,9} - set(board[i]) - get_col(board, k) - get_box(board, i//3, k//3) if len(board[i][k]) == 1: board[i][k] = board[i][k].pop() if board[k][j] == 0: board[k][j] = {1,2,3,4,5,6,7,8,9} - set(board[k]) - get_col(board, j) - get_box(board, k//3, j//3) if len(board[k][j]) == 1: board[k][j] = board[k][j].pop() def get_col(board, j): return [board[i][j] for i in range(9)] def get_box(board, i, j): return [board[m][n] for m in range(i*3, (i+1)*3) for n in range(j*3, (j+1)*3)] ``` 其中,is_valid函数用于判断填充的数字是否符合数独规则;update_candidates函数用于更新每个位置的候选数字集合。如果候选数字集合只有一个元素,则直接填充该数字。 该算法的时间复杂度为指数级别,但是在实际应用中,由于数独中待填充的位置较少,因此算法的实际运行时间较短。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值