数独算法产生重复值
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()
来自此列表的值,那么您也不需要跟踪访问次数。
我正在尝试实施回溯算法来解决数独问题,但出于某种原因,它会在同一个 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()
来自此列表的值,那么您也不需要跟踪访问次数。