如何用python计算数独_用python解决数独
看到了,季以安:用Python解數(shù)獨(dú)[0],寫得很詳細(xì),我沒(méi)有全部看完,借用他的思路,我也來(lái)試試,回頭再去看他怎么弄,特別是遞歸的實(shí)現(xiàn)方式。
先上思路:
'''1.解9*9數(shù)獨(dú)的思路:(1)一個(gè)單元格的推理:根據(jù)行、列、小矩陣得出該單元格有可能填入的數(shù)值的三個(gè)列表;求并集,縮小范圍,簡(jiǎn)單的題目好多單元格都能得到唯一值。(2)重復(fù)上面這個(gè)過(guò)程2.實(shí)現(xiàn):(1)復(fù)制生成三個(gè)新數(shù)獨(dú)(按行、按列、按小矩陣)(2)把上述三個(gè)數(shù)獨(dú)每個(gè)空單元格內(nèi)填滿’有可能‘的數(shù)值(用列表填入)(3)遍歷每個(gè)空單元格,去找這個(gè)單元格分別是三個(gè)新數(shù)獨(dú)的哪個(gè)位置(是三個(gè)列表-求交集)(4)更新原數(shù)獨(dú),遞歸更新后的數(shù)獨(dú)(5)我也知道這個(gè)程序思路,只能做比較簡(jiǎn)單的數(shù)獨(dú),就是練練,寫得好會(huì)考慮怎么解高等級(jí)數(shù)獨(dú)。'''圖一:數(shù)獨(dú)就是一個(gè)二維列表,用過(guò)xlwings操作過(guò)excel的很好理解 以下考慮算法的時(shí)候就看著這張圖就行了
思路確定后,來(lái)定一下程序大致的框架:
soduku1 #待求解的數(shù)獨(dú)
soduku2 #復(fù)制soduku1,然后遍歷空單元格,按行來(lái)確定該單元格可能填入哪些值(一個(gè)列表),
全算出來(lái)后就是一個(gè)新數(shù)獨(dú)(思路不斷在二維列表和圖一之間切換)
soduku3 #和上面一樣,按列,基本相同,
soduku4 #按小矩陣,實(shí)現(xiàn)需要計(jì)算思考
A:def 復(fù)制待解數(shù)獨(dú)(soduku1):
# 1.因?yàn)閟oduku1是有用的,不要去修改,所以復(fù)制
# 2.soduku2,soduku3的結(jié)構(gòu)是一樣的,所有寫在一起
# 3.按行生成soduku2很簡(jiǎn)單,就是單純的復(fù)制soduku1,只不過(guò)因?yàn)槭嵌S列表,
要用copy.deepcopy(soduku1)才有用,不能soduku2=soduku1[:],更不能
soduku2=soduku1這么粗暴
# 4.按列生成soduku3,復(fù)雜一點(diǎn),這里是框架,先不說(shuō)
return newsodu
B:def 按待解數(shù)獨(dú)生成小矩陣數(shù)獨(dú)(soduku1):
#這個(gè)需要一點(diǎn)觀察,還需要一點(diǎn)小學(xué)水平算法
return newsodu
C:def 把新生成的三個(gè)數(shù)組空格全填滿(sodu):
#看圖方式:cells(0,0)(看圖一,這個(gè)表示第0行,第0列,即第一個(gè)
單元格,后面都采取這樣的方式看圖)
# soduku2的cells(0,0)會(huì)被填入[2,4,5,6],表示按行第一個(gè)單元格可能是2,4,5,6
# 全填滿無(wú)非是循環(huán)
# 分別傳入?yún)?shù)soduku2,soduku3,soduku4,就會(huì)得到三個(gè)填滿的新數(shù)獨(dú)
return 填滿的三個(gè)新數(shù)獨(dú)
def 重復(fù)算():
調(diào)用C:分別傳入soduku2,soduku3,soduku4
爛尾了,本來(lái)想一步步寫思路,沒(méi)耐心寫了,好像也不太有人看,看別人代碼有時(shí)是很難受的,直接上代碼,基本思路注釋里還是有的。
'''1.解9*9數(shù)獨(dú)的思路:(1)一個(gè)單元格的推理:根據(jù)行、列、小矩陣得出該單元格有可能填入的數(shù)值的三個(gè)列表;求并集,縮小范圍,簡(jiǎn)單的題目好多單元格都能得到唯一值。(2)重復(fù)上面這個(gè)過(guò)程2.實(shí)現(xiàn):(1)復(fù)制生成三個(gè)新數(shù)獨(dú)(按行、按列、按小矩陣)(2)把上述三個(gè)數(shù)獨(dú)每個(gè)空單元格內(nèi)填滿’有可能‘的數(shù)值(用列表填入)(3)遍歷每個(gè)空單元格,去找這個(gè)單元格分別是三個(gè)新數(shù)獨(dú)的哪個(gè)位置(是三個(gè)列表-求交集),簡(jiǎn)單的題目這一步就能得到一個(gè)唯一值(4)更新原數(shù)獨(dú),遞歸更新后的數(shù)獨(dú)(5)我也知道這個(gè)程序思路,只能做比較簡(jiǎn)單的數(shù)獨(dú),就是練練,寫得好會(huì)考慮怎么解高等級(jí)數(shù)獨(dú)。'''
import copy
import numpy as np
import xlwings as xw
soduku = [[] for i in range(9)]
soduku[0] = ['',8,9,1,'',3,'','','']
soduku[1] = ['',2,7,4,'','',8,'','']
soduku[2] = ['','','',5,2,'','','','']
soduku[3] = ['',7,6,9,'','',5,'',8]
soduku[4] = [8,'',3,6,'',1,2,'','']
soduku[5] = ['',4,5,3,8,7,1,6,'']
soduku[6] = [7,'',8,2,1,4,3,9,5]
soduku[7] = [9,3,1,7,6,5,4,'',2]
soduku[8] = [4,'',2,'','',9,'','',6]
#復(fù)制待解數(shù)組
#按行直接復(fù)制
#按列用要用zip合并轉(zhuǎn)換
def tran_sodu(sodu,col=None):
if col==None:
return copy.deepcopy(soduku)
else:
new_sodu=list(zip(*copy.deepcopy(soduku)))
new_sodu=[list(i) for i in new_sodu]
return new_sodu
#填滿空單元格,并為了格式統(tǒng)一好處理,把已知的值,由數(shù)值轉(zhuǎn)為列表
def input_none(sodu):
for row in sodu:
row_none = [i for i in range(1,10) if i not in row]
for x in range(len(row)):
if row[x]=="":
row[x]=row_none
else:
row[x]=[row[x]]
return sodu
'''1.按小矩陣復(fù)制待解數(shù)組,9個(gè)小矩陣組成新數(shù)獨(dú)2.用了numpy知識(shí),很好用,但在單個(gè)元素的數(shù)據(jù)類型上有很多坑3.思路:把待解數(shù)組reshape成27組,每組3個(gè)元素的矩陣A,那么第一個(gè)小矩陣就取矩陣A的第1、4、7個(gè)元素,依次類推'''
def jvzheng(soduku):
arr = np.array(soduku)
# arr=[list(i) for i in arr.reshape(27,3)]
arr = arr.reshape(27,3).tolist()
for a in arr:
for b in range(3):
if a[b]!="":
a[b]=int(a[b])
newSodu=[[] for i in range(9)]
j=0
for i in range(9):
newSodu[i]=arr[j]+arr[j+3]+arr[j+6]
j=j+1
if j>=3 and j%3==0:
j=j+6
return newSodu
'''1、開始解數(shù)獨(dú)后,遍歷每個(gè)空單元格,去找該單元格對(duì)應(yīng)到三個(gè)新數(shù)獨(dú)的單元格時(shí):(1)如果待解數(shù)獨(dú)的單元格是soduku-cells(j,k),那么對(duì)應(yīng)過(guò)去,按行的當(dāng)然也是:按行-cells(j,k),按列的也很簡(jiǎn)單,相反,是:按列-cells(k,j)(2)按小矩陣的難解一點(diǎn),就是下面的函數(shù),和jvzheng()的主要代碼一樣,就是再轉(zhuǎn)置一次,就轉(zhuǎn)回去了,效果是:按小矩陣-cells(j,k)(3)按行-cells(j,k),按列-cells(k,j),按小矩陣-cells(j,k)這三個(gè)列表就能求交集了,最希望得到的是一個(gè)唯一值'''
def jvzheng2(soduku):
arr = np.array(soduku)
# arr=[list(i) for i in arr.reshape(27,3)]
arr=arr.reshape(27,3).tolist()
newSodu=[[] for i in range(9)]
j=0
for i in range(9):
newSodu[i]=arr[j]+arr[j+3]+arr[j+6]
j=j+1
if j>=3 and j%3==0:
j=j+6
return newSodu
import sys
sys.setrecursionlimit(50000) #這里設(shè)置大一些
'''1.遞歸函數(shù),重復(fù)2.基本原理在jvzheng2()上面寫了'''
def fin(s):
global cc,rr,jz,jz2
rr=input_none(tran_sodu(soduku)) # 按行填滿各空格有可能的缺失值
cc=input_none(tran_sodu(soduku,1)) #按列
jz=input_none(jvzheng(soduku)) #按小矩陣
jz2=jvzheng2(jz)
'''規(guī)則一:行、列、小矩陣求交集遍歷待解數(shù)獨(dú)的每個(gè)空單元格,找到該單元格在rr,cc,jz2中的位置,并求交集當(dāng)這個(gè)交集只有一個(gè)值時(shí),就填入待解數(shù)獨(dú)中'''
for j in range(9):
for k in range(9):
if s[j][k]=="":
'''下面的判斷是numpy數(shù)據(jù)類型的一個(gè)坑,numpy會(huì)將[9]這樣的單個(gè)數(shù)值的列表直接處理成數(shù)字9,這在算到最后一步時(shí),代碼就會(huì)跳錯(cuò)識(shí)了,因此加了判斷,核心代碼是else下面的那名就夠了。'''
if isinstance(jz2[j][k],int):
target=set(rr[j][k])&set(cc[k][j])
else:
target=set(rr[j][k])&set(cc[k][j])&set(jz2[j][k])
if len(target)==1:
s[j][k]=list(target)[0]
'''規(guī)則二:如[8,4,[3,7,9],6,[1,5],[1,3,5],[1,5,9],[1,3,9],2]第三個(gè)[3,7,9],雖然有三個(gè)可能值,但觀察一行,‘7’,在其他位置都沒(méi)有出現(xiàn)過(guò),因此第三位置肯定是‘7’'''
'''規(guī)則三……規(guī)則越多,能解的難題越多,達(dá)到通用,就不會(huì)出現(xiàn)“遞歸深度不夠”,”棧溢出“等問(wèn)題,數(shù)獨(dú)知識(shí)有限,寫不下去了,只能解簡(jiǎn)單數(shù)獨(dú)。'''
#每遞歸一次,待解數(shù)獨(dú)就會(huì)更新
#把待解數(shù)獨(dú)攤開,如果沒(méi)有一個(gè)空值,那說(shuō)明解完了
if "" not in np.array(s).reshape(1,81)[0].tolist():
# if "" not in s[3]:
return s
else:
fin(s)
return s
def main():
fin(soduku)
print(soduku)
xw.Range("K1").value = soduku
if __name__ == '__main__':
main()
解出來(lái)了,因?yàn)槔訉?shí)在太簡(jiǎn)單了,稍微復(fù)雜一點(diǎn)就出現(xiàn)“棧溢出”了。
因?yàn)橐?guī)則只有一條,無(wú)論怎么遞歸都得不到答案。
總結(jié)
以上是生活随笔為你收集整理的如何用python计算数独_用python解决数独的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python实现组合问题_python3
- 下一篇: python向dict里添加_Pytho