具有暴力回溯错误的Python Sudoku递归

时间:2012-11-13 01:26:30

标签: python recursion sudoku backtracking

我正在为家庭作业做一个数独谜题解算器,但遇到了一些困难。现在的代码已经超越了解决方案,虽然它确实可以轻松解决问题,而对于更难的谜题,它会因为没有明显原因而陷入困境。我将不胜感激任何帮助。 (check_cell确定放置是否有效。)

  1. 在此代码中是否正确实现了回溯,如果没有,将如何修复?
  2. 如何阻止求解器冻结?它解决了大约3行然后冻结,将大多数值更改为9s。
  3. 一些代码:

    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
    

1 个答案:

答案 0 :(得分:4)

你遇到的麻烦是你的一些递归调用没有正确地返回结果,所以你的解决方案一旦被发现,就会被遗忘在递归堆栈的几个层次上。这是您需要的第一个修复,将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中的问题,(回溯后留下了9s)但实际上它只是装饰性的。
  4. 第一个问题很容易解决。只需将solve_helper来电更改为mover来电。这实际上就是你在问题中提出的原始代码中的内容。直接调用solve_helper实际上并没有得到错误的结果(因为solve_helper将第二次跳过已填写的单元格),但它会为递归的每个级别添加一个不必要的额外函数调用。 / p>

    第二个问题稍微复杂一点,这就是你被卡在某些电路板上的地方。你需要做的是将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分钟,但它最终确实有效。其他电路板(特别是那些在前几个开放空间中需要8s或9s的电路板)可能需要更长时间。