[英]sudoku solver with constraints and backtracking in python
我意識到這個問題已在這里討論了很多,我已經閱讀了所有內容。 但是,我的程序不起作用。 好吧,它解決了簡單和中等難度的網格,但是當涉及到一些困難的難題時,它似乎進入了一個無限循環。
我再次閱讀了很多關於這個主題的文章,但我仍然無法理解為什么我的程序不起作用。 如果你能向我解釋,我將非常感激。
我從一些輔助函數開始,它們起作用,所以它們不是很重要,但我會發布它們 - 也許你會給它們任何反饋
所以,我有一個帶整數的列表列表:
[[5, 0, 0, 7, 1, 9, 0, 0, 4],
[0, 0, 1, 0, 3, 0, 5, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 8, 5, 9, 7, 2, 6, 4, 0],
[0, 0, 0, 6, 0, 1, 0, 0, 0],
[0, 2, 6, 3, 8, 5, 9, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 3, 0, 5, 0, 2, 0, 0],
[8, 0, 0, 4, 9, 7, 0, 0, 6]]
首先,我定義了一些輔助函數
from copy import deepcopy
def nice_print(grid): #just printing tool
for line in grid:
print(line)
def box(row,col,grid): #returns a list of numbers that are in the same box
row = (row // 3)*3 #with grid[row][col]
col = (col // 3)*3
return grid[line][row:row+3:]+grid[line+1][row:row+3:]+grid[line+2][row:row+3:]
現在我需要檢查是否有任何數字可以輕松放入網格中
def constraints(grid):
ngrid = deepcopy(grid)
#in every cell with '0' i put a set{1..9}
for i in range(9):
for j in range(9):
if grid[i][j] == 0:
ngrid[i][j] = set(range(1,10))
#checking all conditions
for k in range(81):
for i in range(9):
for j in range(9):
if type(ngrid[i][j]) == set:
#square
if not ngrid[i][j].isdisjoint(set(box(i,j,grid))):
ngrid[i][j].difference_update(set(box(i,j,grid)))
#line
if not ngrid[i][j].isdisjoint(set(grid[i])):
ngrid[i][j].difference_update(set(grid[i]))
#row
if not ngrid[i][j].isdisjoint(set(list(zip(*grid))[j])):
ngrid[i][j].difference_update(set(list(zip(*grid))[j]))
#if there is the last remaining number i put it in the
#first grid and change the type of ngrid's cell to int
if len(ngrid[i][j]) == 1:
grid[i][j] = list(ngrid[i][j])[0]
ngrid[i][j] = list(ngrid[i][j])[0]
#i parse from set&int to string
for i in range(9):
for j in range(9):
if type(ngrid[i][j])==set:
grid[i][j]=''
for item in ngrid[i][j]:
grid[i][j]+=str(item)
else:
grid[i][j]=str(grid[i][j])
return grid
然后我定義它是什么 - 待解決......
def solved(grid):
ans = True
for num in range(1,10):
num=str(num)
#line
for line in grid:
if line.count(num) != 1:
ans = False
break
#row
for row in list(zip(*grid)):
if row.count(num) != 1:
ans = False
break
#square
for i in [0,3,6]:
for j in [0,3,6]:
if box(i,j,grid).count(num) != 1:
ans = False
break
return ans
現在我定義一些輔助函數
def grid_to_list(grid):
lst = []
for line in grid:
lst+=line
return lst
def parse_coordinate(s):
row = s // 9
col = s % 9
return row,col
def choice(x):
if len(x) > 1:
return len(x)
else:
return 10
def check_constraints(grid,value,row,col):
ans = True
if grid[row].count(value) > 0:
ans = False
if list(zip(*grid)).count(value) > 0:
ans = False
if box(row,col,grid).count(value) > 0:
ans = False
return ans
最后我們將介紹這個故事的主要部分 - 回溯
def explore(grid):
if solved(grid):
return True #YAY!!!
else:
while not solved(grid):
lst = grid_to_list(grid) #i parse grid to list because i need
sth = min(*lst,key=choice) #to find the cell with min length
pos = lst.index(sth)
sth = lst[pos]
row,col = parse_coordinate(pos)
for n in sth:
if check_constraints(grid,n,row,col): #if it's safe to place
grid[row][col] = n #sth in grid[row][col]
if explore(grid): #i put it there and
return True #continue exploring
grid[row][col]=sth #if this doesn't work i return to the cell the previous value
return False
其他一些功能:將它重新組合在一起
def str_to_int(grid):
for i in range(9):
for j in range(9):
grid[i][j]=int(grid[i][j])
return grid
def solve(grid):
grid = constraints(grid)
if explore(grid):
nice_print(str_to_int(grid))
else:
print("there seems to be a problem")
所以我的程序將以下解決方案返回到上面的網格:
[5, 6, 8, 7, 1, 9, 3, 2, 4]
[9, 7, 1, 2, 3, 4, 5, 6, 8]
[2, 3, 4, 5, 6, 8, 7, 9, 1]
[1, 8, 5, 9, 7, 2, 6, 4, 3]
[3, 9, 7, 6, 4, 1, 8, 5, 2]
[4, 2, 6, 3, 8, 5, 9, 1, 7]
[6, 1, 9, 8, 2, 3, 4, 7, 5]
[7, 4, 3, 1, 5, 6, 2, 8, 9]
[8, 5, 2, 4, 9, 7, 1, 3, 6]
但這個網格
[[0, 7, 1, 6, 8, 4, 0, 0, 0],
[0, 4, 9, 7, 0, 0, 0, 0, 0],
[5, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 8, 0, 0, 0, 0, 5, 0, 4],
[0, 0, 0, 3, 0, 7, 0, 0, 0],
[2, 0, 3, 0, 0, 0, 0, 9, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 9],
[0, 0, 0, 0, 0, 3, 7, 2, 0],
[0, 0, 0, 4, 9, 8, 6, 1, 0]]
它無法解決。 它嘗試不同的數字,並沒有停止:(
首先,在def探索中,我不會有“如果解決了”。 這意味着,當它沒有解決時,你進行兩次測試。 相反,你可以在while循環后得到一個'return true'。 那么,如果它已經解決了,它將永遠不會進入while循環並返回true。
我也懷疑pos = lst.index(sth)
可能有點慢。 編寫一個只返回最短列表的pos
的函數可能會更好。 如果它正在進行參考比較,那可能差別不大。 我也很驚訝choice()
並沒有在int上測試len()。 這個輔助函數可能會使代碼更清晰:
def find_min_list(grid):
minRow = 0
minCol = 0
minLength = 10
for i in range(10):
for j in range(10):
if type(grid[i][j]) is list and len(grid[i][j]) < minLength:
minLength = len(grid[i][j])
minRow = i
minCol = j
return minRow, minCol
這是未經測試但應該做的伎倆
現在只是查看你的代碼很難診斷出什么問題。 我建議嘗試輸出一些信息到文本文件。 這樣你就可以看出你的探索是否正在進行無限循環(它可能是多次選擇相同的最小值),或者你的解算器只需要花費很長的時間才能完成。 如果它是后者,很難確定即使是沒有輸出的問題。 另一個選擇是讓你的探索功能打印出一個“深度”,這樣你就可以看到它是深入還是不斷陷入深度1。
編輯:我懷疑最重要的問題是你的探索是非常昂貴的。 現在,它天真地嘗試列表中所有未解決部分的每種約束組合。 每次嘗試一個數字時,一個優化就是預先形成'約束'。 希望這會讓你的探索不必深入,因為它會開始刪除很多潛在的列表。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.