前言
数独是一种在9×9的方格中填写数字1~9的游戏,它的规则是这样的:每一行都包含数字1~9;每一列都包含数字1~9;9×9的方格共分成3×3的大方格时,每个大方格里都包含1~9。它的行、列和大方格如下图所示:
这样的题目主要考察同学们的观察和逻辑推理能力,多练习此类题目有益于大脑的逻辑训练。 本文来介绍一种用
Python
求解该类问题的方法。
题目分析
比如一个数独是这样的:
为了用
Python
求解,将上面的数独图形转换为二维数组,其中空的部分用
0
表示:
sudoku = [ [8,1,0,0,3,0,0,2,7], [0,6,2,0,5,0,0,9,0], [0,7,0,0,0,0,0,0,0], [0,9,0,6,0,0,1,0,0], [1,0,0,0,2,0,0,0,4], [0,0,8,0,0,5,0,7,0], [0,0,0,0,0,0,0,8,0], [0,2,0,0,1,0,7,5,0], [3,8,0,0,7,0,0,4,2],]
为了让上述二维数组在控制台显示得更象一个数独,我们可以将其打印出来:
def printsudoku(): print("┌"+"─"*7 + "┬"+"─"*7 + "┬"+"─"*7 + "┐") for i in range(len(sudoku)): line = "│ " if i == 3 or i == 6: print("├"+"─"*7 + "┼"+"─"*7 + "┼"+"─"*7 + "┤") for j in range(len(sudoku[i])): if j == 3 or j == 6: line += "│ " if sudoku[i][j] == 0: flag = "□" else: flag = str(sudoku[i][j]) line += flag + " " print(line + "│") print("└"+"─"*7 + "┴"+"─"*7 + "┴"+"─"*7 + "┘")printsudoku()
解题步骤
找到未填充的单元格
为了解一个数独,我们必须首先确定在二维数组
sudoku
中还未填充数字1~9的单元格,当然只要确定了该单元格的行和列,它肯定就可以被确定:
def findNextCellToFill(sudoku): for i in range(9): for j in range(9): if sudoku[i][j] == 0: return i,j return -1,-1
上述函数只要找到一个单元格是
0
的数,即意味着该位置需要填写1~9,然后将其位置处的行列数值返回,若整个二维数组中所有单元格都不为0,则意味着该二维数独已经解出答案。
校验填充数字的有效性
我们计划采用的方法是穷举法,这样就必须对每一个填充进来的值进行测试,测试的标准就是依据数独本身的规则,即每行、每列、每个大方格中数字不重且都不为0:
def isValid(sudoku, i, j, e): rowOk = all([e != sudoku[i][x] for x in range(9)]) if rowOk: columnOk = all([e != sudoku[x][j] for x in range(9)]) if columnOk: secTopX, secTopY = 3*(i//3), 3*(j//3) for x in range(secTopX, secTopX+3): for y in range(secTopY, secTopY+3): if sudoku[x][y] == e: return False return True return False
解数独 在以上的准备工作做好后,我们用一个递归函数来解数独:
def solveSudoku(sudoku, i=0, j=0): i,j = findNextCellToFill(sudoku) if i == -1: return True for e in range(1, 10): if isValid(sudoku, i, j ,e): sudoku[i][j] = e if solveSudoku(sudoku, i, j): return True sudoku[i][j] = 0 return False
上述函数中用到了递归,前三行是确认数独中还未填充有效数字的单元格行列值,如果函数
findNextCellToFill
返回
-1
,那就意味着已经找到数独的解。 之后在这个空单元格内测试1~9这九个数字哪个合适,如果合适就填上,在这个假设正确的情况下再回调自身出现了问题,那就必须回退,进行下一个值的测试,直到满足为止。 以上是假定数独有解的情况,当然如果无解,这个函数以返回
False
而终结。 下面利用该函数对数独进行求解:
solveSudoku(sudoku)printsudoku()
小结
本文对数独的解算给出一种
Python
方法,请有兴趣的同学找一些数独来测试。