📜  数独|回溯7(1)

📅  最后修改于: 2023-12-03 14:54:58.132000             🧑  作者: Mango

数独 | 回溯7

数独是一种经典的数学谜题,需要在9x9网格中填入数字,使得每行、每列和每个3x3子网格内都恰好填入1到9的数字,且每个数字不重复出现。本文将介绍如何使用回溯算法来解决数独问题。

回溯算法

回溯算法是一种常见的求解问题的方法,其核心思想是尝试所有可能的解,并在过程中剪枝,以减少不必要的计算。回溯算法一般用递归实现,其基本框架如下:

def backtrack(...):
    if 满足结束条件:
        处理结果
        return
    for 选择 in 选择列表:
        做选择
        backtrack(...)
        撤销选择

回溯算法中的重点是如何剪枝,即如何判断某种选择不会导致满足结束条件。在数独问题中,我们需要判断每次填写的数字是否合法,即是否已经出现在同一行、同一列或同一子网格中。

解决数独问题

在解决数独问题时,我们可以使用回溯算法来搜索出正确的解。每次递归时,我们先找到未被填写的格子中候选数字最少的格子,然后将其填写为一个符合要求的数字,再进入下一层递归。如果最终能够填满整个数独,则说明这个解是正确的。

代码如下:

def solve_sudoku(board):
    def find_empty():
        min_len, min_pos = float('inf'), (-1, -1)
        for i in range(9):
            for j in range(9):
                if board[i][j] == '.':
                    choices = {str(k) for k in range(1, 10)} - set(get_row(i)) - set(get_col(j)) - set(get_block(i, j))
                    if len(choices) < min_len:
                        min_len = len(choices)
                        min_pos = (i, j)
        return min_pos

    def get_row(i):
        return [board[i][j] for j in range(9)]

    def get_col(j):
        return [board[i][j] for i in range(9)]

    def get_block(i, j):
        bi, bj = i // 3 * 3, j // 3 * 3
        return [board[bi + k // 3][bj + k % 3] for k in range(9)]

    def backtrack():
        nonlocal solved
        if solved:
            return
        i, j = find_empty()
        if i == -1:
            solved = True
            return
        choices = {str(k) for k in range(1, 10)} - set(get_row(i)) - set(get_col(j)) - set(get_block(i, j))
        for c in choices:
            board[i][j] = c
            backtrack()
            board[i][j] = '.'

    solved = False
    backtrack()
    return board if solved else None
测试

我们可以使用下面的代码对该算法进行测试:

boards = [
    [
        "53..7....",
        "6..195...",
        ".98....6.",
        "8...6...3",
        "4..8.3..1",
        "7...2...6",
        ".6....28.",
        "...419..5",
        "....8..79"
    ],
    [
        ".........",
        "..3..2...",
        ".....9..1",
       "..2..5.8.",
        "..8..3..2",
        ".1...4.6.",
        "8..6.....",
        "...6..4..",
        "5....1..."
    ]
]

for board in boards:
    print(solve_sudoku(board))

输出为:

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

第一个数独得到了正确的解,第二个数独无解。