【发布时间】:2020-08-25 08:30:10
【问题描述】:
[这篇文章反响不佳,所以我提出了一些建议的修改,以尝试改进它以供后代使用。希望对以后发现它的人有所帮助!]
我一直在尝试使用一个简单的数独示例来理解递归/回溯/DFS。我对数独示例本身并不感兴趣,因此根据建议,我已将下面的示例最小化为仅 2x2 数独板,以便专注于让我对递归感到困惑的点(感谢@MisterMiyagi 的建议)。
在下面的代码中,我有一个辅助函数check_board,它接受一个 2x2 矩阵并检查是否有任何数字在其行和列中重复(即检查输入的 2x2 数独是否有效)。然后函数solve_sudoku是我理解的标准DFS/回溯算法,通过选择第一个空位置(由0表示)来解决数独,并尝试其中的值1和2,递归地寻找解决方案.
所以输入[[0,0], [0,2]] 输出应该是[[[2,1],[1,2]],但我收到的是输出False。
@ThierryLathuille 通过注意到代码中的问题来提供帮助:在尝试了每个可能的后代节点之后(在这种情况下,通过尝试两个值 1、2),我错过了“回溯”步骤,需要添加一行重置值为 0,这意味着对于所有后续调用,矩阵中的方格将停留在 2 中(或者,在最初的 9x9 示例中,停留在 9 中):
当您看到您在正方形中尝试的最后一个值无法得出有效的解决方案时,您尝试下一个,直到达到 9。此时,您返回并返回递增前一个方块,但您当前的方块仍然包含值 9,因此它被认为不可用于下一次尝试。
您只需在返回之前将其恢复为原始值 0:
现在我仍然感到困惑和问题的重点:我正在尝试像一棵树一样思考递归。一旦其中一个节点尝试了正方形的每个值并且其后代节点都返回False,它不只是将False 报告给它的父节点吗?为什么其他人会再次查看带有 2 的板?
如果您能帮助我理解递归,我将不胜感激!
编辑:@ThierryLathuille 在评论中再次回答了这个问题!非常感谢!
请注意,当递归调用您的函数时,您不会传递棋盘的副本,而只会在任何地方操作同一个棋盘。当您的代码运行时,每次您探索树的一个分支时,您在探索过程中接触到的所有方块都会留下一个非零值,因此它们不再被视为空闲。
我有一个错误的想法,即每当以递归方式调用函数时,它都会获取其所有变量的新副本以进行操作。它当然可以,但不是输入!对代码的另一个修复是使用 Python 的 copy 模块和 copy_board = copy.deepcopy(board) 行,并在函数的每个实例化处操作并返回 copy_board,这是我错误地认为 Python 总是在递归中所做的。
也许这与按值传递与按引用传递有关,而 Python 总是按引用传递?任何关于这个主题的更多讨论仍然很感激!
这是带有修复它的行注释掉的损坏代码:
def check_board(board: list):
for i in chars:
for j in chars:
for k in chars:
if k == j:
continue
if board[i][j] == board[i][k] and board[i][j] != 0:
return False
if board[j][i] == board[k][i] and board[j][i] != 0:
return False
return True
def solve_sudoku(board: list):
chars = range(2)
if not check_board(board):
return False
for i in chars:
for j in chars:
if board[i][j] == 0:
for a in chars:
board[i][j] = a+1
if solve_sudoku(board):
return solve_sudoku(board)
# uncommenting this next line fixes the algorithm
#board[i][j] = 0
return False
return board
board = [[0,0],[0,2]]
if __name__ == "__main__":
print(solve_sudoku(board))
输出:
False
【问题讨论】:
-
这是很多代码。请帮助我们通过删除无用的代码和 cmets 来帮助您,并更明确地描述您的问题——例如,哪个函数是递归的?如果你想知道递归,你能把这个例子减少到重现你的问题所需的最低限度吗?为什么您认为这是递归问题而不是众多检查之一?当您已经提供了一块您(希望)自己检查过的电路板时,我们是否真的需要
check_board及其 6 个嵌套的for循环? -
非常感谢您做出这些更改。这些可能有助于理解 Python 如何传递对象:Are python variables pointers、Changing an object pointed to by two variables、How are arguments passes in Python
标签: python recursion depth-first-search backtracking sudoku