• 实验背景

数独起源于拉丁方阵,由单元格、行、列、宫等元素组成,规则是在每行、每列、每宫的九个单元格中填入数字1-9,不重复。给定一定数量提示数的盘面作为初始条件,称为初盘。根据规则将所有单元格填满得到的盘面称为终盘,也就是数独的解。标准的数独初盘只能对应一个终盘。

一般采用随机方式生成数独,即从空白的数独盘面开始随机填数,填入的数字不能违背数独的基本规则,并且要保证解的唯一性。在随机填入过程中,判断某个位置是否可以填入一个随机的数是根据这一位置所处行列宫区域中是否已有此数来决定。当填入这一数之后,修改它所处区域的标记信息。当填入的数字达到一定量之后,再进行数独求解。

数独的求解常用的生成方法有挖洞法,即挖去一个数独终盘上某些位置上的数,使其形成的局面只有一个解,即做为数独初盘。初始的数独终盘,可根据一定的算法生成,也可以根据某个已知的终盘,进行若干次行列互换或数字的交换等方法得到。

  • 问题分析

实验需要我们通过设计算法来输出不同难度的数独题目和答案。

其中数独生成的规则为:根据9X9表盘上面的已知数字,推理出所有剩余空格的数字,并且需满足每一行、每一列、每个九宫格内的数字均含1-9,不重复。数独的难度通过对数独终盘挖掉数字的个数多少来划分。

  • 设计思路

①构造数独规则:先初始化数独空白表格,在空白数独中随机选择要求个空白单元格,筛选出该单元格满足条件的数字并填入。

②深度优先算法:定义深度优先算法。

③基础数独生成:使用深度优先算法对已经部分填入的数独进行遍历求解,输出基本数独。

④实现难度划分:在已经求解完毕的基本数独上随机选取单元格,假设扣除该单元格的数字并检验扣除后数独的唯一性,直至成功扣除指定个数的单元格,生成对应难度的数独题目。再次使用深度优先算法对扣除指定个数单元格的数独题进行遍历求解,并输出对应的数独答案。

  • 基础数独生成

①定义类Sudoku

class Sudoku:
    def __init__(self):
        # 初始化九宫格
        self.grids= np.zeros((9, 9), dtype=int, order='C')
       
self.possibleNums= {1, 2, 3, 4, 5, 6, 7, 8, 9}
       
# 用于挖洞时保存挖好的数独
        self.uniqueGrids = None

②使用Python,导入Numpy库,使用矩阵来保存和处理数独数据。使用zeros方法生成9X9的的0矩阵,初始化数独。导入Random库的randint方法来生成随机的行标和列标用来随机定位一个单元格。

③定义函数get_possible用行、列、九宫格不可重复的规则筛选出指定单元格满足条件的所有取值并存储在集合中。

# 由三个约束条件剔除数据

def get_possible(self, row, col):bRow, bCol = row // 3, col // 3# 提取行标为row的行
rowSet = set(self.grids[row])# 提取列标为col的列
colSet = set(self.grids[:,col])# 由行标列表取出所在的3*3小九宫格
blockSet = set(self.grids[bRow * 3: bRow * 3 + 3,bCol * 3: bCol * 3 + 3].reshape(9))# possible中剔除所在行、所在列和所在小九宫格里的相同的数字
return self.possibleNums - rowSet - colSet - blockSet

④定函数dfs深度优先,遍历数独的所有单元格并通过递归对get_possible函数返回的当前单元格的可能取值进行填入,如果数独无法正确填写,便以回溯的方法回到上一个单元格填入其他的可能值继续执行。

# 深度优先方法解数独

def dfs(self):for row in range(9):for col in range(9):# 00列开始寻找空白格
if self.grids[row, col] == 0:# get_possible得到当前空白格可以填入的数值
possible = self.get_possible(row, col)# 深度优先,填入第一个值,递归填入剩下空白格的值
# 如果无法正确填写,就回归上一个刚填的格,重新填入possible里第二值
# 直到所有空白格都正确填入
for value in possible:self.grids[row, col] = valueif self.dfs():return Trueself.grids[row, col] = 0return Falsereturn True

⑤定义lasVegas函数,先随机定位空白单元格,再调用get_possible函数获取可能的取值并随机填入,直到填充够指定的个数(NUM)后输出仅填入指定数量数字的数独盘,同时调用Random函数的sample方法来打乱顺序,增加数独生成的随机性。再调用dfs函数对数独进行求解并输出求解完毕的基础数独终盘。

# 拉斯维加斯算法生成数独

def lasVegas(self, initGivenCount):# initGivenCount为初始给定数字的个数
while initGivenCount:# 用随机行列定位一个单元格
row = random.randint(0, 8)col = random.randint(0, 8)# 选中空白格,且避免再次选中已经填入的单元格
if self.grids[row, col] == 0:# 随机在可能的取值中取一个数,避免初始的宫格中都是1,2,3等数字
value = random.sample(self.get_possible(row, col), 1)[0]self.grids[row, col] = valueinitGivenCount -= 1# 先输出填充给定初始个数(initGivenCount)数字的基础数独
self.output_sudo()# 再采用深度优先方法解出数独并更新数据
if self.dfs():return Trueelse:return False
  • 难易程度划分

①定义函数checkUnique函数用来判断扣除该单元格后的数独答案是否唯一,如果唯一则可以扣除,如果不唯一则不能扣除。

# 挖洞后判断是否只有唯一的答案
def checkUnique(self, row, col):
    for value in range(1, 10):
        if self.grids[row, col] != value:
            # 假设挖掉这个数字
            self.grids[row, col] = 0
            # 将挖掉的方块置0后,用get_possible来检查
            if value in self.get_possible(row, col):
                # 更换一个其他符合规则的数字之后检查是否可以解
                # 将除去原本值的其他所有值依次尝试
                self.grids[row, col] = value
                if self.dfs():
                    return False
           
# 上面进行深度优先过程已经改变了self.grids的值,恢复更换这个数字之前的状态,再继续换别的数
            self.grids = self.uniqueGrids.copy()
   
# 已尝试所有其他数字发现无解,即只有唯一解
    return True

②定义函数potholing函数实现挖除指定数量单元格,每次均调用函数checkUnique来进行唯一性判断,通过挖除数量的多少得到对应难度的数独题并输出。

# 挖洞出题
def potholing(self, level):
    # level表示要挖掉的数字个数
    self.uniqueGrids = self.grids.copy()
   
digged = 0
    while digged < level:
        row = random.randint(0, 8)
       
col = random.randint(0, 8)
       
# 跳过被挖过的格子
        if self.uniqueGrids[row, col] == 0:
            continue
       
# 挖掉该格子后能生成唯一的九宫格。如果有就继续挖,如果没有唯一解就不挖这个格子
        if self.checkUnique(row, col):
            # 保存挖洞后的状态
            self.uniqueGrids[row, col] = 0
            # 挖完洞后继续挖,直到挖出指定数量的格子
            self.grids = self.uniqueGrids.copy()
           
digged += 1

③重新用dfs深度优先算法对挖除对应数量单元格的数独进行求解.

④格式化输出重新求解后的数独终盘

# 格式化输出数独

def output_sudo(self):for i in range(9):if i % 3 == 0:print('  +-------+-------+--------+')print('  ', end='')for j in range(9):if j in [0, 3, 6]:print("| ", end="")if self.grids[i, j] == 0:print(" ", end=" ")else:print(self.grids[i, j], end=' ')if j == 8:print(" |", end="")print("")print('  +-------+-------+--------+')
def main():NUM=eval(input('\n输入一个数字,程序将以这个个数生成
基础数数 独:'))digCount=eval(input('\n输入需要挖掉的数量:'))myShuDu = Sudoku()print('\n生成的基础数独如下:')myShuDu.lasVegas(NUM)print('\n数独答案如下:')myShuDu.output_sudo()# 输出挖洞后的数独题
myShuDu.potholing(digCount)print('\n挖掉要求个数后的数独题如下:')myShuDu.output_sudo()# 重新将挖洞后的数独利用深度优先算法解答
myShuDu.dfs()print('\n重新用深度优先算法计算出的数独答案如下:')myShuDu.output_sudo()if __name__ == '__main__':main()
  • 算法测试

1.程序运行后,在输入框中输入11,即先填入11个数字在空白数独中,再以此进行深度优先求解数独,同时输出初始数独和求解出的数独终盘。空数独填写11个数之后,后面采用深度优先算法成功的概率在99%以上,而且重新回退尝试的次数非常少,运行时间非常短。

程序运行截图如下

2.在第二个输入框输入50,即在求解出来的数独终盘中扣除50个单元格中的数字,并再以此进行深度优先求解数独,同时输出扣除后的数独题目和求解出的数独终盘。经测试,挖55个洞以下生成一个有唯一解的数独可以在极短的时间内运行完毕。

程序运行截图如下

可见由初始数独深度优先求解出的数独终盘与挖洞后深度优先求解出的数独终盘完全一致,运行无误。

  • 结论

本次实验中,通过对生成随机数独问题的研究,加深了对递归和深度优先问题的理解。

根据此算法,普通微机在5ms左右可以随机生成一局任意难度的数独初盘。在生成过程中,尽可能地使用了随机值,除了数独的基本规则,不再受其他因素的制约,这使得生成的初盘,理论上可以随机分布,所以本算法在实际的数独游戏或比赛的出题中,是比较有效的,且对于数独的统计分析工作具有一定的意义。

通过这次实验,我认到学好算法分析与设计要重视实践操作,在实践中可以发现很多问题,以此来提高自己的逻辑思维和解决问题的能力。

随机数独的生成的实验思路概述相关推荐

  1. (附代码)数独大作业【读取数独,解数独,生成唯一解数独(随机,特定形状,不同难度生成),玩数独】

    注:未经同意不要转载. 上学期简单的做了一个数独程序,实现了一些功能,想简单的为大家提供的思路. 为了避免某些情况出现,具体代码暂时先不发了,有不太懂的地方可以评论提问啊. 下面是我的具体报告: 一, ...

  2. 软件工程基础个人项目——数独终局生成求解

    目录 1.源代码的GitHub链接: 2.PSP表格(预估): 3.题目要求: 4.解题思路: 1)数独游戏规则 2)生成数独终局 2)求解数独 5.设计实现过程: 第一部分:sudoku类的构建 第 ...

  3. 数独终局生成及残局求解

    文章目录 一.项目地址 二.各模块开发时间预估 三.学习过程.解题思路 3.1 开发语言及运行环境 3.2 项目要求分析 3.2.1 需求建模 3.2.2 数据流设计方法 3.3 解题思路 3.3.1 ...

  4. 数独 ( 二 ) ——生成数独终局

    数独 ( 二 ) --生成数独终局 我项目的github链接 :https://github.com/gmj0301/gmj 请查看前面的博客,有修改! 要求 在命令行中使用 -c 参数加数字 N ( ...

  5. MS中Perl脚本实现原子随机掺杂(或生成空位)

    MS中Perl脚本实现原子随机掺杂或生成空位 1.初始模型 2.确定原子掺杂比例 3.建立perl脚本文件 4.Perl脚本内容 5.运行脚本,查看结果 7.生成空位 以Au与Pt按一定比例随机掺杂建 ...

  6. 数独问题求解二:解题思路(1)

    数独问题求解二:解题思路(1) 初拿到任务,我对需求中的许多问题并没有太多思路,于此,我拿着题目在百度以及Google进行了搜索,很巧的是我找到了很多前人对该类项目的总结,这对我的解题有了很大的帮助, ...

  7. 一种基于Voronoi图的曲边化随机颗粒模型生成方法

    一种基于Voronoi图的曲边化随机颗粒模型生成方法 简介  在数值模拟中,除了对物体的整体模拟,还要对物体的细观层面进行模拟,以探讨裂纹的生产.扩展:或者细观层面的变化情况.特别是对于某些颗粒增强型 ...

  8. java生成随机时间_Java生成某段时间内的随机时间

    上代码: import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { /** * 生成随机时间 ...

  9. 用qemu模拟Intel x86平台实验环境 —— 概述

    文章系列: 用qemu模拟Intel x86平台实验环境 -- 概述 用qemu模拟Intel x86平台实验环境 -- 启动系统 用qemu模拟Intel x86平台实验环境 -- 加载并运行app ...

最新文章

  1. 使用 jetty-maven-plugin发布maven项目
  2. 在CodeMash 2012的“ Wat”演讲中提到的这些怪异JavaScript行为的解释是什么?
  3. IE7关闭窗口不提示确认的js
  4. 财务一体化项目,进度与计划11
  5. 3.3.10 动态SQL
  6. 叫板BBC!80后湖南姑娘,花3年首次拍出水下的中国,惊艳了全世界
  7. [转] 测试员,敢问路在何方(来自微软工程师)
  8. python百度百科-python语言概述
  9. 【前端性能】必须要掌握的原生JS实现JQuery
  10. 十大著名黑客——斯蒂芬-沃兹尼克
  11. redis安装----非基于lnmp安装
  12. 【ML经典书籍系列1】解读PRML
  13. Taskctl的定时任务调度
  14. bootstrap-fileinput插件获取图片文件并展示,不进行上传
  15. 利用Kuhn-Munkras算法求最小权值匹配
  16. 攻防世界-baby_web详解
  17. 刚刚,一页马克思手稿在阿里拍卖上拍到了290万
  18. 重读GPDB 和 TiDB 论文引发的 HTAP 数据库再思考
  19. 第一行代码Android(第三版)学习第一天
  20. java调用应用程序_java调用第三方应用程序

热门文章

  1. 桥接模式下无法连接到ens33网络
  2. 解除微软系统正版验证
  3. VMware Fusion 12.2 SLIC 2.6 MOD
  4. AndroidStudio使用程序员字体
  5. html5文字特效教程视频,IT兄弟连 HTML5教程 CSS3属性特效 自定义文字
  6. 博云 x 某农商行 | 银行信息化运维系统升级的最佳实践
  7. 计算机多媒体技术操作题
  8. 研发新员工(应届生)培训计划——初稿
  9. 《Ceph源码分析》——导读
  10. 计算机组成原理um实验总结,计算机组成原理第三次实验报告.doc