Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/359.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 试图理解递归/回溯,简单不雅观的数独示例_Python_Recursion_Depth First Search_Backtracking_Sudoku - Fatal编程技术网

Python 试图理解递归/回溯,简单不雅观的数独示例

Python 试图理解递归/回溯,简单不雅观的数独示例,python,recursion,depth-first-search,backtracking,sudoku,Python,Recursion,Depth First Search,Backtracking,Sudoku,[这篇文章收视率很低,所以我提出了一些修改建议,试图为子孙后代改进它。我希望它对将来找到它的任何人都有帮助!] 我一直试图通过一个简单的数独例子来理解递归/回溯/DFS。我对数独示例本身并不感兴趣,因此根据建议,我将下面的示例最小化为一个2x2的数独板,以便只关注让我对递归感到困惑的一点(谢谢@mistermiagi的建议) 在下面的代码中,我有一个helper函数check_board,它接受一个2x2矩阵,并检查其行和列中是否有任何数字重复(即检查输入的2x2数独是否有效)。然后,函数sol

[这篇文章收视率很低,所以我提出了一些修改建议,试图为子孙后代改进它。我希望它对将来找到它的任何人都有帮助!]

我一直试图通过一个简单的数独例子来理解递归/回溯/DFS。我对数独示例本身并不感兴趣,因此根据建议,我将下面的示例最小化为一个2x2的数独板,以便只关注让我对递归感到困惑的一点(谢谢@mistermiagi的建议)

在下面的代码中,我有一个helper函数
check_board
,它接受一个2x2矩阵,并检查其行和列中是否有任何数字重复(即检查输入的2x2数独是否有效)。然后,函数
solve_sudoku
就是我所理解的一种标准DFS/回溯算法,通过选择第一个空位置(用0表示)并尝试其中的值1和2,递归地寻找解决方案来解决数独问题

因此,输入
[[0,0],[0,2]]
时,输出应该是
[[2,1],[1,2]]
,但我收到的是输出
False

@Thierrylahuille注意到了代码中的问题:在尝试了每个可能的子节点(在本例中,通过尝试两个值1和2)后,我错过了“回溯”步骤,需要添加一行,将值重置为0,这意味着矩阵中的正方形在所有后续调用中都将卡在2中(或者,在最初的9x9示例中,卡在9):


当您看到在正方形中尝试的上一个值不能得到有效的解决方案时,您可以尝试下一个值,直到达到9。此时,您返回并返回到增加上一个正方形中的值,但当前正方形中仍然包含值9,因此认为下一次尝试不可用

您只需将其恢复为原始值0,然后再返回:

if board[3*a+c][3*b+d] == board[3*a+e][3*b+f] and board[3*a+c][3*b+d] != 0:
    board[i][j] = 0
    return False
现在我仍然感到困惑,问题的关键是:我试着像树一样思考递归。一旦其中一个节点尝试了一个正方形的每个值,并且它的后代节点都返回了
False
,它不就是向它的父节点报告
False
?为什么其他人会看到那只野猪你又跟2号打了吗

如果你能帮助我理解递归,我将非常感激

编辑:@thierrylahuille在评论中再次回答了这个问题!非常感谢


请注意,递归调用函数时,不会传递板的副本,而只会在任何地方操作同一块板。随着代码的运行,每次探索树的一个分支时,在探索过程中接触到的所有方块都会保留非零值,因此不再认为它们是自由的

我有一个错误的想法,即每当在递归中调用一个函数时,它都会得到其所有变量的一个新副本来执行操作。当然,它是这样做的,但不是输入!代码的另一个修复方法是使用Python的
copy
模块和
copy\u board=copy.deepcopy(board)
并在函数的每次实例化时操作并返回
copy\u 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

当您看到在一个方格中尝试的上一个值不能得到有效的解决方案时,您可以尝试下一个值,直到达到9。此时,您返回并返回到增加上一个方格中的值,但您的当前值仍然包含值9,因此它被视为不可用于下一次尝试。

您只需将其恢复为原始值0,然后再返回:

if board[3*a+c][3*b+d] == board[3*a+e][3*b+f] and board[3*a+c][3*b+d] != 0:
    board[i][j] = 0
    return False
输出:

[[4, 8, 6, 9, 1, 5, 7, 3, 2], 
 [7, 2, 5, 4, 6, 3, 1, 9, 8],
 [3, 9, 1, 7, 8, 2, 4, 5, 6], 
 [5, 6, 4, 1, 9, 7, 2, 8, 3],
 [9, 7, 3, 6, 2, 8, 5, 1, 4],
 [8, 1, 2, 5, 3, 4, 6, 7, 9],
 [2, 5, 7, 8, 4, 9, 3, 6, 1], 
 [1, 3, 8, 2, 5, 6, 9, 4, 7],
 [6, 4, 9, 3, 7, 1, 8, 2, 5]]
这是正确的答案


但是,到达它需要很长的时间(大约30秒左右…),因为代码的很多部分,特别是检查行/列/块中是否存在重复项的效率非常低。请查看有效方法的想法。

这是很多代码。请帮助我们删除无用的代码和注释,并更明确地描述您的问题,例如,哪个函数是重新定义的草书?如果你想知道递归,你能把这个例子减少到重现你的问题所需的最小值吗?为什么你认为这是一个递归问题,而不是许多检查中的一个?当你已经提供了一个你(希望如此)想要的板时,我们真的需要
check_board
,以及它的6嵌套
for
循环吗检查过你自己吗?@MarcelBesixdouze请不要编辑你的帖子说“已解决”。通过这样做,并删除最初回答的问题的关键部分,该线程将失去未来访问者的上下文,他们将无法理解您最初遇到的问题。请参阅这些元线程:非常感谢您所做的这些更改。这些更改可能有助于理解Python如何传递对象:,非常感谢!这真的很好,但我还是有点不知道发生了什么。我试着像树一样思考递归。一旦其中一个节点尝试了一个正方形的每个值,并且它的后代节点都返回了False,它不就是向它的父节点报告False吗?为什么其他人会带着错误的眼光看那块板再次使用9?请注意,当递归调用函数时,不会传递板的副本,而只会在任何地方操作同一块板。随着代码的运行,每次探索树的一个分支时,在探索过程中接触到的所有方块都会留下一个非零值,因此它们不是零