topic
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。
一个数独。
答案被标成红色。
Note:
- 给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
- 你可以假设给定的数独只有唯一解。
- 给定数独永远是 9x9 形式的。
思路
给定一个9x9数组board,先判断此数独是否有效或有解,若无解则打印“This is an invalid sudoku!”,若有解则打印“This is a valid sudoku!”,并解此数独打印结果。
1. 判断数独是否有效
定义一个函数,判断此数独是否有解:isValidSudoku()
,输入参数是9x9的二维数组,返回值是True或False。函数体中依次判断此数独是否满足数独的三条解法规则。
第一条,数字 1-9 在每一行只能出现一次:依次取出board中每行数据i,用列表生成式去除列表i中所有的‘.’得到列表r,再将列表i变成集合去重,去除元素‘.’,得到i中所有非‘.’的元素集合row,若r与row长度相同说明该行中数字元素不重复,否则说明i中有重复的数字元素,一旦出现重复数字,此数独无效,直接返回False。
for i in board:
r=[j for j in i if j!='.']
row=set(i)-{'.'}
if len(r)!=len(row):
return False
第二条,数字 1-9 在每一列只能出现一次:先遍历列表board,用列表生成式取出每列数据的列表temp,与第一条一样,除去temp中‘.’得到列表col,对列表temp去重,再删去元素‘.’得到集合c,比较集合c和列表col的长度,若长度相同说明该列数据无重复数字元素,若长度不同则违反了条件二,直接返回False。
for i in range(9):
temp=[board[j][i] for j in range(9)]
col=[j for j in temp if j!='.']
c=set(temp)-{'.'}
if len(c)!=len(col):
#print('col false:',i)
return False
第三条,数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次:首先要取出每个3x3宫内的数据,采用列表切片取出三行每行三列的数据(首行首列位置由range(0,9,3)给出),每宫的数据存为列表temp,然后同前两条规则一样,去除‘.’的列表unit和去重的集合u比较长度,判断是否有重复数字。
for i in range(0,9,3):
for j in range(0,9,3):
temp = board[i][j:j + 3] + board[i + 1][j:j + 3] + board[i + 2][j:j + 3]
unit = [i for i in temp if i!='.']
u=set(temp)-{'.'}
if len(unit)!=len(u):
return False
return True
数独中所有行、列及3x3宫内数据全部满足以上三条规则,则判定此数独有效,返回True。
2. 解数独
定义解数独的函数solveSudoku(board)
,输入参数是待解的数独二维数组board,解数独的思路如下:
①首先找到数独中的第一个‘.’:如果找不到‘.’,说明已解完此数独,返回结果board即可(递归的基例);如果能找到,记下第一个‘.’的行列坐标(row,col),此位置为本次解的目标位置。
def solveSudoku(board):
#①find position of the first '.'
row,col=find_empty(board)
if row==None:
return board
定义一个函数,寻找第一个‘.’:find_empty()
。输入参数为待解数独二维数组board,返回值为寻找到的第一个‘.’的行列索引(索引值0~8);当board中不存在‘.’时,返回两个None,代码如下:
def find_empty(board):
for r in range(9):
for c in range(9):
if board[r][c]=='.':
return r,c
else:
return None,None
②然后根据三条规则依次算出目标位置(row,col)的可行解集合key1、key2、key3,对这三个可行解集合求交集得到本轮可行解集合key。
#②find feasible key for the position
key1={'1','2','3','4','5','6','7','8','9'}-set(board[row])
key2={'1','2','3','4','5','6','7','8','9'}-set([board[i][col] for i in range(9)])
key3=find_key3(board,row,col)
key=key1&key2&key3
③最后进行递归操作:
若key为空,说明此情况无解,直接返回False;
若key中有值,弹出一个填入目标位置,对填入一个可行解后的新数独进行下一轮的解,若下一轮解数独有返回值,则此返回值即为此数独的最终解,直接返回该结果即可;若下一轮解数独返回False,说明本次填入的解不对,继续从key中pop出其他可行解进行递归,直至得到最终结果。
#③recursion
while key:
k = key.pop()
board[row][col]=k
temp=solveSudoku(board)
if temp:
return temp
else:
board[row][col] = '.'
else:
return False
代码
def find_key3(board,row,col):
r=row//3*3
c=col//3*3
temp=board[r][c:c+3]+board[r+1][c:c+3]+board[r+2][c:c+3]
key3={'1','2','3','4','5','6','7','8','9'}-set(temp)
return key3
def find_empty(board):
for r in range(9):
for c in range(9):
if board[r][c]=='.':
return True,r,c
else:
return False,board,None
def solveSudoku(board):
"""
:type board: List[List[str]]
:rtype: void Do not return anything, modify board in-place instead.
"""
#①find position of the first '.'
row,col=find_empty(board)
if row==None:
return board
#②find feasible key for the position
key1={'1','2','3','4','5','6','7','8','9'}-set(board[row])
key2={'1','2','3','4','5','6','7','8','9'}-set([board[i][col] for i in range(9)])
key3=find_key3(board,row,col)
key=key1&key2&key3
#③recursion
while key:
k = key.pop()
board[row][col]=k
temp=solveSudoku(board)
if temp:
return temp
else:
board[row][col] = '.'
else:
return False
def isValidSudoku(board):
"""
:type board: List[List[str]]
:rtype: bool
"""
for i in board:
r=[j for j in i if j!='.']
row=set(i)-{'.'}
if len(r)!=len(row):
return False
for i in range(9):
temp=[board[j][i] for j in range(9)]
col=[j for j in temp if j!='.']
c=set(temp)-{'.'}
if len(c)!=len(col):
return False
for i in range(0,9,3):
for j in range(0,9,3):
temp = board[i][j:j + 3] + board[i + 1][j:j + 3] + board[i + 2][j:j + 3]
unit = [i for i in temp if i!='.']
u=set(temp)-{'.'}
if len(unit)!=len(u):
return False
return True
def main():
board=[["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"]]
for i in board:
print(i)
if isValidSudoku(board):
print('This is a valid sudoku! \nThe sudoku solution is as follow:\n')
key = solveSudoku(board)
for i in key:
print(i)
else:
print('This is an invalid sudoku!')