回溯算法可视化解数独
解数独
terminal 内执行可以看到过程
内置判断数独是否有效的方法
内置了两个待解决的数独,答案是一样的
- 快速方法,内置数独缺失的比较少,返回较快
python3 script.py quick
- 正常运行
python3 script.py
import sys
import time
def is_valid_sudoku(board) -> bool:
"""判断是否有效只需要判断是否重复即可,set方法高效,只要去重后的长度和之前的长度相同,说明无重复"""
# 行数否重复
for row in range(9):
_row = [i for i in board[row] if i != "."]
if len(set(_row)) != len(_row):
return False
# 列是否重复
for col in range(9):
_col = [board[row][col] for row in range(9) if board[row][col] != "."]
if len(set(_col)) != len(_col):
return False
# 九宫格
for i in [3, 6, 9]:
for j in [3, 6, 9]:
res = []
cols = list(range(j - 3, j))
for row in range(i - 3, i):
res.extend([board[row][col] for col in cols if board[row][col] != "."])
if len(set(res)) != len(res):
return False
return True
def solve_sudoku(board):
"""board: [[]] 9*9"""
# 是否有效
if not is_valid_sudoku(board):
print("invalid sudoku")
exit(-1)
res = []
def backtrack(bd, row, col):
print("=======Trying Answer=======")
for b in bd:
print("\033[K", " ".join(b))
print("\033[F" * 11)
time.sleep(0.2)
# row == length 遍历结束
if row == len(board):
for b in bd:
if "." in b:
# 未找到解
return
# 找到解
print("\033[K========Find Answer========")
for b in bd:
print("\033[K", " ".join(b))
# 多个解可以res.append(deepcopy(bd))
exit(0)
# 待解决
if bd[row][col] == ".":
# 尝试1-9
for i in range(1, 10):
bd[row][col] = str(i)
# 判断是否有效(行、列、宫)
if not is_valid(bd, row, col):
bd[row][col] = "."
continue
# 列index小于8,行不动,继续遍历列
if col < 8:
backtrack(bd, row, col + 1)
else:
# 列大于等于8,下一行第一列
backtrack(bd, row + 1, 0)
bd[row][col] = "."
else: # 本来就有数字,直接去处理下一个
if col < 8:
backtrack(bd, row, col + 1)
else:
backtrack(bd, row + 1, 0)
backtrack(board, 0, 0)
return res
def is_valid(board, row, col):
# 行
res = []
for i in range(9):
if board[row][i] != ".":
res.append(board[row][i])
if len(set(res)) != len(res):
return False
# 列
res = []
for i in range(9):
if board[i][col] != ".":
res.append(board[i][col])
if len(set(res)) != len(res):
return False
# 九宫格
# (8+3)//3*3 = 9,向上取3,6,9
row = (row + 3) // 3 * 3
col = (col + 3) // 3 * 3
res = []
for r in range(row - 3, row):
for c in range(col - 3, col):
if board[r][c] != ".":
res.append(board[r][c])
if len(set(res)) != len(res):
return False
return True
if __name__ == "__main__":
quick = False
if len(sys.argv) == 2:
if sys.argv[-1] == "quick":
quick = True
question = [
["5", "3", ".", ".", "7", ".", ".", ".", "."],
["6", ".", ".", "1", "9", "5", ".", ".", "."],
[".", "9", "8", ".", ".", ".", ".", "6", "."],
["8", ".", ".", ".", "6", ".", ".", ".", "3"],
["4", ".", ".", "8", ".", "3", ".", ".", "1"],
["7", ".", ".", ".", "2", ".", ".", ".", "6"],
[".", "6", ".", ".", ".", ".", "2", "8", "."],
[".", ".", ".", "4", "1", "9", ".", ".", "5"],
[".", ".", ".", ".", "8", ".", ".", "7", "9"],
]
quick_question = [
["5", "3", "4", "6", "7", "8", ".", "1", "2"],
["6", "7", "2", "1", "9", "5", "3", "4", "8"],
["1", "9", ".", "3", "4", "2", ".", "6", "7"],
["8", ".", "9", ".", "6", "1", "4", "2", "3"],
["4", "2", "6", "8", "5", "3", "7", "9", "1"],
["7", "1", "3", "9", "2", ".", "8", "5", "6"],
["9", ".", "1", "5", "3", "7", "2", "8", "4"],
["2", "8", "7", "4", "1", "9", "6", "3", "5"],
["3", "4", "5", ".", "8", "6", "1", "7", "9"],
]
try:
solve_sudoku(question if not quick else quick_question)
except KeyboardInterrupt:
...
# 正确答案[
# ["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"],
# ]