看到了,季以安:用Python解数独[0],写得很详细,我没有全部看完,借用他的思路,我也来试试,回头再去看他怎么弄,特别是递归的实现方式。
先上思路:
'''1.解9*9数独的思路:(1)一个单元格的推理:根据行、列、小矩阵得出该单元格有可能填入的数值的三个列表;求并集,缩小范围,简单的题目好多单元格都能得到唯一值。(2)重复上面这个过程2.实现:(1)复制生成三个新数独(按行、按列、按小矩阵)(2)把上述三个数独每个空单元格内填满’有可能‘的数值(用列表填入)(3)遍历每个空单元格,去找这个单元格分别是三个新数独的哪个位置(是三个列表-求交集)(4)更新原数独,递归更新后的数独(5)我也知道这个程序思路,只能做比较简单的数独,就是练练,写得好会考虑怎么解高等级数独。'''图一:数独就是一个二维列表,用过xlwings操作过excel的很好理解 以下考虑算法的时候就看着这张图就行了
思路确定后,来定一下程序大致的框架:
soduku1 #待求解的数独
soduku2 #复制soduku1,然后遍历空单元格,按行来确定该单元格可能填入哪些值(一个列表),
全算出来后就是一个新数独(思路不断在二维列表和图一之间切换)
soduku3 #和上面一样,按列,基本相同,
soduku4 #按小矩阵,实现需要计算思考
A:def 复制待解数独(soduku1):
# 1.因为soduku1是有用的,不要去修改,所以复制
# 2.soduku2,soduku3的结构是一样的,所有写在一起
# 3.按行生成soduku2很简单,就是单纯的复制soduku1,只不过因为是二维列表,
要用copy.deepcopy(soduku1)才有用,不能soduku2=soduku1[:],更不能
soduku2=soduku1这么粗暴
# 4.按列生成soduku3,复杂一点,这里是框架,先不说
return newsodu
B:def 按待解数独生成小矩阵数独(soduku1):
#这个需要一点观察,还需要一点小学水平算法
return newsodu
C:def 把新生成的三个数组空格全填满(sodu):
#看图方式:cells(0,0)(看图一,这个表示第0行,第0列,即第一个
单元格,后面都采取这样的方式看图)
# soduku2的cells(0,0)会被填入[2,4,5,6],表示按行第一个单元格可能是2,4,5,6
# 全填满无非是循环
# 分别传入参数soduku2,soduku3,soduku4,就会得到三个填满的新数独
return 填满的三个新数独
def 重复算():
调用C:分别传入soduku2,soduku3,soduku4
烂尾了,本来想一步步写思路,没耐心写了,好像也不太有人看,看别人代码有时是很难受的,直接上代码,基本思路注释里还是有的。
'''1.解9*9数独的思路:(1)一个单元格的推理:根据行、列、小矩阵得出该单元格有可能填入的数值的三个列表;求并集,缩小范围,简单的题目好多单元格都能得到唯一值。(2)重复上面这个过程2.实现:(1)复制生成三个新数独(按行、按列、按小矩阵)(2)把上述三个数独每个空单元格内填满’有可能‘的数值(用列表填入)(3)遍历每个空单元格,去找这个单元格分别是三个新数独的哪个位置(是三个列表-求交集),简单的题目这一步就能得到一个唯一值(4)更新原数独,递归更新后的数独(5)我也知道这个程序思路,只能做比较简单的数独,就是练练,写得好会考虑怎么解高等级数独。'''
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]
#复制待解数组
#按行直接复制
#按列用要用zip合并转换
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
#填满空单元格,并为了格式统一好处理,把已知的值,由数值转为列表
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.按小矩阵复制待解数组,9个小矩阵组成新数独2.用了numpy知识,很好用,但在单个元素的数据类型上有很多坑3.思路:把待解数组reshape成27组,每组3个元素的矩阵A,那么第一个小矩阵就取矩阵A的第1、4、7个元素,依次类推'''
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、开始解数独后,遍历每个空单元格,去找该单元格对应到三个新数独的单元格时:(1)如果待解数独的单元格是soduku-cells(j,k),那么对应过去,按行的当然也是:按行-cells(j,k),按列的也很简单,相反,是:按列-cells(k,j)(2)按小矩阵的难解一点,就是下面的函数,和jvzheng()的主要代码一样,就是再转置一次,就转回去了,效果是:按小矩阵-cells(j,k)(3)按行-cells(j,k),按列-cells(k,j),按小矩阵-cells(j,k)这三个列表就能求交集了,最希望得到的是一个唯一值'''
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) #这里设置大一些
'''1.递归函数,重复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)
'''规则一:行、列、小矩阵求交集遍历待解数独的每个空单元格,找到该单元格在rr,cc,jz2中的位置,并求交集当这个交集只有一个值时,就填入待解数独中'''
for j in range(9):
for k in range(9):
if s[j][k]=="":
'''下面的判断是numpy数据类型的一个坑,numpy会将[9]这样的单个数值的列表直接处理成数字9,这在算到最后一步时,代码就会跳错识了,因此加了判断,核心代码是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]
'''规则二:如[8,4,[3,7,9],6,[1,5],[1,3,5],[1,5,9],[1,3,9],2]第三个[3,7,9],虽然有三个可能值,但观察一行,‘7’,在其他位置都没有出现过,因此第三位置肯定是‘7’'''
'''规则三……规则越多,能解的难题越多,达到通用,就不会出现“递归深度不够”,”栈溢出“等问题,数独知识有限,写不下去了,只能解简单数独。'''
#每递归一次,待解数独就会更新
#把待解数独摊开,如果没有一个空值,那说明解完了
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()
解出来了,因为例子实在太简单了,稍微复杂一点就出现“栈溢出”了。
因为规则只有一条,无论怎么递归都得不到答案。