Python 中递归数独求解器的重复值

问题描述 投票:0回答:1

我正在尝试使用递归来制作数独求解器,方法是将可能的值存储在字典中,以索引为键,将可能值的集合作为值。参数 b 只是表示为单个列表的板,并且 .get_row、.get_col、.get_box 方法正确返回各自的值。如果字典中的条目有一个包含单个元素的集合,则它将被放入板 b 中,否则,如果没有可能的包含单个元素的集合,则从可能的数字集中选取一个值并开始递归。这是完整的可重复代码:

class Sudoku:
    def solve_it(self, board: List[List[str]]) -> None:
        """
        :param board: sudoku board gets modified inplace at .create_board_from_list
        :return: None
        """
        unit_values = []
        for i in range(len(board)):  # flattening the board into unit_values
            for j in range(len(board[i])):
                unit_values.append(board[i][j])
        self.go_through_recursive(board, unit_values)

    def get_row(self, n: int, b):  # retrieve all elements of nth row in the board
        assert 0 <= n <= 8
        return set(b[9 * n: 9 * (n + 1)])

    def get_col(self, n: int, b):  # retrieve all elements of nth col in the board
        assert 0 <= n <= 8
        return set(b[n::9])

    def get_box(self, n: int, b):  # retrieve all elements of nth box in the board
        assert 0 <= n <= 8
        return set(b[i] for i in range(81) if (i // 27) == (n // 3) and i % 9 // 3 == n % 3)

    def go_through_recursive(self, board, b, d=False):
        """
        :param board: sudoku board as matrix where each row is a line
        :param b: sudoku board as a single long list
        """
        numbers = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}
        while True:

            if d:
                return True

            missing_dict = {}  # populate missing_dict
            for idx in range(len(b)):
                if b[idx] == '.':
                    row, col, box = idx // 9, idx % 9, (idx // 27) * 3 + (idx % 9) // 3
                    # union of all the present values in current row, col and box
                    values = self.get_row(row, b).union(self.get_col(col, b)).union(self.get_box(box, b))
                    values.remove('.')
                    missing_ones = numbers.difference(values)  # possible values to input in the slot
                    if len(missing_ones) == 0:  # impossible to continue
                        return False
                    missing_dict[idx] = missing_ones

            old_count = b.count('.')
            # now we iterate through the dictionary of possible values per index,
            # if one index has a set with a single number it means that that's the only possible number so we store it
            for idx, missings in missing_dict.items():  # store assured values
                if len(missings) == 1:
                    b[idx] = missings.pop()

            if b.count('.') == 0:  # check if complete
                self.create_board_from_list(board, b)
                return True

            if b.count('.') == old_count:  # if no progress has been made
                for idx, s in missing_dict.items():  # iterate through the dictionary
                    for number in s:  # create a new board and store indecisive value then recur
                        if d:
                            return True
                        bb = b[:]
                        bb[idx] = number
                        d = self.go_through_recursive(board, bb)

    def create_board_from_list(self, board, b):
        temp_board = []
        chunk = 9
        for idx in range(0, len(b), chunk):
            temp_board.append(b[idx: idx + chunk])
        for idx in range(len(board)):
            board[idx] = temp_board[idx]
        print('done')

我遇到的问题是,一旦完成,一些数字就会重复(在行、列或框中)。我还注意到,如果我检查“存储保证值”循环内的可能值,它有时会返回整个范围 [1, 9] 可能是某些重写的原因:

row, col, box = idx // 9, idx % 9, (idx // 27) * 3 + (idx % 9) // 3
values = self.get_row(row, b).union(self.get_col(col, b)).union(self.get_box(box, b))
values.remove('.')  # some of these values are [1, 9]

鉴于此板的输入:

board = 
[[".",".","9","7","4","8",".",".","."],
 ["7",".",".",".",".",".",".",".","."],
 [".","2",".","1",".","9",".",".","."],
 [".",".","7",".",".",".","2","4","."],
 [".","6","4",".","1",".","5","9","."],
 [".","9","8",".",".",".","3",".","."],
 [".",".",".","8",".","3",".","2","."],
 [".",".",".",".",".",".",".",".","6"],
 [".",".",".","2","7","5","9",".","."]]

正确的输出是

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

虽然代码有错误的板

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

编辑:

我更改了

go_through_recursive
以将唯一的可能值直接存储到板上,在这种情况下,字典中唯一可能的设置长度是 2 或更大。但这似乎陷入了无限循环

    def go_through_recursive(self, board, b, d=False):
        """
        :param board: sudoku board as matrix where each row is a line
        :param b: sudoku board as a single long list
        """
        numbers = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}
        while True:

            old_count = b.count('.')
            missing_dict = {}  # populate missing_dict
            for idx in range(len(b)):
                if b[idx] == '.':
                    row, col, box = idx // 9, idx % 9, (idx // 27) * 3 + (idx % 9) // 3
                    # union of all the present values in current row, col and box
                    values = self.get_row(row, b).union(self.get_col(col, b)).union(self.get_box(box, b))
                    values.remove('.')
                    missing_ones = numbers.difference(values)  # possible values to input in the slot
                    if len(missing_ones) == 0:  # impossible to continue
                        return False
                    elif len(missing_ones) == 1:
                        b[idx] = missing_ones.pop()
                    else:
                        missing_dict[idx] = missing_ones

            if b.count('.') == 0:  # check if complete
                self.create_board_from_list(board, b)
                return True

            if b.count('.') == old_count:  # if no progress has been made
                for idx, s in missing_dict.items():  # iterate through the dictionary
                    for number in s:  # create a new board and store indecisive value then recur
                        bb = b[:]
                        bb[idx] = number
                        if self.go_through_recursive(board, bb):
                            return True
python algorithm sudoku
1个回答
0
投票

这里有两个问题:

  • 当你执行

    b[idx] = missings.pop()
    时,你应该退出循环,因为现在
    missings
    不再是最新的:通过在网格上放置一个数字,这个数字不再允许出现在其他一些空单元格上,并且这个字典中没有正确反映。这个错误是导致在同一行上两次获得相同数字的原因。简单的解决方法是在分配后立即退出循环,以便可以在外循环的下一次迭代中重建字典

  • 在给出的示例中,棋盘最初没有强制条目(使用字典检查后空单元格的数量保持不变),因此对左上角单元格进行了随机选择。在示例输出中,结果表明选择是 3,但这使得这个特定的数独无法解决。但你的算法没有实现回溯。当它后来发现没有解决方案时,它就返回

    False
    。但这还为时过早。现在应该考虑到一些随机选择是不正确的,应该做出不同的选择。

© www.soinside.com 2019 - 2024. All rights reserved.