第二个求解案例,我想到了数独。曾经有一段时间对数独求解非常感兴趣,在学习C++语言时也编写了数独求解程序。本次Python学习,要求比之前高一点,把出题程序也一起编写了吧。

1、数独简介

数独 (英语:Sudoku)是一种逻辑性的数字填充游戏,玩家须以数字填进每一格,而每行、每列和每个宫(即3x3的大格)有1至9所有数字。游戏设计者会提供一部分的数字,使谜题只有一个答案。答案满足同一个数字不可能在同一行、列或宫中出现多于一次。

2、算法

2.1 数独解题算法

最简单直接的算法就是:从第一个空格开始,尝试填入数字;之后再尝试下一个空格;如果失败,就回退,成功就继续,直至所有空格填满。具体如下:

(1)使用9X9二维矩阵表示数独局面,0表示空格,非零表示已经填写数字。从第一个元素开始,转步骤(2)

(2)搜索下一个空格。

(2-1)搜索到空格,转步骤(3);

(2-2)搜索不到空格,说明所有空格填写完毕,搜索到一个成功解题方案,结束程序。

(3)按照数独规则搜索当前空格可填写的数字集合。

(3-1)搜索不到可填写数字。当前方案不可行,将当前格恢复为0,回退一步,重新填写当前格数字。

(3-2)搜索到可填写的数字集合。从集合中取出一个数字,填入当前格。转步骤(2)。

第(2)、(3)步可以用递归程序实现,比较好实现回退和继续搜索功能。

这个算法简单粗暴,求解一般的数独题目都没有问题。但是在后续出题程序中,用这个算法测试出题结果是否可行时,经过大量随机数据测试,发现以下问题:

(1)效率低。有时甚至程序长时间运行不出结果,只能强行中止。

(2)一次只能搜索一个解法,不能测试题目的求解结果是否唯一。

分析问题(1)的原因主要在于初始搜索的可选数字较多,造成程序需要搜索方案数目剧增。因此,可以计算当前局面中所有空格的可选数字数目,从最小可选数字数目空格开始尝试填写数字。这样可以大大减少需要搜索方案数目。同时,为了避免频繁计算所有空格的可选数字数目,将搜索到的空格的可选数字集合存起来,在设置空格新数字、回退时更新相关空格的可选数字集合。

问题(2)的原因在于搜索到一个方案就结束,可以设置搜索的可行方案数目上限(上限设置为2就可以搜索求解方案是否唯一),达不到上限就回退继续搜索。

修改后的算法如下:

(1)使用9X9二维矩阵表示数独局面,0表示空格,非零表示已经填写数字。设置求解方案数目上限为NUP,搜索当前局面所有空格的可选数字集合,存入二维数组lsava中。转步骤(2)

(2)根据lsava,搜索可选数字数目最小的空格。

(2-1)搜索到空格,转步骤(3);

(2-2)搜索不到空格,说明所有空格填写完毕。搜索到一个成功解题方案,存储当前成功方案至成功方案列表。如果成功方案列表总数等于NUP,结束程序;否则将当前格恢复为0,恢复相关空格可选数字集合至lsava,回退一步,重新填写当前格数字。

(3)从lsava中取得当前空格可填写的数字集合。

(3-1)数字集合为空。当前方案不可行,将当前格恢复为0,恢复相关空格可选数字集合至lsava。回退一步,重新填写当前格数字。

(3-2)搜索到可填写的数字集合。从集合中取出一个数字,填入当前格。更新相关空格可选数字集合至lsava,同时暂存更新之前的集合,用于后续可能的回退操作。转步骤(2)。

2.2 数独出题算法

出题方法一:

最先想到的出题方法是仿照解题过程,从空盘开始逐渐填写数字:

  1. 从全部为空的局面开始;
  2. 在所余空格中随机挑选一个;
  3. 在选定空格的可用数字集合中随机挑选一个数字填入空格;
  4. 利用数独解题算法判断当前局面是否有解,如果否,则回退一步,恢复当前空格,转到步骤(3);如果有解,进一步判断解是否唯一,如果唯一,则找到结果,返回,否则转步骤(2)。

该算法的实现详见3.2。运行后发现,尽管采用了2.1中的优化后求解策略,但是由于随机设置数字过程很容易出现很多不可解局面,耗费大量搜索时间,有时甚至无法在10分钟内求解出结果。

为了解决这个问题,查阅网络文献,有了出题方法二:

主要思路是从一个完成的数独结果开始,随机挖除一定数目的空格,然后再判断当前局面解是否唯一。这样就避免的无解情况,搜索效率大幅提升。

挖除的空格数目越多,题目求解难度越高,同时多解概率也越高。可以设置空格数目的上限和下限值,调节所出题目的难度。

方法2需要大量数独结果,本算法使用一个固定数独结果,通过随机交换1-9数字位置产生所需大量数独结果,理论上可以产生9!= 362880个结果。再考虑题目上随机位置挖洞,可以产生的数独题目足够用了。具体如下:

  1. 通过随机交换1-9数字位置,产生一个数独结果;
  2. 设置空格数目的上限和下限值;
  3. 随机产生上限空格个位置,挖空位置,并保存挖空前的数值。
  4. 从空格上限开始,向下限方向搜索;
  5. 填入一个挖空前的数字。判断当前局面结果是否唯一,如果唯一,则结束;否则继续步骤(5)。
  6. 如果搜索不到唯一解,转置步骤(1)尝试下一个数独结果。

3、算法实现

3.1 数独解题

主程序Shudu1.py,输入输出及程序调用。

''''

    数独搜索

    读取用户输入文件,搜索结果。如果得不到结果,则提示失败。

'''

import sys

import CShudu1 as csd

#  主程序

if len(sys.argv)<2 :

    print("使用方法: ")

    print(sys.argv[0],"FileName")

    print("FileName为输入文件文件名称前缀,真正输入文件为 Filename.dat, 输出文件名称为FileName.res。")

    exit(1)

fin=sys.argv[1]+".dat"

fout=sys.argv[1]+".res"

print("输入文件:",fin,"输出文件",fout)

#初始化

cs=csd.CShudu()

nret=cs.ReadData(fin)

if nret!=0:

    print("读取输入文件错误!")

    exit(2)

print("原始数据:")

cs.PrintData()

# 开始查找

cs.CalLsava()

nr=cs.getNumRes(1)

if len(cs.lsres)>=1 :

    print("成功找到{}个结果!".format(len(cs.lsres)))

    cs.loadres(0)

    cs.PrintData()

    nret=cs.WriteData(fout)

    if nret!=0:

        print("写输出文件错误!")

        exit(3)

else :

    print("本题无解!")

   

子程序CShudu1.py,以类的形式封装数独解题算法。

import random

'''

封装为类的数独搜索

'''

class CShudu :

    def __init__(self): # 初始化

        # 数据

        self.clear()

    def clear(self): # 清除数据

        #当前局面数据

        self.data=[[0 for i in range(9)] for j in range(9)]

        #当前局面每个格子的可用数据列表

        self.lsava=[[set() for i in range(9)] for j in range(9)]

        #找到的可行解列表

        self.lsres=[]

    # 从保持data数据至lsres

    def saveres(self):

        self.lsres.append([self.data[i].copy() for i in range(9)])

        #print("in saveres",self.lsres)

    # 从lsres装载数据至data

    def loadres(self,k):

        #print("in loadres",self.lsres)

        self.data=[self.lsres[k][i].copy() for i in range(9)]

    # 从文件读矩阵数据

    def ReadData(self,fname) :

        try:

            f=open(fname,"r")

        except FileNotFoundError:

        #打开文件错误

            return 1

        for i in range(9):

            str=f.readline()

            lstr=str.split()

            #文件读取错误

            if len(lstr) < 9 : return 2

            self.data[i]=[int(lstr[j]) for j in range(9)]

        f.close()

        return 0

    # 写矩阵数据至屏幕

    def PrintData(self) :

        print("-"*40)

        for i in range(9):

            print(self.data[i])

    # 写矩阵数据至文件

    def WriteData(self,fname) :

        f=open(fname,"w")

        #打开文件错误

        if not f : return 1

        for i in range(9):

            s1=''

            for j in range(9):

                s1=s1+str(self.data[i][j])+" "

            s1=s1+'\n'

            f.write(s1)

        f.close()

        return 0

    # 搜索位置(i,j)可用数字集合Sava

    def FindSava(self,i,j):

        Sava=set()

        Snava=set()

        for k in range(9):

            if self.data[i][k]>0 : Snava.add(self.data[i][k])   #行

            if self.data[k][j]>0 : Snava.add(self.data[k][j])   #列

        m=int(i/3)*3

        n=int(j/3)*3

        for k in range(m,m+3):

            for l in range(n,n+3):

                if self.data[k][l]>0 : Snava.add(self.data[k][l])

        Sava= {i for i in range(1,10)}-Snava

        return Sava

    # 查找目前局面的可行方案数,搜索上限为nup:

    # 返回值:0,找不到可行方案;1,成功

    # self.lsres,列表,存放找到的数独结果

    def getNumRes(self,nup):

        nret=0

        nmin=10

        for i in range(9):

            for j in range(9):

                if self.data[i][j]==0 :

                    if len(self.lsava[i][j])<nmin:

                        nmin=len(self.lsava[i][j])

                        ni=i

                        nj=j

        if nmin>9:

            self.saveres()

            nret=1

            #print("In getNumRes,len(self.lsres),nret=",len(self.lsres),nret)

            #self.PrintData()

            return nret #找到一个方案

        # 找到ni,nj点

        #self.PrintData()

        Sava=self.lsava[ni][nj].copy()

        #print("In getNumRes1 ni,nj,Sava=",ni,nj,Sava)

        while len(Sava) > 0 :

           x=Sava.pop()

           self.data[ni][nj]=x

           ls1=self.UpdateLsava(ni,nj)

           #print("In getNumRes2, ni=",ni,"nj=",nj,"X=",x)

           nret=self.getNumRes(nup)

           #恢复

           self.data[ni][nj]=0

           self.RestoreLsava(ls1)

           if len(self.lsres)>=nup :

              break    # 搜索完成

        #print("In getNumRes3,len(self.lsres),nret=",len(self.lsres),nret)

        return nret

    #计算全部空格的可行数字列表

    def CalLsava(self):

        for i in range(9):

            for j in range(9):

                if self.data[i][j]==0 :

                    self.lsava[i][j]=self.FindSava(i,j)

    #更新指定格相关的数字列表,返回原始列表

    def UpdateLsava(self,i,j):

        ls1=[]

        for k in range(9):

            if self.data[i][k]==0 :

                ls1.append((i,k,self.lsava[i][k].copy()))

                self.lsava[i][k]=self.FindSava(i,k)   #行

            if self.data[k][j]==0 :

                ls1.append((k,j,self.lsava[k][j].copy()))

                self.lsava[k][j]=self.FindSava(k,j)   #列

        m=int(i/3)*3

        n=int(j/3)*3

        for k in range(m,m+3):

            if k==i: continue

            for l in range(n,n+3):

                if l==j: continue

                if self.data[k][l]==0 :

                    ls1.append((k,l,self.lsava[k][l].copy()))

                    self.lsava[k][l]=self.FindSava(k,l)

        return ls1

    #根据列表恢复Lsava

    def RestoreLsava(self,lsava):

        for i,j,s1 in lsava:

            self.lsava[i][j]=s1

    # 进行一次数独出题

    def set1(self):

        self.clear()

        Imax=60     #设置数字的格子上限

        lb1=random.sample(range(81),Imax)

        lb=[]

        for k in lb1:

            i=int(k/9)

            j=k-i*9

            lb.append((i,j))

        #print("lb=",lb)

        self.CalLsava()

        nret=self.TrySetNext(lb,0)

        return nret

    # 递归尝试填充第k步

    def TrySetNext(self,lb,k):

        nret=0

        i,j=lb[k]

        Sava=self.lsava[i][j].copy()

        print("In TrySetNext,k,Sava=",k,Sava)

        while len(Sava)>0:

            m=Sava.pop()              #尝试设置一个数字

            self.data[i][j]=m

            ls1=self.UpdateLsava(i,j)

            #print("In TrySetNext,i,j,m=",i,j,m)

            #self.PrintData()

            #print("lsava=",self.lsava)

            self.lsres=[]    #找到的方案数目

            if k>20:

                n=self.getNumRes(2)

            else:

                n=1

                self.lsres=[1,2]

            #print("In TrySetNext,len(self.lsres)=",len(self.lsres))

            if len(self.lsres)>=2:#大于等于2个结果

                #继续搜索下一个点

                if (k+1<len(lb)):

                    nret=self.TrySetNext(lb,k+1)

                    if nret: break

            elif len(self.lsres)==1:#只有一个结果

                 nret=1

                 break

            #没有成功结果

            #恢复,尝试下一个可用点

            self.data[i][j]=0

            self.RestoreLsava(ls1)

        #print("In TrySetNext,nret=",nret)

        return nret

运行结果:

E:\程序\test\python\shudu>Shudu1.py data\1

输入文件: data\1.dat 输出文件 data\1.res

原始数据:

----------------------------------------

[7, 0, 0, 0, 0, 2, 0, 0, 1]

[3, 0, 0, 0, 5, 0, 0, 7, 0]

[0, 8, 1, 4, 0, 0, 3, 0, 0]

[0, 0, 0, 0, 6, 0, 0, 0, 0]

[0, 7, 3, 0, 0, 9, 0, 0, 2]

[0, 0, 8, 1, 4, 0, 5, 0, 0]

[0, 0, 0, 0, 0, 0, 0, 0, 8]

[0, 1, 5, 0, 3, 0, 0, 6, 0]

[0, 6, 0, 7, 0, 0, 9, 0, 5]

成功找到1个结果!

----------------------------------------

[7, 4, 6, 3, 9, 2, 8, 5, 1]

[3, 9, 2, 8, 5, 1, 4, 7, 6]

[5, 8, 1, 4, 7, 6, 3, 2, 9]

[1, 5, 9, 2, 6, 3, 7, 8, 4]

[4, 7, 3, 5, 8, 9, 6, 1, 2]

[6, 2, 8, 1, 4, 7, 5, 9, 3]

[9, 3, 7, 6, 2, 5, 1, 4, 8]

[8, 1, 5, 9, 3, 4, 2, 6, 7]

[2, 6, 4, 7, 1, 8, 9, 3, 5]

3.2 数独出题实现1

仿照解题过程,从空盘开始逐渐填写数字。

  1. 主程序SDSet1.py

'''

    数独出题程序

    出题结果存在在指定文件中。

'''

import sys

import CShudu1 as sd

#  主程序

if len(sys.argv)<3 :

    print("数独出题程序使用方法: ")

    print(sys.argv[0],"FileName N")

    print("FileName为输出文件文件名称前缀,第i个数独结果输出文件名称为FileName_i.dat;N为生成的数独题目数目。")

    exit(1)

fout=sys.argv[1]

nout=int(sys.argv[2])

#初始化

cs=sd.CShudu()

# 开始出题

for i in range(nout):

    nr=cs.set1()

    fout=sys.argv[1]+"-"+str(i)+".dat"

    print("生成第",i+1,"个数独题目:",fout)

    cs.PrintData()

    nret=cs.WriteData(fout)

    if nret!=0:

        print("写输出文件错误!")

        exit(3)

    if len(cs.lsres)>=1 :

        cs.loadres(0)

        fout=sys.argv[1]+"-"+str(i)+".res"

        print("生成第",i+1,"个数独结果:",fout)

        cs.PrintData()

        nret=cs.WriteData(fout)

        if nret!=0:

            print("写输出文件错误!")

            exit(3)

这里调用的子程序同3.1子程序CShudu1.py。

运行结果:

E:\程序\test\python\shudu>SDSet1.py data\t 1

生成第 1 个数独题目: data\t-0.dat

----------------------------------------

[3, 4, 0, 8, 1, 9, 2, 0, 5]

[2, 5, 0, 0, 3, 0, 4, 8, 1]

[0, 1, 0, 0, 2, 4, 9, 3, 0]

[0, 0, 0, 0, 5, 0, 0, 1, 6]

[0, 0, 0, 0, 0, 2, 0, 0, 0]

[0, 0, 3, 1, 0, 0, 5, 0, 2]

[0, 0, 0, 2, 4, 0, 8, 0, 0]

[4, 0, 0, 9, 0, 0, 1, 0, 0]

[0, 0, 1, 0, 8, 3, 0, 2, 0]

生成第 1 个数独结果: data\t-0.res

----------------------------------------

[3, 4, 7, 8, 1, 9, 2, 6, 5]

[2, 5, 9, 6, 3, 7, 4, 8, 1]

[6, 1, 8, 5, 2, 4, 9, 3, 7]

[9, 2, 4, 3, 5, 8, 7, 1, 6]

[1, 6, 5, 4, 7, 2, 3, 9, 8]

[8, 7, 3, 1, 9, 6, 5, 4, 2]

[7, 3, 6, 2, 4, 1, 8, 5, 9]

[4, 8, 2, 9, 6, 5, 1, 7, 3]

[5, 9, 1, 7, 8, 3, 6, 2, 4]

3.3 数独出题实现2

主要思路是从一个完成的数独结果开始,随机挖除一定数目的空格,然后再判断当前局面解是否唯一。

主程序SDSet2.py

'''

    数独出题程序

    出题结果存在在指定文件中。

'''

import sys

import CShudu2 as sd

#  主程序

if len(sys.argv)<3 :

    print("数独出题程序使用方法: ")

    print(sys.argv[0],"FileName N")

    print("FileName为输出文件文件名称前缀,第i个数独结果输出文件名称为FileName_i.dat;N为生成的数独题目数目。")

    exit(1)

fout=sys.argv[1]

nout=int(sys.argv[2])

#初始化

cs=sd.CShudu()

# 开始出题

for i in range(nout):

    nr=cs.set2()

    if len(cs.lsres)==1 :

        fout=sys.argv[1]+"-"+str(i)+".dat"

        nb=0

        for k in range(9):nb+=cs.data[k].count(0)

        print("生成第{}个数独题目:{}。题目中数字总数{}个。".format(i+1,fout,81-nb))

        cs.PrintData()

        nret=cs.WriteData(fout)

        if nret!=0:

            print("写输出文件错误!")

            exit(3)

        cs.loadres(0)

        fout=sys.argv[1]+"-"+str(i)+".res"

        print("生成第",i+1,"个数独结果:",fout)

        cs.PrintData()

        nret=cs.WriteData(fout)

        if nret!=0:

            print("写输出文件错误!")

            exit(3)

    else:

        print("生成第",i+1,"个数独失败!")

子程序CShudu2.py

import random

'''

封装为类的数独搜索

'''

class CShudu :

    def __init__(self): # 初始化

        # 数据

        self.clear()

    def clear(self): # 清除数据

        #当前局面数据

        self.data=[[0 for i in range(9)] for j in range(9)]

        #当前局面每个格子的可用数据列表

        self.lsava=[[set() for i in range(9)] for j in range(9)]

        #找到的可行解列表

        self.lsres=[]

    # 从保持data数据至lsres

    def saveres(self):

        self.lsres.append([self.data[i].copy() for i in range(9)])

        #print("in saveres",self.lsres)

    # 从lsres装载数据至data

    def loadres(self,k):

        #print("in loadres",self.lsres)

        self.data=[self.lsres[k][i].copy() for i in range(9)]

    # 从文件读矩阵数据

    def ReadData(self,fname) :

        try:

            f=open(fname,"r")

        except FileNotFoundError:

        #打开文件错误

            return 1

        for i in range(9):

            str=f.readline()

            lstr=str.split()

            #文件读取错误

            if len(lstr) < 9 : return 2

            self.data[i]=[int(lstr[j]) for j in range(9)]

        f.close()

        return 0

    # 写矩阵数据至屏幕

    def PrintData(self) :

        print("-"*40)

        for i in range(9):

            print(self.data[i])

    # 写矩阵数据至文件

    def WriteData(self,fname) :

        f=open(fname,"w")

        #打开文件错误

        if not f : return 1

        for i in range(9):

            s1=''

            for j in range(9):

                s1=s1+str(self.data[i][j])+" "

            s1=s1+'\n'

            f.write(s1)

        f.close()

        return 0

    # 搜索位置(i,j)可用数字集合Sava

    def FindSava(self,i,j):

        Sava=set()

        Snava=set()

        for k in range(9):

            if self.data[i][k]>0 : Snava.add(self.data[i][k])   #行

            if self.data[k][j]>0 : Snava.add(self.data[k][j])   #列

        m=int(i/3)*3

        n=int(j/3)*3

        for k in range(m,m+3):

            for l in range(n,n+3):

                if self.data[k][l]>0 : Snava.add(self.data[k][l])

        Sava= {i for i in range(1,10)}-Snava

        return Sava

    # 查找目前局面的可行方案数,搜索上限为nup:

    # 返回值:0,找不到可行方案;1,成功

    # self.lsres,列表,存放找到的数独结果

    def getNumRes(self,nup):

        nret=0

        nmin=10

        for i in range(9):

            for j in range(9):

                if self.data[i][j]==0 :

                    if len(self.lsava[i][j])<nmin:

                        nmin=len(self.lsava[i][j])

                        ni=i

                        nj=j

        if nmin>9:

            self.saveres()

            nret=1

            #print("In getNumRes,len(self.lsres),nret=",len(self.lsres),nret)

            #self.PrintData()

            return nret #找到一个方案

        # 找到ni,nj点

        #self.PrintData()

        Sava=self.lsava[ni][nj].copy()

        #print("In getNumRes1 ni,nj,Sava=",ni,nj,Sava)

        while len(Sava) > 0 :

           x=Sava.pop()

           self.data[ni][nj]=x

           ls1=self.UpdateLsava(ni,nj)

           #print("In getNumRes2, ni=",ni,"nj=",nj,"X=",x)

           nret=self.getNumRes(nup)

           #恢复

           self.data[ni][nj]=0

           self.RestoreLsava(ls1)

           if len(self.lsres)>=nup :

              break    # 搜索完成

        #print("In getNumRes3,len(self.lsres),nret=",len(self.lsres),nret)

        return nret

    #计算全部空格的可行数字列表

    def CalLsava(self):

        for i in range(9):

            for j in range(9):

                if self.data[i][j]==0 :

                    self.lsava[i][j]=self.FindSava(i,j)

    #更新指定格相关的数字列表,返回原始列表

    def UpdateLsava(self,i,j):

        ls1=[]

        for k in range(9):

            if self.data[i][k]==0 :

                ls1.append((i,k,self.lsava[i][k].copy()))

                self.lsava[i][k]=self.FindSava(i,k)   #行

            if self.data[k][j]==0 and k!=i:

                ls1.append((k,j,self.lsava[k][j].copy()))

                self.lsava[k][j]=self.FindSava(k,j)   #列

        m=int(i/3)*3

        n=int(j/3)*3

        for k in range(m,m+3):

            if k==i: continue

            for l in range(n,n+3):

                if l==j: continue

                if self.data[k][l]==0 :

                    ls1.append((k,l,self.lsava[k][l].copy()))

                    self.lsava[k][l]=self.FindSava(k,l)

        return ls1

    #根据列表恢复Lsava

    def RestoreLsava(self,lsava):

        for i,j,s1 in lsava:

            self.lsava[i][j]=s1

    # 进行一次数独出题

    def set2(self):

        self.clear()

        Iall=81

        Imax=35     #设置有数字格子上限

        Imin=30     #设置有数字格子下限

        IBmax=Iall-Imin+1

        IBmin=Iall-Imax

        NT=10000    #尝试的上限

        nt=0

        nret=0

        while nt<NT :

            nt+=1

            #设置数据

            self.setObj()

            #将要挖空格的位置

            lb1=random.sample(range(Iall),IBmax)

            lb=[]

            for k in lb1:

                i=int(k/9)

                j=k-i*9

                lb.append((i,j,self.data[i][j]))

            #print("lb=",lb)

            for k in range(IBmax):

                i,j,d=lb[k]

                self.data[i][j]=0

            self.CalLsava()

            for k in range(IBmax-1,IBmin-1,-1):

                i,j,d=lb[k]

                self.data[i][j]=d

                ls1=self.UpdateLsava(i,j)

                self.lsres=[]

                n=self.getNumRes(2)

                #print("In set2,nt,k,i,j,n=",nt,k,i,j,n)

                #print("In set2,len(self.lsres)=",len(self.lsres))

                if len(self.lsres)==1:#只有一个结果

                     nret=1

                     break

                elif len(self.lsres)<1:#无结果

                     nret=0

                     break

                else:   #2个结果

                    nret=2

            if len(self.lsres)==1:#只有一个结果

                break

        return nret

       

    # 设置data为目标数据

    def setObj(self):

        dobj=[

            [9,7,6,4,3,2,8,5,1],

            [5,3,4,8,7,1,9,2,6],

            [1,8,2,5,9,6,4,7,3],

            [6,1,9,2,4,3,5,8,7],

            [7,5,3,1,8,9,6,4,2],

            [4,2,8,6,5,7,1,3,9],

            [3,4,7,9,6,5,2,1,8],

            [8,9,1,3,2,4,7,6,5],

            [2,6,5,7,1,8,3,9,4]

        ]

        m=random.sample(range(1,10),9)

        for i in range(9):

            for j in range(9):

                self.data[i][j]=m[dobj[i][j]-1]

       

运行结果:

E:\程序\test\python\shudu>SDSet2.py data\t 3

生成第1个数独题目:data\t-0.dat。题目中数字总数34个。

----------------------------------------

[0, 0, 0, 0, 2, 0, 6, 4, 9]

[0, 0, 0, 0, 0, 0, 0, 0, 7]

[0, 6, 3, 0, 5, 0, 1, 0, 0]

[7, 0, 5, 3, 0, 0, 4, 0, 0]

[0, 0, 0, 0, 6, 5, 0, 1, 0]

[1, 0, 0, 0, 0, 8, 9, 2, 5]

[0, 0, 8, 5, 0, 4, 0, 0, 0]

[0, 5, 9, 2, 3, 0, 0, 0, 4]

[0, 0, 4, 8, 0, 6, 2, 5, 0]

生成第 1 个数独结果: data\t-0.res

----------------------------------------

[5, 8, 7, 1, 2, 3, 6, 4, 9]

[4, 2, 1, 6, 8, 9, 5, 3, 7]

[9, 6, 3, 4, 5, 7, 1, 8, 2]

[7, 9, 5, 3, 1, 2, 4, 6, 8]

[8, 4, 2, 9, 6, 5, 7, 1, 3]

[1, 3, 6, 7, 4, 8, 9, 2, 5]

[2, 1, 8, 5, 7, 4, 3, 9, 6]

[6, 5, 9, 2, 3, 1, 8, 7, 4]

[3, 7, 4, 8, 9, 6, 2, 5, 1]

生成第2个数独题目:data\t-1.dat。题目中数字总数33个。

----------------------------------------

[1, 0, 0, 0, 0, 0, 0, 0, 0]

[2, 0, 0, 0, 5, 0, 1, 8, 0]

[0, 7, 8, 2, 1, 0, 6, 5, 3]

[0, 0, 1, 0, 6, 3, 0, 7, 5]

[0, 0, 3, 4, 7, 0, 0, 0, 0]

[6, 0, 0, 0, 0, 0, 4, 0, 0]

[3, 0, 0, 1, 0, 0, 0, 0, 7]

[0, 1, 0, 3, 0, 6, 5, 0, 0]

[0, 9, 0, 5, 0, 7, 3, 0, 0]

生成第 2 个数独结果: data\t-1.res

----------------------------------------

[1, 5, 9, 6, 3, 8, 7, 2, 4]

[2, 3, 6, 7, 5, 4, 1, 8, 9]

[4, 7, 8, 2, 1, 9, 6, 5, 3]

[9, 4, 1, 8, 6, 3, 2, 7, 5]

[5, 2, 3, 4, 7, 1, 9, 6, 8]

[6, 8, 7, 9, 2, 5, 4, 3, 1]

[3, 6, 5, 1, 9, 2, 8, 4, 7]

[7, 1, 4, 3, 8, 6, 5, 9, 2]

[8, 9, 2, 5, 4, 7, 3, 1, 6]

生成第3个数独题目:data\t-2.dat。题目中数字总数35个。

----------------------------------------

[0, 0, 0, 1, 4, 0, 0, 2, 0]

[2, 4, 0, 0, 6, 9, 7, 0, 8]

[0, 3, 0, 0, 0, 8, 0, 6, 4]

[8, 9, 0, 5, 0, 0, 0, 0, 0]

[0, 0, 4, 9, 3, 0, 0, 1, 0]

[1, 0, 0, 8, 2, 6, 9, 4, 0]

[4, 0, 6, 0, 0, 0, 0, 0, 0]

[0, 0, 0, 4, 0, 1, 0, 8, 2]

[5, 8, 2, 0, 0, 0, 0, 0, 0]

生成第 3 个数独结果: data\t-2.res

----------------------------------------

[7, 6, 8, 1, 4, 5, 3, 2, 9]

[2, 4, 1, 3, 6, 9, 7, 5, 8]

[9, 3, 5, 2, 7, 8, 1, 6, 4]

[8, 9, 7, 5, 1, 4, 2, 3, 6]

[6, 2, 4, 9, 3, 7, 8, 1, 5]

[1, 5, 3, 8, 2, 6, 9, 4, 7]

[4, 1, 6, 7, 8, 2, 5, 9, 3]

[3, 7, 9, 4, 5, 1, 6, 8, 2]

[5, 8, 2, 6, 9, 3, 4, 7, 1]

Python学习案例2——数独解题及出题程序相关推荐

  1. python漂亮界面 数独游戏源代码_使用Python编写数独游戏自动出题程序

    原标题:使用Python编写数独游戏自动出题程序 数独是一个很好玩的游戏,可以锻炼推理能力.下面的代码可以自动生成数独游戏题目. fromrandom importshuffle, randrange ...

  2. python递归算法案例教案_Python电子教案2-1-Python程序实例解析.ppt

    Python电子教案2-1-Python程序实例解析.ppt 简单说,eval()的作用是将输入的字符串内容变成Python语句,并执行这个语句.实例代码1.1使用eval()函数将用户的部分输入(T ...

  3. python数独游戏源代码_使用Python编写数独游戏自动出题程序

    数独是一个很好玩的游戏,可以锻炼推理能力.下面的代码可以自动生成数独游戏题目. from random import shuffle, randrange def generate(): # 初始网格 ...

  4. 数独游戏python制作_使用Python编写数独游戏自动出题程序

    数独是一个很好玩的游戏,可以锻炼推理能力.下面的代码可以自动生成数独游戏题目. from random import shuffle, randrange def generate(): # 初始网格 ...

  5. 使用Python编写数独游戏自动出题程序

    数独是一个很好玩的游戏,可以锻炼推理能力.下面的代码可以自动生成数独游戏题目. from random import shuffle, randrange def generate():     # ...

  6. python学习案例——中国人口统计

    import numpy as np import matplotlib as mpl import matplotlib.pyplot as pltmpl.rcParams['font.sans-s ...

  7. Python综合案例—利用tkinter实现计算器的程序

    目录 一.导入 tkinter 库 定义全局变量 二.定义回调函数 三.创建窗口对象 四.创建标签控件 五.创建数字按钮 六.创建加.减.乘.除和等于按钮 七.创建清空按钮 八.总结 用Python实 ...

  8. 数独解题程序的python实现_python实现自动解数独小程序

    跟朋友最近聊起来数独游戏,突发奇想使用python编写一个自动计算数独解的小程序. 数独的规则不再过多阐述,在此描述一下程序的主要思路: (当前程序只针对于简单的数独,更复杂的还待深入挖掘) 1.计算 ...

  9. Python项目实战学习案例--股票模拟交易系统

    Python学习案例–股票模拟交易系统 源代码地址:https://gitee.com/wujize188_admin/mini_stock.git 主要技术 后台:Flask框架,sqlalchem ...

最新文章

  1. bitcoin全节点部署及bitcoind bitcoin-cli命令使用解释
  2. 160个Crackme004
  3. C#中提示:当前上下文中不存在名称“ConfigurationManager”
  4. Java中 synchronized 关键字的理解
  5. bzoj 3343 教主的魔法 分块
  6. 通过 Service 访问 Pod - 每天5分钟玩转 Docker 容器技术(136)
  7. Loading动画加载素材模板,UI设计师好帮手
  8. Flutter基础布局组件及实现
  9. 我的2009:知识管理篇
  10. [转]Asp.Net下导出/导入规则的Excel(.xls)文件
  11. 给新服务器装linux系统,新服务器安装linux系统安装教程
  12. LaTeX下载安装-1
  13. jclasslib安装
  14. 战神引擎传奇手游源码【诛仙玛法单职业五大陆】
  15. 0014 UVA1589 象棋 Xiangqi
  16. 实战案例!使用 Python 进行 RFM 客户价值分析!
  17. Allegro-PCB导入DXF文件
  18. 国科大学习资料--人工智能原理与算法-第十四次作业解析(学长整理)
  19. 计算机论文课题来源,浅析论文题目的来源和意义
  20. 上线切换 - 如何导入在制品

热门文章

  1. 从零搭建开发脚手架 Spring Boot集成Mybatis-plus之一
  2. 阿里云ECS服务器 跨域Access-Control-Allow-Origin 问题
  3. 红外温度传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  4. 直播预告|ICML专场最后一场啦!来蹲守直播间呀
  5. 能编程100行的c语言题目,C语言编程100题
  6. BAT机器学习面试1000道
  7. 树莓派与win10主机传输文件方式(未完,占坑)
  8. 吉林大学计算机李昕,本报新聘百名特约教工通讯员
  9. 每逢佳节胖三斤? 春节过后天猫上家用健身机3天被疯抢10万台
  10. HPUX 11iV3 LVM新变化