日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数独问题流程图_算法实践——数独的基本解法

發(fā)布時(shí)間:2023/12/10 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数独问题流程图_算法实践——数独的基本解法 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

數(shù)獨(dú)(Sudoku)是一種運(yùn)用紙、筆進(jìn)行演算的邏輯游戲。玩家需要根據(jù)9×9盤面上的已知數(shù)字,推理出所有剩余空格的數(shù)字,并滿足每一行、每一列、每一個(gè)粗線宮內(nèi)的數(shù)字均含1-9,不重復(fù)。 每一道合格的數(shù)獨(dú)謎題都有且僅有唯一答案,推理方法也以此為基礎(chǔ),任何無解或多解的題目都是不合格的。

如下圖所示,就是一個(gè)數(shù)獨(dú)的題目

關(guān)于數(shù)獨(dú)的詳細(xì)介紹,參看“百度百科——數(shù)獨(dú)”

數(shù)獨(dú)的基本解法就是利用規(guī)則的摒棄法

一些定義

每一行稱為數(shù)獨(dú)的行,每一列稱為數(shù)獨(dú)的列,每一個(gè)小九宮格稱為數(shù)獨(dú)的宮。數(shù)獨(dú)的基本規(guī)則就是每一行、每一列、每一宮中,1-9這9個(gè)數(shù)字都只出現(xiàn)一次。

用(行,列)表示上圖的單元格,例如(1,1)表示第一行第一列的單元格,(2,4)表示第二行第四列的單元格

如上圖,每個(gè)空白單元格中能填的數(shù)字都是有限制的。

例如:(1,1)就只能填7和8;而(6,4),只能填8;

那些只能填一個(gè)數(shù)字的空白單元格,我們稱之為唯一數(shù)單元格,上圖中(6,4)就是唯一數(shù)單元格

解題的順序,就是從唯一數(shù)單元格開始,由于唯一數(shù)單元格只能填一個(gè)數(shù),故先在這個(gè)單元格里填數(shù)。在這個(gè)單元格里填數(shù),由于規(guī)則的定義,那么這個(gè)單元格所在的行、所在的列、所在的宮的其他單元格就不能再填這個(gè)數(shù)了。這些單元格能填的數(shù)的可能性就少了。有可能會(huì)產(chǎn)生新的唯一數(shù)單元格。

在相當(dāng)?shù)囊恍┑臄?shù)獨(dú)題目中,從唯一數(shù)單元格開始填數(shù),不停的在唯一數(shù)單元格填數(shù)就可以把數(shù)獨(dú)解出來。

如果在解題的過程中,發(fā)現(xiàn)某些空白單元格沒有數(shù)字能填這樣的單元格稱之為無解單元格,那就說明:要么這個(gè)數(shù)獨(dú)沒有解;要么之前的解題過程有問題,需要返回檢查之前的解題過程查看。

但是還有不少的數(shù)獨(dú)的題目,在解題的過程中,在還有空白單元格的情況下,卻找不到唯一數(shù)單元格,也就是意味著每個(gè)空白單元格中能填的數(shù)字至少有2個(gè)。我們稱之為無唯一數(shù)單元格的狀況

這個(gè)時(shí)候怎么辦?我們找到其中一個(gè)可能數(shù)最少的空白單元格(這個(gè)沒有定論,可以是可能數(shù)最少的空白單元格;也可以是第一個(gè)空白單元格;也可以是可能數(shù)最多的空白單元格,選哪個(gè)空白單元格對(duì)后面的解題是否有影響,沒有證明過,不好妄下定論。憑感覺選可能數(shù)最少的空白單元格是最好的選擇),由于能填的數(shù)字不止一個(gè),先把當(dāng)前的狀態(tài)保存起來,再在能選的數(shù)字中選擇一個(gè)數(shù)字填寫(從小到大選擇),然后繼續(xù)求解下去。如果能解出最后的結(jié)果,說明當(dāng)前的選擇是正確的;如果后面的求解過程有問題,說明當(dāng)前的數(shù)字的選擇有問題,那么再挑選另一個(gè)數(shù)填寫,繼續(xù)求解。如果,所有的選擇都求不出最后的結(jié)果,還是說明:要么這個(gè)數(shù)獨(dú)沒有解;要么之前的解題過程有問題,需要返回檢查之前的解題過程查看。如此反復(fù),直到求出最終的答案。

會(huì)有種極端的情況(可能性不大)。那就是在當(dāng)前的空白單元格的所有可能的數(shù)字都選擇了一遍,都沒有解。而之前又沒有出現(xiàn)無唯一數(shù)單元格的狀況。那就說明這個(gè)數(shù)獨(dú)根本就沒有解

下圖是數(shù)獨(dú)求解的流程圖

下面談?wù)勗撍惴ǖ木唧w實(shí)現(xiàn)

1、數(shù)獨(dú)狀態(tài)的表示

用計(jì)算機(jī)來求解數(shù)獨(dú)。基本的一點(diǎn)就是如何表示數(shù)獨(dú)的狀態(tài)。

用整形一維數(shù)組來表示數(shù)獨(dú)的狀態(tài)

用Num(80)表示數(shù)獨(dú)的狀態(tài)(數(shù)組的下標(biāo)從0開始),數(shù)獨(dú)是一個(gè)二維表格,而數(shù)組是一維數(shù)組。那么就存在一維和二維之間的轉(zhuǎn)換

一維數(shù)組的下標(biāo)Index(小標(biāo)從0開始)和二維下標(biāo)X、Y(下標(biāo)從0開始)之間的轉(zhuǎn)換公式

一維到二維的轉(zhuǎn)換

X=Int(Index/9)

Y=Index mod 9

二維到一維的轉(zhuǎn)換

Index=X*9+Y

數(shù)組中的每個(gè)整數(shù)表示數(shù)獨(dú)對(duì)應(yīng)的單元格的狀態(tài)

正數(shù)表示空白單元格能填的數(shù)的組合,用二進(jìn)制表示。用位來表示該單元格是否能填相應(yīng)的數(shù)字,1表示能填,0表示不能填。

如文章開始的數(shù)獨(dú)的單元格(1,1)可能填7和8,則第7位和第8位上是1(位數(shù)是從后往前數(shù)),其余位都是0,用整數(shù)表示就是Num(0)=0110000002=192

每在單元格中填一個(gè)數(shù)字,則把相應(yīng)的行、列、宮中其余的單元格把該數(shù)字去掉。

我們可以充分利用位運(yùn)算來簡化去數(shù)字的過程。如:要把單元格去掉7這個(gè)數(shù)字的可能。首先7對(duì)應(yīng)的二進(jìn)制位0010000002,取其反數(shù)得到1101111112,再和目標(biāo)單元格的數(shù)值進(jìn)行AND的位運(yùn)算,來實(shí)現(xiàn)去除該單元格7這個(gè)數(shù)字的可能性(由于位運(yùn)算的便捷,不需要考慮該單元格是否原本包含7這個(gè)數(shù)字的可能性)。

如:(1,1)=0110000002 AND 1101111112=0100000002,去除7這個(gè)可能性,只剩8這個(gè)可能性了,也就是成為唯一數(shù)單元格

再比如:(1,9)=0100000102 AND 1101111112=0100000102,原本單元格就沒有7這個(gè)可能性,執(zhí)行位運(yùn)算后,還是原來的可能性,沒有發(fā)生變化。

負(fù)數(shù)表示該單元格已經(jīng)確定的數(shù),例如:(1,2)=-6,表示該單元格已近填了數(shù)字6

0表示該單元格既沒有填確定的數(shù)字,也沒有可填數(shù)的可能性。也就是上文說的無解單元格

為了算法中計(jì)算的方便,事先把這些二進(jìn)制數(shù)都緩存起來,用一個(gè)一維的數(shù)組表示

用數(shù)組V來表示各個(gè)位對(duì)應(yīng)的數(shù)字

V(0)=0000000012=1

V(1)=0000000102=2

V(2)=0000001002=4

V(3)=0000010002=8

V(4)=0000100002=16

V(5)=0001000002=32

V(6)=0010000002=64

V(7)=0100000002=128

V(8)=1000000002=256

V(9)=1111111112=511

數(shù)字7對(duì)應(yīng)的二進(jìn)制數(shù)為V(6)=0010000002=64,7的反數(shù)為V(9)-V(6)=1101111112=447

每個(gè)單元格初始的值都是V(9)=1111111112=511

2、如何獲得一個(gè)單元格的可填數(shù)的個(gè)數(shù)

由于是用二進(jìn)制來表示單元格的狀態(tài),那么可填數(shù)的個(gè)數(shù)就是該數(shù)字中1的個(gè)數(shù)。我們之前有一個(gè)很方便的方法快速計(jì)算一個(gè)數(shù)中1的個(gè)數(shù),參看算法的強(qiáng)大——快速計(jì)算一個(gè)正二進(jìn)制整數(shù)中包含多少個(gè)1。

3、狀態(tài)的緩存

依據(jù)之前的說法,在碰到無唯一數(shù)單元格的情況時(shí),要把當(dāng)前的狀態(tài)緩存起來。考慮到實(shí)際情況,從算法的角度上來說,用棧(先進(jìn)后出)這個(gè)數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)比較合適。可以自己寫一個(gè)棧的實(shí)現(xiàn)。但是,目前很多的編程語言都實(shí)現(xiàn)了基本的數(shù)據(jù)結(jié)構(gòu),提供了基本的數(shù)據(jù)結(jié)構(gòu)的類和方法供我們調(diào)用。

以Visual Studio為例,它有Stack這個(gè)類,實(shí)現(xiàn)了棧的基本操作。有兩個(gè)棧的方法:Push(壓棧)——把數(shù)據(jù)寫到棧里面;Pop(出棧)——把數(shù)據(jù)從棧里提出來,并刪除棧中的數(shù)據(jù)。

4、代碼說明

基本的變量

Private _Num(80) As?Integer

Private _V(9) As?Integer

Private _S As System.Text.StringBuilder

Private _HasString As?Boolean

_Num數(shù)組表示數(shù)獨(dú)的狀態(tài);_V數(shù)組是輔助數(shù)組,緩存常用的二進(jìn)制數(shù)

_S是一個(gè)文本對(duì)象,保存數(shù)獨(dú)求解的過程;_HasString是個(gè)開關(guān)變量,表示是否記錄求解過程;這兩個(gè)變量是輔助變量,僅僅起到記錄的作用。

類的初始化

Public?Sub?New(Optional?ByVal HasString As?Boolean = True)

Dim I As?Integer

_V(0) = 1

For I = 1 To 8

_V(I) = _V(I - 1) * 2

Next

_V(9) = 511

For I = 0 To 80

_Num(I) = _V(9)

Next

_S = New System.Text.StringBuilder

_HasString = HasString

End?Sub

代碼的前半段生成V這個(gè)數(shù)組,_V(9)=511。后半段,初始化數(shù)獨(dú)數(shù)組。由于是空白數(shù)獨(dú)數(shù)組,故每個(gè)單元格的值都是_V(9)

在給定的單元格里移除某個(gè)數(shù)字的可能性代碼

Private?Function RemoveNum(ByVal Row As?Integer, ByVal Col As?Integer, ByVal Num2 As?Integer) As?Integer

Dim Index As?Integer = Row * 9 + Col

If _Num(Index) > 0 Then _Num(Index) = _Num(Index) And Num2

Return _Num(Index)

End?Function

3個(gè)參數(shù),Row表示行,Col表示列(都是下標(biāo)從0開始),Num2表示要去除的數(shù)的反碼,以二進(jìn)制表示。

例如:在(1,1)這個(gè)單元格去除7這個(gè)可能性,則調(diào)用RemoveNum(0,0,1101111112)

返回值是該單元格的狀態(tài)值,如果返回0,表示該單元就成了無解單元格,要后面的代碼做適當(dāng)?shù)奶幚?/p>

在給定的單元格填某個(gè)數(shù)的代碼

Private?Function SetNumPri(ByVal Row As?Integer, ByVal Col As?Integer, ByVal Num As?Integer) As?Boolean

If (_V(Num) And _Num(Row * 9 + Col)) = 0 Then?Return?False

_Num(Row * 9 + Col) = -(Num + 1)

Num = _V(9) - _V(Num)

Dim I As?Integer, J As?Integer

For I = 0 To 8

If RemoveNum(I, Col, Num) = 0 Then?Return?False

If RemoveNum(Row, I, Num) = 0 Then?Return?False

Next

Dim R1 As?Integer = Int(Row / 3) * 3

Dim C1 As?Integer = Int(Col / 3) * 3

For I = R1 To R1 + 2

For J = C1 To C1 + 2

If RemoveNum(I, J, Num) = 0 Then?Return?False

Next

Next

Return?True

End?Function

3個(gè)參數(shù),Row表示行,Col表示列,Num表示要填充的數(shù)字(下標(biāo)從0開始),這個(gè)方法是供類內(nèi)部調(diào)用,從程序的角度來說,程序處理下標(biāo),從0開始比從1開始要來得簡單。

例如:在(1,1)中填入數(shù)字7,則調(diào)用SetNumPri(0,0,6)

代碼的第1行,先利用位運(yùn)算判斷當(dāng)前單元格能否填制定的數(shù)字,不能填返回False

代碼的第2行,設(shè)置當(dāng)前單元格為指定數(shù)字,之前說了,用負(fù)數(shù)表示已填好的數(shù)字

代碼的第3行,獲得當(dāng)前數(shù)字的反碼,為后面去除該單元格所在的行、列、宮的其他單元格的該數(shù)字做準(zhǔn)備

后面有兩個(gè)循環(huán),第一個(gè)循環(huán)去除行、列的其他單元格的該數(shù)字;第二個(gè)雙循環(huán)去除宮的其他單元格的該數(shù)字。在調(diào)用RomoveNum方法時(shí),若返回的是0,說明產(chǎn)生了無解單元格,那說明在這個(gè)單元格填該數(shù)字是不合理的,故返回False

當(dāng)全部的代碼都能順利完成了,說明這個(gè)單元格填該數(shù)字是合理的,返回True

該方法的另一個(gè)重載形式

Private?Function SetNumPri(ByVal Index As?Integer, ByVal Num2 As?Integer) As?Boolean

Dim Row As?Integer = Int(Index / 9)

Dim Col As?Integer = Index Mod 9

Dim I As?Integer

For I = 0 To 8

If _V(I) = Num2 Then?Exit For

Next

Return SetNumPri(Row, Col, I)

End?Function

這也是一個(gè)供內(nèi)部調(diào)用的方法,兩個(gè)參數(shù),Index是一維數(shù)組的下標(biāo);Num2是數(shù)字的二進(jìn)制的形式。整個(gè)方法就是參數(shù)的轉(zhuǎn)換,然后調(diào)用之前的方法

下面是兩個(gè)供外面調(diào)用的方法

Public?Function SetNum(ByVal Row As?Integer, ByVal Col As?Integer, ByVal Num As?Integer) As?Boolean

Return SetNumPri(Row - 1, Col - 1, Num - 1)

End?Function

Public?Function SetLine(ByVal Row As?Integer, ByVal?ParamArray Num() As?Integer) As?Boolean

If Num.Length = 0 Then?Return?True

Dim I As?Integer

For I = 0 To IIf(Num.Length - 1 > 8, 8, Num.Length - 1)

If Num(I) > 0 AndAlso SetNumPri(Row - 1, I, Num(I) - 1) = False?Then?Return?False

Next

Return?True

End?Function

第一個(gè)方法是公開給外部調(diào)用的填數(shù)的方法。對(duì)外來說,從直觀性上來說,下標(biāo)是從1開始比較合適,但是內(nèi)部的方法從0開始比較好。

如在(1,1)填7,調(diào)用SetNum(1,1,7),這個(gè)方法轉(zhuǎn)而調(diào)用SetNumPri(0,0,6)

這個(gè)方法一般用在初始化數(shù)獨(dú)時(shí)候調(diào)用

第二個(gè)方法也是公開給外部的方法,一次填寫一行數(shù)的方法,如果是空白單元格,則用0替代

如本文開始的數(shù)獨(dú),填寫第一行代碼就是SetLine(1,0,6,0,5,9,3,0,0,0)

幾個(gè)輔助方法

Private?Sub RestoreNum(ByVal L As?List(Of?Integer))

Dim I As?Integer

For I = 0 To 80

_Num(I) = L.Item(I)

Next

AppendString("Restore Matrix")

End?Sub

恢復(fù)L中的數(shù)據(jù)到數(shù)獨(dú)數(shù)組中,L是之前緩存的數(shù)據(jù)。AppendString這個(gè)方法是將數(shù)據(jù)記錄到文本對(duì)象

Private?Function Get1Count(ByVal Value As?Integer) As?Integer

Dim C As?Integer = 0

Do?While Value > 0

Value = Value And (Value - 1)

C += 1

Loop

Return C

End?Function

獲得一個(gè)數(shù)中1的個(gè)數(shù),也就是獲得一個(gè)空白單元格的可填數(shù)的數(shù)目

例如:(1,1)=0110000002,Get1Count(0110000002)=2,說明(1,1)這個(gè)單元格能填2個(gè)數(shù)

Private?Function GetIndexOfNum(ByVal Num As?Integer, ByVal Index As?Integer) As?Integer

Dim I As?Integer, K As?Integer = 0

For I = 0 To 8

If (_V(I) And Num) <> 0 Then

K += 1

If K = Index Then?Return I + 1

End?If

Next

Return -1

End?Function

獲得指定數(shù)Num(二進(jìn)制形式)的第Index個(gè)的可填數(shù)

還是以上面的為例,(1,1)=0110000002,

GetIndexOfNum(0110000002,1)=7,表示第1個(gè)可填數(shù)是7

GetIndexOfNum(0110000002,2)=8,表示第2個(gè)可填數(shù)是8

GetIndexOfNum(0110000002,3)=-1,表示沒有第3個(gè)可填數(shù)

輔助記錄函數(shù)

這些函數(shù)對(duì)求解算法沒啥太大的幫助,僅僅是將求解的過程記錄到文本中,以供日后研究參考

Private?Function ReturnNumString(ByVal Num As?Integer) As?String

If Num < 0 Then?Return?"#" & (-Num) & " "

Dim I As?Integer, S As?String = ""

For I = 0 To 8

If (_V(I) And Num) <> 0 Then S &= (I + 1)

Next

Return S.PadRight(10)

End?Function

返回一個(gè)數(shù)字的文本格式,如果是空白單元格,返回該單元格的所有可填數(shù);如果是已填單元格,返回#+數(shù)字的字符串。返回的字符串經(jīng)過對(duì)齊處理。

Private?Function ReturnMatrix() As?String

Dim I As?Integer, J As?Integer, S As?String = ""

For I = 0 To 8

For J = 0 To 8

S &= ReturnNumString(_Num(I * 9 + J))

Next

S &= vbNewLine

Next

Return S

End?Function

返回整個(gè)數(shù)獨(dú)的狀態(tài)文本

Private?Sub AppendString(ByVal Text As?String, Optional?ByVal AppendMatrix As?Boolean = True)

If _HasString = False?Then?Exit Sub

_S.AppendLine(Text)

_S.AppendLine()

If AppendMatrix = True?Then

_S.AppendLine(ReturnMatrix)

_S.AppendLine()

End?If

End?Sub

將文本添加到文本對(duì)象,并根據(jù)AppendMatrix參數(shù)來決定是否將整個(gè)數(shù)獨(dú)的狀態(tài)添加到文本對(duì)象

Private?Function IndexToXY(ByVal Index As?Integer) As?String

Return (Int(Index / 9) + 1) & "-" & (Index Mod 9 + 1) & " Num:" & -_Num(Index)

End?Function

返回指定Index的坐標(biāo)和已填的數(shù),用于在文本對(duì)象中

Public?Function CalculationString() As?String

Return _S.ToString

End?Function

對(duì)外公開的方法,返回文本對(duì)象,也就是之前記錄的求解過程,共日后研究參考

主求解函數(shù)——算法的核心

下面的3個(gè)函數(shù)是算法的核心

Private?Function FindMinCell() As?Integer

Dim I As?Integer, C As?Integer

Dim tP As?Integer = -1, tMin As?Integer = 20

I = 0

Do

If _Num(I) > 0 Then

C = Get1Count(_Num(I))

If C = 1 Then

If SetNumPri(I, _Num(I)) = False?Then?Return -2

AppendString("SetNum " & IndexToXY(I))

If I = tP Then

tP = -1

tMin = 20

End?If

I = -1

Else

If C < tMin Then

tP = I

tMin = C

End?If

End?If

End?If

I += 1

Loop?Until I > 80

Return tP

End?Function

該函數(shù)是獲得最少可能數(shù)的單元格(可填數(shù)大于2的空白單元格)

該函數(shù)返回值有3個(gè)可能性

返回值:-1,沒有找到這樣的單元格,函數(shù)從某個(gè)唯一數(shù)單元格開始填數(shù),依次填下去,并且把所有的空白單元格都填滿。這說明,求解結(jié)束。

返回值:-2,沒有找到這樣的單元格,函數(shù)從某個(gè)唯一數(shù)單元格開始填數(shù),依次填下去,產(chǎn)生了無解單元格。說明之前的求解過程有錯(cuò)誤或者說該數(shù)獨(dú)無解

返回值:0-80,找到這樣的單元格,并且當(dāng)前的數(shù)獨(dú)數(shù)組中不再存在唯一數(shù)單元格(函數(shù)直接會(huì)在唯一數(shù)單元格上填數(shù))

Public?Function Calculate() As?Integer()

Dim I As?Integer

Dim K As?Integer

Dim Q As?New?Stack(Of?List(Of?Integer))

Dim L As?List(Of?Integer)

_S = New System.Text.StringBuilder

AppendString("Init Matrix")

K = FindMinCell()

Do?While K <> -1

If K = -2 Then

If Q.Count = 0 Then

AppendString("Error!!!!!", False)

Return?Nothing

End?If

L = Q.Pop

K = L(82)

L.RemoveAt(82)

I = L(81) + 1

L.RemoveAt(81)

AppendString("Stack Pop " & Q.Count + 1, False)

RestoreNum(L)

K = FindNextK(Q, L, K, I)

Else

L = New?List(Of?Integer)

L.AddRange(_Num)

K = FindNextK(Q, L, K, 1)

End?If

Loop

AppendString("Calculating Complete!!!!")

Dim V(80) As?Integer

For I = 0 To 80

V(I) = -_Num(I)

Next

Return V

End?Function

對(duì)外公開的主求解函數(shù),返回最終結(jié)果的整形數(shù)組

首先解釋一下棧對(duì)象Q,由于棧Q每次壓棧的時(shí)候只能壓一個(gè)對(duì)象,而當(dāng)出現(xiàn)無唯一數(shù)單元格的情況的時(shí)候,需要將當(dāng)前的數(shù)據(jù)緩存起來。需要緩存的內(nèi)容有三個(gè)部分,分別是數(shù)獨(dú)數(shù)組、找到的最少可能數(shù)的單元格的下標(biāo)、最少可能數(shù)的單元格的選擇填的第幾個(gè)數(shù)。故用一個(gè)List(of Integer)對(duì)象將之前的三個(gè)內(nèi)容緩存起來。L(0)—L(80)表示是數(shù)獨(dú)數(shù)組,L(81)是最少可能數(shù)的單元格的下標(biāo),L(82)是最少可能數(shù)的單元格的選擇填的第幾個(gè)數(shù)。

該函數(shù)的主要是判斷K的值,如上個(gè)函數(shù)所述,K的值主要有3種

K=-1,說明沒有空白單元格,數(shù)獨(dú)已經(jīng)完美的求解完成,直接返回結(jié)果

K=-2,說明有無解單元格,那么判斷棧Q中的數(shù)據(jù),如果棧Q中沒有數(shù)據(jù),說明該數(shù)獨(dú)無解;如果棧Q中有數(shù)據(jù),那么把數(shù)據(jù)提出來,把數(shù)獨(dú)的狀態(tài)恢復(fù)到之前的情況。并從上次緩存的最少可能數(shù)單元格中,提取下一個(gè)可填數(shù)去繼續(xù)進(jìn)行嘗試。

舉例說明,緩存了0,1。說明上次嘗試的是第1個(gè)單元格(下標(biāo)從0開始)的第1個(gè)可填數(shù)。由于出現(xiàn)了無解單元格,說明第1個(gè)可填數(shù)是不正確的,那么繼續(xù)嘗試第2個(gè)可填數(shù)。調(diào)用的方法:FindNextK(Q, L, K, I),之前I已經(jīng)加過1了。

K=0-80,得到最少可能數(shù)的單元格的下標(biāo)。從該單元格的第1個(gè)可填數(shù)開始嘗試。調(diào)用的方法:FindNextK(Q, L, K, 1)

嘗試可能數(shù)的函數(shù)是FindNextK,返回值也是分為3種,-1、-2、0-80。意義和上面一樣

Private?Function FindNextK(ByVal Q As?Stack(Of?List(Of?Integer)), ByVal L As?List(Of?Integer), ByVal K As?Integer, ByVal Index As?Integer) As?Integer

Dim J As?Integer = GetIndexOfNum(_Num(K), Index)

Do?While J <> -1

If SetNumPri(K, _V(J - 1)) = True?Then

AppendString("Stack Push " & Q.Count + 1, False)

AppendString("SetNum MayBe " & IndexToXY(K))

L.Add(Index)

L.Add(K)

Q.Push(L)

K = FindMinCell()

Exit Do

End?If

RestoreNum(L)

Index += 1

J = GetIndexOfNum(_Num(K), Index)

Loop

If J = -1 Then K = -2

Return K

End?Function

輔助函數(shù),獲得嘗試可能數(shù)的結(jié)果

首先,通過GetIndexOfNum獲得當(dāng)前可填數(shù)。如果返回值-1的話,說明當(dāng)前已經(jīng)沒有可填數(shù),出現(xiàn)無解單元格,直接返回值為-2

然后嘗試在當(dāng)前單元格填數(shù),調(diào)用SetNumPri(K, _V(J - 1)),返回True表示該數(shù)能填,那么把當(dāng)前的狀態(tài)緩存到棧Q中,并通過FindMinCell函數(shù)獲得下一個(gè)可能的K值,并返回;返回False表示該數(shù)不能填,恢復(fù)數(shù)據(jù)到數(shù)獨(dú)數(shù)組,繼續(xù)嘗試下一個(gè)數(shù)。

至此該算法類的代碼都說明完整了

在該算法中僅僅用了最基本的解法——摒除法。遇見唯一數(shù)單元格,就直接填數(shù),如果遇見無唯一數(shù)單元格,則緩存數(shù)據(jù),并對(duì)該單元格的所有可填數(shù)做嘗試,直到求解出該數(shù)獨(dú)為止。

會(huì)有人疑問,利用棧Q緩存數(shù)據(jù),會(huì)不會(huì)極大的占用系統(tǒng)資源,導(dǎo)致無法解題的情況。以目前的情況來看,我用該算法求解了“程序員們都是不被世人所理解的真正天才嗎?-請(qǐng)大家看這個(gè)數(shù)獨(dú)的解法”中的號(hào)稱最難的數(shù)獨(dú),并把求解的結(jié)果保存到文件后打開分析了一下,發(fā)現(xiàn)棧Q的緩存不超過20步,以20步為例,每步83*4字節(jié),則一共20*83*4=6640字節(jié)<7K字節(jié)。遠(yuǎn)小于系統(tǒng)的承受能力。因此,不必?fù)?dān)心系統(tǒng)的承受能力

如果,誰有好的數(shù)獨(dú)的算法,歡迎交流,不吝賜教。

讓我們實(shí)戰(zhàn)看看成果,用該算法求解本文開頭的數(shù)獨(dú),代碼如下:

Dim tS As New clsSudoku

tS.SetLine(1, 0, 6, 0, 5, 9, 3, 0, 0, 0)

tS.SetLine(2, 9, 0, 1, 0, 0, 0, 5, 0, 0)

tS.SetLine(3, 0, 3, 0, 4, 0, 0, 0, 9, 0)

tS.SetLine(4, 1, 0, 8, 0, 2, 0, 0, 0, 4)

tS.SetLine(5, 4, 0, 0, 3, 0, 9, 0, 0, 1)

tS.SetLine(6, 2, 0, 0, 0, 1, 0, 6, 0, 9)

tS.SetLine(7, 0, 8, 0, 0, 0, 6, 0, 2, 0)

tS.SetLine(8, 0, 0, 4, 0, 0, 0, 8, 0, 7)

tS.SetLine(9, 0, 0, 0, 7, 8, 5, 0, 1, 0)

tS.Calculate()

My.Computer.FileSystem.WriteAllText("1.txt", tS.CalculationString, False)

該數(shù)獨(dú)還是比較簡單的,一路唯一數(shù)單元格到底

結(jié)果如下:

Calculating Complete!!!!

#7??????? #6??????? #2??????? #5??????? #9??????? #3??????? #1??????? #4??????? #8

#9??????? #4??????? #1??????? #2??????? #7??????? #8??????? #5??????? #3??????? #6

#8??????? #3??????? #5??????? #4??????? #6??????? #1??????? #7??????? #9??????? #2

#1??????? #9??????? #8??????? #6??????? #2??????? #7??????? #3??????? #5??????? #4

#4??????? #7??????? #6??????? #3??????? #5??????? #9??????? #2??????? #8??????? #1

#2??????? #5??????? #3??????? #8??????? #1??????? #4??????? #6??????? #7??????? #9

#3??????? #8??????? #7??????? #1??????? #4??????? #6??????? #9??????? #2??????? #5

#5??????? #1??????? #4??????? #9??????? #3??????? #2??????? #8??????? #6??????? #7

#6??????? #2??????? #9??????? #7??????? #8??????? #5??????? #4??????? #1??????? #3

下面是該算法類的完整代碼

Public?Class?clsSudoku

Private _Num(80) As?Integer

Private _V(9) As?Integer

Private _S As System.Text.StringBuilder

Private _HasString As?Boolean

Public?Sub?New(Optional?ByVal HasString As?Boolean = True)

Dim I As?Integer

_V(0) = 1

For I = 1 To 8

_V(I) = _V(I - 1) * 2

Next

_V(9) = 511

For I = 0 To 80

_Num(I) = _V(9)

Next

_S = New System.Text.StringBuilder

_HasString = HasString

End?Sub

Private?Function Get1Count(ByVal Value As?Integer) As?Integer

Dim C As?Integer = 0

Do?While Value > 0

Value = Value And (Value - 1)

C += 1

Loop

Return C

End?Function

Private?Function RemoveNum(ByVal Row As?Integer, ByVal Col As?Integer, ByVal Num2 As?Integer) As?Integer

Dim Index As?Integer = Row * 9 + Col

If _Num(Index) > 0 Then _Num(Index) = _Num(Index) And Num2

Return _Num(Index)

End?Function

Public?Function SetNum(ByVal Row As?Integer, ByVal Col As?Integer, ByVal Num As?Integer) As?Boolean

Return SetNumPri(Row - 1, Col - 1, Num - 1)

End?Function

Public?Function SetLine(ByVal Row As?Integer, ByVal?ParamArray Num() As?Integer) As?Boolean

If Num.Length = 0 Then?Return?True

Dim I As?Integer

For I = 0 To IIf(Num.Length - 1 > 8, 8, Num.Length - 1)

If Num(I) > 0 AndAlso SetNumPri(Row - 1, I, Num(I) - 1) = False?Then?Return?False

Next

Return?True

End?Function

Private?Function SetNumPri(ByVal Row As?Integer, ByVal Col As?Integer, ByVal Num As?Integer) As?Boolean

If (_V(Num) And _Num(Row * 9 + Col)) = 0 Then?Return?False

_Num(Row * 9 + Col) = -(Num + 1)

Num = _V(9) - _V(Num)

Dim I As?Integer, J As?Integer

For I = 0 To 8

If RemoveNum(I, Col, Num) = 0 Then?Return?False

If RemoveNum(Row, I, Num) = 0 Then?Return?False

Next

Dim R1 As?Integer = Int(Row / 3) * 3

Dim C1 As?Integer = Int(Col / 3) * 3

For I = R1 To R1 + 2

For J = C1 To C1 + 2

If RemoveNum(I, J, Num) = 0 Then?Return?False

Next

Next

Return?True

End?Function

Private?Function SetNumPri(ByVal Index As?Integer, ByVal Num2 As?Integer) As?Boolean

Dim Row As?Integer = Int(Index / 9)

Dim Col As?Integer = Index Mod 9

Dim I As?Integer

For I = 0 To 8

If _V(I) = Num2 Then?Exit For

Next

Return SetNumPri(Row, Col, I)

End?Function

Private?Function FindMinCell() As?Integer

Dim I As?Integer, C As?Integer

Dim tP As?Integer = -1, tMin As?Integer = 20

I = 0

Do

If _Num(I) > 0 Then

C = Get1Count(_Num(I))

If C = 1 Then

If SetNumPri(I, _Num(I)) = False?Then?Return -2

AppendString("SetNum " & IndexToXY(I))

If I = tP Then

tP = -1

tMin = 20

End?If

I = -1

Else

If C < tMin Then

tP = I

tMin = C

End?If

End?If

End?If

I += 1

Loop?Until I > 80

Return tP

End?Function

Public?Function Calculate() As?Integer()

Dim I As?Integer

Dim K As?Integer

Dim Q As?New?Stack(Of?List(Of?Integer))

Dim L As?List(Of?Integer)

_S = New System.Text.StringBuilder

AppendString("Init Matrix")

K = FindMinCell()

Do?While K <> -1

If K = -2 Then

If Q.Count = 0 Then

AppendString("Error!!!!!", False)

Return?Nothing

End?If

L = Q.Pop

K = L(82)

L.RemoveAt(82)

I = L(81) + 1

L.RemoveAt(81)

AppendString("Stack Pop " & Q.Count + 1, False)

RestoreNum(L)

K = FindNextK(Q, L, K, I)

Else

L = New?List(Of?Integer)

L.AddRange(_Num)

K = FindNextK(Q, L, K, 1)

End?If

Loop

AppendString("Calculating Complete!!!!")

Dim V(80) As?Integer

For I = 0 To 80

V(I) = -_Num(I)

Next

Return V

End?Function

Private?Sub RestoreNum(ByVal L As?List(Of?Integer))

Dim I As?Integer

For I = 0 To 80

_Num(I) = L.Item(I)

Next

AppendString("Restore Matrix")

End?Sub

Private?Function GetIndexOfNum(ByVal Num As?Integer, ByVal Index As?Integer) As?Integer

Dim I As?Integer, K As?Integer = 0

For I = 0 To 8

If (_V(I) And Num) <> 0 Then

K += 1

If K = Index Then?Return I + 1

End?If

Next

Return -1

End?Function

Private?Function FindNextK(ByVal Q As?Stack(Of?List(Of?Integer)), ByVal L As?List(Of?Integer), ByVal K As?Integer, ByVal Index As?Integer) As?Integer

Dim J As?Integer = GetIndexOfNum(_Num(K), Index)

Do?While J <> -1

If SetNumPri(K, _V(J - 1)) = True?Then

AppendString("Stack Push " & Q.Count + 1, False)

AppendString("SetNum MayBe " & IndexToXY(K))

L.Add(Index)

L.Add(K)

Q.Push(L)

K = FindMinCell()

Exit Do

End?If

RestoreNum(L)

Index += 1

J = GetIndexOfNum(_Num(K), Index)

Loop

If J = -1 Then K = -2

Return K

End?Function

Private?Function ReturnNumString(ByVal Num As?Integer) As?String

If Num < 0 Then?Return?"#" & (-Num) & " "

Dim I As?Integer, S As?String = ""

For I = 0 To 8

If (_V(I) And Num) <> 0 Then S &= (I + 1)

Next

Return S.PadRight(10)

End?Function

Private?Function ReturnMatrix() As?String

Dim I As?Integer, J As?Integer, S As?String = ""

For I = 0 To 8

For J = 0 To 8

S &= ReturnNumString(_Num(I * 9 + J))

Next

S &= vbNewLine

Next

Return S

End?Function

Private?Sub AppendString(ByVal Text As?String, Optional?ByVal AppendMatrix As?Boolean = True)

If _HasString = False?Then?Exit Sub

_S.AppendLine(Text)

_S.AppendLine()

If AppendMatrix = True?Then

_S.AppendLine(ReturnMatrix)

_S.AppendLine()

End?If

End?Sub

Private?Function IndexToXY(ByVal Index As?Integer) As?String

Return (Int(Index / 9) + 1) & "-" & (Index Mod 9 + 1) & " Num:" & -_Num(Index)

End?Function

Public?Function CalculationString() As?String

Return _S.ToString

End?Function

End?Class

總結(jié)

以上是生活随笔為你收集整理的数独问题流程图_算法实践——数独的基本解法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。