回溯数独仅适用于 Python 3

Posted

技术标签:

【中文标题】回溯数独仅适用于 Python 3【英文标题】:Backtracking Sudoku only sort of works in Python 3 【发布时间】:2020-02-17 17:11:26 【问题描述】:

我正在努力完成这项工作。它得到了几个数字,但我不知道问题出在哪里。我已经阅读了各种其他代码,虽然我了解它们是如何工作的,但我不知道我的代码的问题出在哪里。我尝试了多种方法,这是我最接近的方法。

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

    [0, 0, 5,   0, 3, 0,  1, 0, 0],
    [0, 7, 0,   5, 0, 1,  0, 3, 0],
    [0, 0, 3,   0, 9, 0,  8, 0, 0],

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

def p(board):
    for i in board:
        print(i)

def find(board):
    for row in range(9):
        for col in range(9):
            if board[row][col] == 0:
                return row, col
    return 1

def check_horizontal(board, row, number):
    for i in range(0, 9):
        if board[row][i] == number:
            return False
    return True

def check_vertical(board, col, number):
    for i in range(0, 9):
        if board[i][col] == number:
            return False
    return True

def check_sqaure(board, row, col, number):
    a = int(row / 3)
    b = int(col / 3)
    for i in range(a * 3, a * 3 + 3):
        for j in range(b * 3, b * 3 + 3):
            if board[i][j] == number:
                return False
    return True

def check_all(board, row, col, number):
    if check_horizontal(board, row, number) and check_vertical(board, col, number) and check_sqaure(board, row, col,number):
        return True

def play(board):
    if find(board) == 1:
        return 1
    row, col = find(board)
    for i in range(1, 10):
        if check_all(board, row, col, i):
            board[row][col] = i
            if play(board) == 1:
                return board
            else:
                if check_all(board, row, col,board[row][col] + 1):
                    board[row][col] = board[row][col] + 1   
play(board)
p(board)

这是输出:

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

正确的结果是:

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

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

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

【问题讨论】:

正确的输出是什么,您对它们为什么不同的看法是什么? 我认为它只适用于第一个数字,然后它只是插入适合的值,当它找不到单元格的数字时它就会停止。有关正确的输出,请查看我的编辑。 第一行的8在哪里? 这是最后一个数字,抱歉打错了 board[raw][col] = board[raw][col] + 1 看起来很奇怪。如果i 不被接受,则强制i + 1 【参考方案1】:

我发现您的 play 函数存在一些问题。

第一个问题是您没有正确回溯。回溯意味着当您放弃当前的部分解决方案时,您需要将您一直在测试的板上的空间标记为未知。如果递归没有找到解决方案,您当前有一些奇怪的代码会尝试向前跳转。我认为你应该删除这些行:

else:
    if check_all(board, row, col,board[row][col] + 1):
        board[row][col] = board[row][col] + 1  

相反,如果循环找不到任何解决方案,您需要重置。所以把它放在代码的末尾,在循环之外:

board[row][col] = 0

另一个问题是您对来自play 的返回值有一些混淆的逻辑。在基本情况下,您将返回标记值1,您稍后的递归代码会对此进行检查。但是如果后面的代码认为它从递归中得到了解决方案,它将返回board 而不是哨兵值。您需要选择其中之一并与之保持一致!另一方面,您依赖默认返回值None 来指示在您从函数末尾跌落时失败。您可能应该通过添加 return None 来明确该返回值。

这是我的做法,归还板子:

def play(board):
    if find(board) == 1:
        return board            # first change, return the board here!
    row, col = find(board)
    for i in range(1, 10):
        if check_all(board, row, col, i):
            board[row][col] = i
            if play(board) is not None: # second change, check for a non-None value, not == 1
                return board
    board[row][col] = 0         # third change, do proper backtracking (replaced three lines)
    return None                 # fourth change, be explicit about our return value on failure

在你调用find 两次的地方仍然有一个小缺陷,但这不会使代码出错,只是比必要的慢一点。您可以通过更改前几行来改进它:

coords = find(board)
if coords == 1:
    return board
row, col = coords

我也更愿意使用None 作为失败find 的哨兵,但这更多的是品味问题,而不是真正错误或低效的任何事情。

【讨论】:

以上是关于回溯数独仅适用于 Python 3的主要内容,如果未能解决你的问题,请参考以下文章

Python数独回溯

python中数独的回溯算法

求解数独的回溯算法

Javascript递归回溯数独求解器

具有回溯的数独求解器不能总是检测到多个解决方案

数独回溯 无效数独