前言


数独是一种在9×9的方格中填写数字1~9的游戏,它的规则是这样的:每一行都包含数字1~9;每一列都包含数字1~9;9×9的方格共分成3×3的大方格时,每个大方格里都包含1~9。它的行、列和大方格如下图所示:

python 有效的数独 python解决数独_二维数组


这样的题目主要考察同学们的观察和逻辑推理能力,多练习此类题目有益于大脑的逻辑训练。 本文来介绍一种用 Python

求解该类问题的方法。

题目分析


比如一个数独是这样的:

python 有效的数独 python解决数独_二维数组_02


为了用 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()

python 有效的数独 python解决数独_数独_03

解题步骤

找到未填充的单元格


为了解一个数独,我们必须首先确定在二维数组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 有效的数独 python解决数独_python 有效的数独_04

小结


本文对数独的解算给出一种Python方法,请有兴趣的同学找一些数独来测试。