提问人:Armenta 提问时间:9/7/2023 最后编辑:ShadowRangerArmenta 更新时间:9/7/2023 访问量:69
如何停止在我的数独网格上获得重复项?
How to stop getting duplicates on my sudoku grid?
问:
我已经对所有内容进行了编码,以至于我的程序试图自行解决数独问题,但是当我运行它时,我在网格上得到了重复项。我知道问题出在代码检查每个 3x3 网格的部分,但我找不到问题所在。
这是我的全部代码:
grid2 = [[5,3,0,0,7,0,0,0,0], [6,0,0,1,9,5,0,0,0], [0,9,8,0,0,0,0,6,0], [8,0,0,0,6,0,0,0,3], [4,0,0,8,0,3,0,0,1], [7,0,0,0,2,0,0,0,6], [0,6,0,0,0,0,2,8,0], [0,0,0,4,1,9,0,0,5], [0,0,0,0,8,0,0,7,9]]
def valid(grid,x,y,n):
for i in range (9):
if(grid[x][i] == n):
return False
return True
for i in range(9):
if(grid[y][i] == n):
return False
return True
x //= 3
y //= 3
for i in range (3):
for j in range(3):
if(grid[x+i][y+j] == n):
return False
return True
def sudoku(grid):
for x in range(9):
for y in range(9):
if(grid[x][y] == 0):
for n in range(1,10):
if(valid(grid,x,y,n)):
grid[x][y] = n
result = sudoku(grid)
if result is not None:
return result
grid[x][y] = 0
return
return grid
我试图更改 x 和 y 的值,看看它是否检查每个网格中的 [x,y],但找不到问题。
答:
函数在第一个循环后无条件返回(假设它不在该循环中)。函数的其余部分永远不会运行,因此只要第一个循环通过(确认数字不在行中),您就认为它有效,而无需选中列或 3x3 子框。删除该函数中除最后一个之外的所有功能。valid
True
return False
return True
这仍然会给您留下其他问题(例如,您的第一个和第二个循环正在检查完全相同的东西,我怀疑您的意思是检查一个循环中的行和另一个循环中的列),但您会更接近。
关于简化代码的旁注:
一行是单个子行;你可以用该子中的简单成员资格测试完全替换第一个循环(同样的big-O,但在实践中,让Python在单个操作码中为你完成工作要快得多):list
list
if n in grid[x]:
return False
第二个循环必须更复杂(因为您需要检查多行中每一行上的单个单元格),但您仍然可以通过直接迭代来避免如此多的索引,因此在修复后,它是:
for row in grid: # Direct iteration, faster than index loop, gives a useful name,
if row[y] == n: # and simpler usage
return False
评论
要解决此问题,请在“value”函数中调整 x 和 y 的计算:
grid2 = [[5,3,0,0,7,0,0,0,0], [6,0,0,1,9,5,0,0,0], [0,9,8,0,0,0,0,6,0], [8,0,0,0,6,0,0,0,3], [4,0,0,8,0,3,0,0,1], [7,0,0,0,2,0,0,0,6], [0,6,0,0,0,0,2,8,0], [0,0,0,4,1,9,0,0,5], [0,0,0,0,8,0,0,7,9]]
def valid(grid, x, y, n):
for i in range(9):
if grid[x][i] == n:
return False
for i in range(9):
if grid[i][y] == n:
return False
# Calculate the starting row and column for the 3x3 subgrid
x_start = (x // 3) * 3
y_start = (y // 3) * 3
for i in range(3):
for j in range(3):
if grid[x_start + i][y_start + j] == n:
return False
return True
def sudoku(grid):
for x in range(9):
for y in range(9):
if(grid[x][y] == 0):
for n in range(1,10):
if(valid(grid,x,y,n)):
grid[x][y] = n
result = sudoku(grid)
if result is not None:
return result
grid[x][y] = 0
return
return grid
sudoku(grid2)
我建议使用集合的更简洁的方法。数独棋盘中有 27 组数字,其中需要有互斥(唯一)的数字。每个位置都是其中 3 个组(行、列和块)的一部分。
因此,您可以首先创建一个函数,该函数将生成一组元组,表示给定数字在特定位置的 3 个条目(组、数字):
def sudoSet(r,c,n,base=3):
side = base*base
return { (r//base*base+c//base,n), (r+side,n), (c+2*side,n) }
然后,您可以使用此函数获取整个棋盘的已用排除项集(忽略包含零的位置):
def sudoSets(board,base=3):
return set.union(*(sudoSet(r,c,n,base)
for r,row in enumerate(board)
for c,n in enumerate(row) if n ))
使用这两个函数,检查是否可以将数字放置在给定位置成为一个简单的集合操作:
grid2 = [[5,3,0,0,7,0,0,0,0],
[6,0,0,1,9,5,0,0,0],
[0,9,8,0,0,0,0,6,0],
[8,0,0,0,6,0,0,0,3],
[4,0,0,8,0,3,0,0,1],
[7,0,0,0,2,0,0,0,6],
[0,6,0,0,0,0,2,8,0],
[0,0,0,4,1,9,0,0,5],
[0,0,0,0,8,0,0,7,9]]
used = sudoSets(grid2)
print(sudoSet(7,2,3).isdisjoint(used) ) # True, a 3 can be placed at 7,2
print(sudoSet(7,2,6).isdisjoint(used) ) # False, a 6 cannot be placed at 7,2
通过这种方法,您可以轻松地跟踪电路板上数字的放置和移除,以便进行扩展和回溯:
# place 3 at 7,2:
grid2[7][2] = 3
used |= sudoSet(7,2,3)
# remove 3 placed at 7,2:
used -= sudoSet(7,2,grid2[7][2])
grid2[7][2] = 0
您还可以使用它来验证整个网格是否有效(所用集的大小应恰好是网格中非零数字数量的 3 倍):
isValid = len(used) == 3*sum(len(r)-r.count(0) for r in grid2)
使用这些函数和基于集合的方法,只需几行即可编写出蛮力求解器:
def sudoSolve(board):
solution = [row.copy() for row in board] # prepare solution
used = sudoSets(board) # track preset numbers
empties = [(r,c) for r,row in enumerate(board)
for c,n in enumerate(row) if not n ] # pos to fill
e,inc = 0,0
while 0 <= e+inc < len(empties):
e += inc
r,c = empties[e] # position to assign
if inc < 0: # unexclude previous attempt
used -= sudoSet(r,c,solution[r][c])
solution[r][c] += 1 # try next value
if solution[r][c] > 9: # All tried
solution[r][c] = 0 # clear number for later
inc = -1 # backtrack position
continue
posSet = sudoSet(r,c,solution[r][c]) # position/number groups
if posSet.isdisjoint(used): # no conflict
used |= posSet # track exclusions
inc = 1 # next empty position
else:
inc = 0 # try other at position
return solution if inc >= 0 else []
输出:
print(*sudoSolve(grid2),sep="\n")
[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]
我找到了答案,问题在于我的 for 循环来检查 3x3,因为我没有将 x 和 y 乘以 3。添加该修复了无法完全检查 3x3 的问题。我的代码的另一个问题是“if result is not None”语句。我删除了它,它将完全遍历数独,并且在调用 valid 的 if 语句中,我添加了另一个语句来检查数独是否没有任何 0。 以下是有效的代码:
def valid(grid,x,y,n):
#Checks row
if n in grid[x]:
return False
#Checks columns
for row in grid:
if(row[y] == n):
return False
x //= 3
y //= 3
#Checks each square
for i in range (3):
for j in range(3):
if(grid[x*3+i][y*3+j] == n):
return False
return True
def sudoku(grid):
for x in range(9):
for y in range(9):
if(grid[x][y] == 0):
for n in range(1,10):
if(valid(grid,x,y,n)):
grid[x][y] = n
result = sudoku(grid)
if np.count_nonzero(result) == 81:
return result
return result
grid[x][y] = 0
return grid
return grid
评论