使用Python進行數獨求解詳解 二

2022-10-08 16:00:20 字數 3371 閱讀 2568

目錄

本文是數獨遊戲問題求解的第二篇,在前文中我們使用回溯演算法實現了最簡單版本的數獨遊戲求解方案。本文主要在前文解決方案的基礎上,來思考如何通過改進來提公升數獨問題求解演算法的效能。

閒話少說,我們直接開始吧。 :)

我們首先來回顧下前文的回溯演算法,如下圖示:

在前文中,我們引入了回溯演算法來對數獨問題求解,通過迭代每個子單元格cell的所有可能取值來暴力解決該問題,直到引入數獨九宮格中的新值與屬於同一行,列或block塊的子單元格中確定值之間沒有衝突為止。這種解決方案雖然可以有效解決該問題,但是它絕對不是最佳的解決方案,因為它沒有合理利用數獨九宮格中提供的附加先驗資訊。下面,我們來一步步對前文演算法進行優化吧。。。

優化上述演算法的第乙個想法來自於這樣的觀察,我們的演算法按順序迭代所有數字1到9,直到它找到乙個與已經包含相同值的同一行,列或block塊中的另乙個單元格不衝突的值。但是,數獨九宮格中一些確定值會已經為我們提供了一些資訊,說明哪些數字不可能新增到某個子單元格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

我們優化的思路是首先掃瞄我們的數獨九宮格,將每個子單元格的所有可能的合法候選值儲存在記憶體中然後再逐個迭代它們,而不是迭代所有數字。參考下圖,演示了數獨九宮格的 2 個子單元格的候選值的集合。正如我們的遊戲規則所暗示的那樣,每行,每列和每個block塊不能包含相同的數字,因此在屬於給定子單元格的同一行,列和所屬block塊的單元格中已經確定的所有數字都被排除在外。

既然有了優化思路,那麼我們接下來就可以來用**實現otjau上述想法啦.

接著我們需要乙個資料結構(這裡我們選用字典)來儲存每個子單元格的候選值列表,該函式通過遍歷整個九宮格中空的子單元格並呼叫我們的allowedvalues()函式來返回子單元格的候選值列表.

樣例**如下:

# 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

在上小節中的allowvalues() 函式與我們在前篇文中看到的isvalid() 函式具有類似的邏輯,但在本例中,它返回值為每個子單元格所提取到的合法數字的列表。

樣otjau例**如下:

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:

www.cppcns.com 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

colwww.cppcns.comblockend = 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

有了我們的單元格候選值快取字典,下面我們準備測試該方案是否會顯著提高我們的程式效能。

為此我們還需要將 solve() 函式替換為乙個新的函式solvewithcache(),該函式只迭代每個子單元格cell的合法值列表,而不是所有數字 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

在實現所有改動後測試我們的**為我們提供了所需的結果,與我們的第乙個版本相比,跑同樣50組測試用例執行時間明顯縮短:

the execution time of above program is : 15.41820478439331 s

本文為數獨遊戲求解的第二篇,主要基於第一篇的回溯思想來思考如何利用數獨九宮格的先驗思想來減少回溯的迭代次數,進而提公升演算法提公升效率,並給出了完整**實現.

使用python進行資料清洗

1 在資料清洗前,我們需要先檢視資料概況,了解我們需要清洗的資料大概包含什麼字段 每個字段下面資料範圍大概如何,資料清洗常用到的函式和語法分別有 info函式 用於了解資料總體情況,包括行數,列數,各列名稱等,比如 shape函式 用於檢視資料矩陣的行和列 describe函式 了解datafram...

Python 使用Pandas進行資料預處理

利用pandas庫中的get dummies函式對類別型特徵進行啞變數處理。get dummies語法 pandas.get dummies data,prefix none,prefix sep dummy na false,columns none,sparse false,drop first...

如何使用Python與Mysql進行資料互動

自己的夢想需要你自己去實現 python 在mysql的官網獲取python與mysql的互動手冊mysql官方手冊 pythontodatabase.py import mysql.connector cnx mysql.connector.connect user root password 6...