【问题标题】:Python Sudoku Recursion with Brute Force Backtracking ErrorPython数独递归与蛮力回溯错误
【发布时间】:2012-11-13 01:26:29
【问题描述】:

我正在为家庭作业做数独解谜游戏,但遇到了一些困难。代码现在循环通过解决方案,尽管它确实可以解决简单的难题,而对于更难的难题,它会无缘无故地卡在几个 9 上。我将不胜感激这方面的任何帮助。 (check_cell 确定展示位置是否有效。)

  1. 在这段代码中是否正确实现了回溯,如果没有,如何解决?
  2. 如何阻止求解器冻结?它解决了大约 3 行,然后冻结,将大部分值更改为 9。

一些代码:

def solve_helper(self, row, col):
# Try placing a number in each column of current row
    board = self.the_board
    if board[row][col] != 0:
        ?????
    elif board[row][col] == 0:
        for i in range(1,10):
            print("Setting value at i with ") + str (i) + (" located at " ) + str(row) + str(col)
            self.set_cell(row, col, i)
            self.guesses = self.guesses + 1
            if self.check_cell(row, col):
                if self.solve_helper(row, col): return True
            else:
                self.set_cell(row, col, 0)
    else:
        return self.mover(row,col)
    return False

def mover(self, row, col):
    if col + 1 != 9:
        return self.solve_helper(row, (col+1))
    elif row + 1 != 9:
        print "Moving to row" + str(row + 1)
        return self.solve_helper((row+1),0)
    else:
        print "SOLUTION FOUND"
        return True

【问题讨论】:

    标签: python recursion sudoku backtracking


    【解决方案1】:

    您遇到的麻烦是您的一些递归调用没有正确返回结果,因此您的解决方案在找到时会被遗忘在递归堆栈的几个级别。这是您需要的第一个修复,将return 添加到mover 中进行的递归调用:

    def mover(self, row, col):
        if col + 1 != 9:
            return self.solve_helper(row, (col+1))  # added return
        elif row + 1 != 9:
            print "Moving to row" + str(row + 1)
            return self.solve_helper((row+1),0)     # here too
        else:
            print "SOLUTION FOUND"
            return True
    

    在您的 solve_helper 函数的特殊情况下,您还需要类似的东西,您可以跳过预先解决的单元格。函数的结尾应该是:

    else:
        return self.mover(row, col)  # added return
    return False
    

    编辑:

    好的,我在代码中发现了更多问题。其中两个是求解器的逻辑问题,一个是显示问题,除了在求解过程中看起来很奇怪之外不会引起任何实际问题。

    问题:

    1. 首先,您的最新代码有solve_helper 调用自身,而不是调用mover。这使得它在移动之前需要一个额外的函数调用(尽管我认为它实际上可能不会破坏求解器)。
    2. 其次,如果solve_helper 将一个单元格设置为 9,但随后回溯到(在某些后来的单元格无法解决之后),则 9 在进一步回溯之前不会重置为零。
    3. 最后是显示问题。将单元格设置为 0 不会停止显示其旧值。这看起来很像 #2 中的问题(回溯后留下了 9),但实际上它只是装饰性的。

    第一个问题很容易解决。只需将 solve_helper 调用更改为 mover 调用即可。这实际上就是您在问题中输入的原始代码中的内容。直接调用solve_helper 实际上并不会得到错误的结果(因为solve_helper 将第二次跳过已经填写的单元格),但它会为您的每一级递归添加一个不必要的额外函数调用。

    第二个问题稍微复杂一些,这就是你在某些板上卡住的地方。您需要做的是将执行 self.set_cell(row, col, 0) 的行移出它当前所在的 else 块。事实上,如果您愿意,您实际上可以将它完全移出循环(因为它只是真的有必要如果您在当前单元格的任何值都不起作用后回溯)。以下是我认为这是 for 循环的最佳安排(同时将 return False 语句向上移动):

    for i in range(1,10):
        print("Setting value ") + str (i) + (" at " ) + str(row) + ", " + str(col)
        self.set_cell(row, col, i)
        self.guesses = self.guesses + 1
        if self.check_cell(row, col):
            if self.mover(row, col):
                return True
    print "Backtracking"
    self.set_cell(row, col, 0)
    return False
    

    最后,修复显示问题需要进行两项更改。首先,去掉set_cell 中的条件。您希望始终更新显示。接下来,在update_textfield 中,将delete 调用移到if 块之外,以便它始终发生(将insert 留在if 下)。这使得将单元格设置为零将删除以前的值,但不会使其显示实际的 0 字符(它不会显示任何内容)。

    我认为应该这样做。请注意,您使用的算法仍然很慢。解决a board I found on the internet in a quick Google search 花了 122482 次猜测和超过 5 分钟,但它终于奏效了。其他板(尤其是前几个空地需要 8 或 9 的板)可能需要更长的时间。

    【讨论】:

    • 作为solve_helper的一部分,else语句是否已经跳过非零值?
    • @CluelessCoder:问题是,在您通过转到else 块跳过非零值之后,即使找到了解决方案,您也总是返回 False。任何时候你递归,你需要准备好返回True,如果该调用导致找到解决方案。当您放弃当前的棋盘状态并需要回溯时,您只想返回 False。
    • 我仍然不确定 False 是如何传入的,我不确定如何正确引导非零。代码似乎在回溯,但通过了正确的值,导致三行中的空字符都变成了 9。
    • @CluelessCoder 我认为您对if 语句和solve_helper 中的递归的更改是错误的。您所需要的只是将return 添加到您已经在执行函数调用的行中,包括moversolve_helper 末尾附近的else 案例。我觉得应该够了。
    • 放入您推荐的更改后,该程序仍然无法运行。它将挂在第二行,朝向最后一列,然后回溯以将所有未给定的值设置为 9。我附上了完整的代码,看看是否还有其他问题。 pastebin.com/iNccTXgN