数独算法产生重复值

Sudoku algorithm generates duplicate values

我正在尝试实施回溯算法来解决数独问题,但出于某种原因,它会在同一个 row/column 中生成重复值,这是不允许的。

算法:

from math import floor
from typing import List

class Cell:
    x: int
    y: int
    times_visited: int = 0
    value: int
    is_prefilled: bool

    def __init__(self, x: int, y: int, value: int):
        self.x = x
        self.y = y
        self.value = value
        self.is_prefilled = value != None

def gen_possible_for(cell: Cell, sudoku: List[List[Cell]]) -> List[int]:
    # Horizontal
    horizontal = sudoku[cell.y]
    # Vertical
    vertical = [sudoku[y][cell.x] for y in range(9)]
    # 3x3 square
    base_x = floor(cell.x / 3) * 3
    base_y = floor(cell.y / 3) * 3
    square = [
        sudoku[base_y + rel_y][base_x + rel_x]
        for rel_y in range(3)
        for rel_x in range(3)
    ]

    #print("H:",horizontal)
    #print("V:",vertical)
    #print("S:",square)

    all = horizontal + vertical + square
    return [v for v in range(1, 9+1) if v not in all]

def solve_sudoku(sudoku: List[List[int]]):
    # Recursive backtracking algorithm
    
    sudoku_cells = [Cell(x, y, sudoku[y][x]) for x in range(9) for y in range(9)]

    i = 0
    while i < len(sudoku_cells):
        cell = sudoku_cells[i]

        if not cell.is_prefilled:
            possible_values = gen_possible_for(cell, sudoku)

            if not possible_values or cell.times_visited >= len(possible_values):
                if i == 0:
                    # Sudoku is unsolvable
                    return False
                
                print("decr")
                i -= 1
            else:
                cell.value = possible_values[cell.times_visited]
                i += 1
            
            cell.times_visited += 1
        else:
            i += 1
    
    # For some reason, (x + y * 9) causes the grid to rotate 90 degrees counter-clockwise
    return [[sudoku_cells[y + x * 9].value for x in range(9)] for y in range(9)]

使用以下数独作为输入:

# A sudoku from sudoku.com

sudoku = [
    [None, 6,    7,    None, 5,    4,    None, None, None],
    [1,    None, None, None, None, None, 2,    None, None],
    [None, None, None, 6,    1,    None, None, 4,    None],

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

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

输出为:

2 6 7 2 5 4 1 1 8
1 3 4 7 3 3 2 3 5
2 3 5 6 1 3 3 4 5
8 4 4 1 2 5 1 6 2
5 1 2 8 3 3 4 3 5
6 4 3 1 2 1 8 7 2
5 5 1 1 4 7 5 2 3
7 2 9 3 6 1 5 8 4
4 3 6 5 8 2 7 9 1

出于某种奇怪的原因,它会生成很多重复值,例如 (0,0) 处的 2 和 (0,2) 处的 2、(0,4) 处的 5 和 ( 0,6) 等等。另请注意,没有一条 decr 调试消息(来自第 55 行的打印语句)。

我做错了什么?
提前致谢!

主要问题是您只查看原始网格以限制值,而实际上您需要了解已放入单元格解决方案网格中的试验值。因此,请调整您的 gen_possible_for 以查看单元格,而不是原始拼图网格。

您还需要确保在回溯过去时清除单元格值,在这种情况下,并有办法回溯过去预填充的单元格。并且您需要保留每个单元格的可能值列表(直到您回溯过去);如果您 .pop() 来自此列表的值,那么您也不需要跟踪访问次数。