有人可以帮助解释这个回溯算法中的递归吗?

Posted

技术标签:

【中文标题】有人可以帮助解释这个回溯算法中的递归吗?【英文标题】:Can someone help explain the recursion in this back tracking algorithm? 【发布时间】:2020-02-16 22:46:25 【问题描述】:

这是一个使用python和回溯算法解决数独板的程序,但我似乎不理解solve(bo)中的递归。好像如果条件不满足,则索引重置为 0 并继续在同一位置尝试数字。

也许有人可以用更简单的术语来解释一下,函数如何回溯和重新检查条件?

board = [
    [7,8,0,4,0,0,1,2,0],
    [6,0,0,0,7,5,0,0,9],
    [0,0,0,6,0,1,0,7,8],
    [0,0,7,0,4,0,2,6,0],
    [0,0,1,0,5,0,9,3,0],
    [9,0,4,0,6,0,0,0,5],
    [0,7,0,3,0,0,0,1,2],
    [1,2,0,0,0,7,4,0,0],
    [0,4,9,2,0,6,0,0,7]
]


def solve(bo):
    find = find_empty(bo)
    if not find:
        return True
    else:
        row, col = find

    for i in range(1,10):
        if valid(bo, i, (row, col)):
            bo[row][col] = i

            if solve(bo):
                return True

            bo[row][col] = 0

    return False


def valid(bo, num, pos):
    # Check row
    for i in range(len(bo[0])):
        if bo[pos[0]][i] == num and pos[1] != i:
            return False

    # Check column
    for i in range(len(bo)):
        if bo[i][pos[1]] == num and pos[0] != i:
            return False

    # Check box
    box_x = pos[1] // 3
    box_y = pos[0] // 3

    for i in range(box_y*3, box_y*3 + 3):
        for j in range(box_x * 3, box_x*3 + 3):
            if bo[i][j] == num and (i,j) != pos:
                return False

    return True


def print_board(bo):
    for i in range(len(bo)):
        if i % 3 == 0 and i != 0:
            print("- - - - - - - - - - - - - ")

        for j in range(len(bo[0])):
            if j % 3 == 0 and j != 0:
                print(" | ", end="")

            if j == 8:
                print(bo[i][j])
            else:
                print(str(bo[i][j]) + " ", end="")


def find_empty(bo):
    for i in range(len(bo)):
        for j in range(len(bo[0])):
            if bo[i][j] == 0:
                return (i, j)  # row, col

    return None

print_board(board)
solve(board)
print("___________________")
print_board(board)

【问题讨论】:

【参考方案1】:

对!所以这个解决方案的运作方式是它在板上找到下一个空字段,尝试从1..9 范围内插入数字,然后检查有效性。有效性检查是对给定数字是否正确的试探性答案,即它是否不违反规则。如果是这样,则将数字插入数组并递归调用solve。现在,这里发生了隐式回溯。如果这个对solve 的递归调用不能为这个谜题产生一个完全一致的解决方案,那么从我们的初步猜测开始的整个“分支”将从调用堆栈中删除,失败的猜测在板上重置(删除),并且我们继续下一个猜测。

【讨论】:

所以基本上整个解决方案分支都是这个“初步猜测”的一部分? 另外,我仍然不明白如何继续使用其他可能的解决方案。重置为 0 似乎整个 solve() 方法是从新开始使用的。此外,程序如何知道解决方案是否已经过测试。 首先,归零并不意味着重新开始。请记住,我们仍处于对solve 的某个递归调用的上下文中,因此我们从错误的猜测中回溯并继续下一个猜测。至于跟踪进度,程序并不“知道”测试猜测的历史,而是组织递归调用的层次结构,以便它们跨越整个组合空间。程序正在穿越那个空间,让我们逐渐接近正确的答案。 是否可以类比此算法:在国际象棋中,一个玩家先思考几步,然后如果该序列最终失败,他/她将恢复到当前状态并继续其他可能的移动 嗯...这是一个不精确的类比,因为在数独游戏中,一旦你到达它就很容易发现不一致,而在国际象棋中,你可能已经准备好面对一些明显的局部劣势,以便执行你的长期目标。但在某种意义上是相似的。

以上是关于有人可以帮助解释这个回溯算法中的递归吗?的主要内容,如果未能解决你的问题,请参考以下文章

算法入门(回溯算法)

Java数据结构与算法——递归与回溯

解决分区问题的递归回溯算法

Swift递归回溯算法不起作用

优化回溯算法求解数独

N皇后问题(递归回溯)