使用Python進(jìn)行數(shù)獨(dú)求解詳解(二)
1. 引言
本文是數(shù)獨(dú)游戲問題求解的第二篇,在前文中我們使用回溯算法實(shí)現(xiàn)了最簡單版本的數(shù)獨(dú)游戲求解方案。本文主要在前文解決方案的基礎(chǔ)上,來思考如何通過改進(jìn)來提升數(shù)獨(dú)問題求解算法的性能。
閑話少說,我們直接開始吧。 :)
2. 前文回顧
我們首先來回顧下前文的回溯算法,如下圖示:
在前文中,我們引入了回溯算法來對數(shù)獨(dú)問題求解,通過迭代每個(gè)子單元格cell的所有可能取值來暴力解決該問題,直到引入數(shù)獨(dú)九宮格中的新值與屬于同一行,列或block塊的子單元格中確定值之間沒有沖突為止。這種解決方案雖然可以有效解決該問題,但是它絕對不是最佳的解決方案,因?yàn)樗鼪]有合理利用數(shù)獨(dú)九宮格中提供的附加先驗(yàn)信息。下面,我們來一步步對前文算法進(jìn)行優(yōu)化吧。。。
3. 減少非比要的迭代次數(shù)
優(yōu)化上述算法的第一個(gè)想法來自于這樣的觀察,我們的算法按順序迭代所有數(shù)字1到9,直到它找到一個(gè)與已經(jīng)包含相同值的同一行,列或block塊中的另一個(gè)單元格不沖突的值。但是,數(shù)獨(dú)九宮格中一些確定值會已經(jīng)為我們提供了一些信息,說明哪些數(shù)字不可能添加到某個(gè)子單元格cell中。
# Solve Sudoku using backtracking def solve(board): blank = findEmpty(board) if not blank: return True else: row, col = blank for i in range(1,10): if isValid(board, i, blank): board[row][col] = i if solve(board): return True board[row][col] = 0 return False
我們優(yōu)化的思路是首先掃描我們的數(shù)獨(dú)九宮格,將每個(gè)子單元格的所有可能的合法候選值保存在內(nèi)存中然后再逐個(gè)迭代它們,而不是迭代所有數(shù)字。參考下圖,演示了數(shù)獨(dú)九宮格的 2 個(gè)子單元格的候選值的集合。正如我們的游戲規(guī)則所暗示的那樣,每行,每列和每個(gè)block塊不能包含相同的數(shù)字,因此在屬于給定子單元格的同一行,列和所屬block塊的單元格中已經(jīng)確定的所有數(shù)字都被排除在外。
既然有了優(yōu)化思路,那么我們接下來就可以來用代碼實(shí)現(xiàn)上述想法啦.
3.1 生成候選值字典
接著我們需要一個(gè)數(shù)據(jù)結(jié)構(gòu)(這里我們選用字典)來保存每個(gè)子單元格的候選值列表,該函數(shù)通過遍歷整個(gè)九宮格中空的子單元格并調(diào)用我們的allowedValues()函數(shù)來返回子單元格的候選值列表.
樣例代碼如下:
# Store in a dictionary the legitimate # values for each individual cell def cacheValidValues(board): cache = dict() for i in range(9): for j in range(9): if board[i][j] == 0: cache[(i,j)] = allowedValues(board,i,j) return cache
3.2 生成候選值列表
在上小節(jié)中的allowValues() 函數(shù)與我們在前篇文中看到的isValid() 函數(shù)具有類似的邏輯,但在本例中,它返回值為每個(gè)子單元格所提取到的合法數(shù)字的列表。
樣例代碼如下:
def allowedValues(board,row,col): numbersList = list() for number in range(1,10): found = False # Check if all row elements include this number for j in range(9): if board[row][j] == number: found = True break # Check if all column elements include this number if found == True: continue else: for i in range(9): if board[i][col] == number: found = True break # Check if the number is already included in the block if found == True: continue else: rowBlockStart = 3* (row // 3) colBlockStart = 3* (col // 3) rowBlockEnd = rowBlockStart + 3 colBlockEnd = colBlockStart + 3 for i in range(rowBlockStart, rowBlockEnd): for j in range(colBlockStart, colBlockEnd): if board[i][j] == number: found = True break if found == False: numbersList.append(number) return numbersList
3.3 函數(shù)調(diào)用
有了我們的單元格候選值緩存字典,下面我們準(zhǔn)備測試該方案是否會顯著提高我們的程序性能。
為此我們還需要將 solve() 函數(shù)替換為一個(gè)新的函數(shù)solveWithCache(),該函數(shù)只迭代每個(gè)子單元格cell的合法值列表,而不是所有數(shù)字 1–9。
代碼如下:
def solveWithCache(board, cache): blank = findEmpty(board) if not blank: return True else: row, col = blank for value in cache[(row,col)]: if isValid(board, value, blank): board[row][col] = value if solveWithCache(board, cache): return True board[row][col] = 0 return False
在實(shí)現(xiàn)所有改動后測試我們的代碼為我們提供了所需的結(jié)果,與我們的第一個(gè)版本相比,跑同樣50組測試用例執(zhí)行時(shí)間明顯縮短:
The execution time of above program is : 15.41820478439331 s
4. 總結(jié)
本文為數(shù)獨(dú)游戲求解的第二篇,主要基于第一篇的回溯思想來思考如何利用數(shù)獨(dú)九宮格的先驗(yàn)思想來減少回溯的迭代次數(shù),進(jìn)而提升算法提升效率,并給出了完整代碼實(shí)現(xiàn).
到此這篇關(guān)于使用Python進(jìn)行數(shù)獨(dú)求解詳解(二)的文章就介紹到這了,更多相關(guān)Python數(shù)獨(dú)求解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Pycharm配置遠(yuǎn)程SSH服務(wù)器實(shí)現(xiàn)(切換不同虛擬環(huán)境)
本文主要介紹了Pycharm配置遠(yuǎn)程SSH服務(wù)器實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02pandas實(shí)現(xiàn)數(shù)據(jù)合并的示例代碼
本文主要介紹了pandas實(shí)現(xiàn)數(shù)據(jù)合并的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05如何利用Python獲取鼠標(biāo)的實(shí)時(shí)位置
這篇文章主要給大家介紹了關(guān)于如何利用Python獲取鼠標(biāo)的實(shí)時(shí)位置的相關(guān)資料,主要利用的是pyautogui,一個(gè)自動化鍵鼠操作的Python類庫,需要的朋友可以參考下2022-01-01Django 開發(fā)環(huán)境與生產(chǎn)環(huán)境的區(qū)分詳解
這篇文章主要介紹了Django 開發(fā)環(huán)境與生產(chǎn)環(huán)境的區(qū)分詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07python實(shí)現(xiàn)將json多行數(shù)據(jù)傳入到mysql中使用
這篇文章主要介紹了python實(shí)現(xiàn)將json多行數(shù)據(jù)傳入到mysql中使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12