0.项目

项目地址:https://github.com/John-zjm/suduku
GUI地址:https://github.com/John-zjm/sudoku_Gui


1.需求分析

需求分析

1.生成终局 格式:sudoku.exe -c n
1)不重复
2)1<=n<=1000000,能处理非法参数
4)n在1000以内时,要求程序在 60 s 内给出结果
5)输出生成的数独终盘至sudoku.txt
2. 求解数独 格式:sudoku.exe -s path
1)从指定文件读取,每读81个数字视作一道数独题,忽略其他字符
2)要求文件内的数独题目是合法的
3)文件内数独个数在1000以内时,要求程序在 60 s 内给出结果
4)输出已读入数独的答案至sudoku.txt。若存在未满81个的数字,在已解出的答案后输出“存在错误格式!”


2.解题思路

  • 我主要把项目分为三部分:

    • 输入部分
    • 生成数独终局
    • 解数独

在写界面的时,只要稍作修改就可以使用后两部分。

  • 详细描述一下这三个部分:

    • 输入略过,有Python的异常处理很好写

    • 生成数独终局用的是行列变换法。数独中间的九宫格经过行列变换可以变换为2!×3!×3!×2!×3!×3!=5184(因为固定左上角为5)。这样我需要生成1000000/5184=192个九宫格就够了,而正中九宫格有8!=40320个足够满足要求。

    • 解数独我主要参考了《数独求解的候选数优化算法设计》这篇论文,运用了显性候选数规则、隐性候选数规则、九宫格交叉排除规则。Python的numpy可以很好的对数组进行计算。在进行dfs时,我对候选数进行了估值,具体是(10-候选个数) + 同行确定数字个数 + 同列确实数字个数


3.设计实现

  • 输入处理类:
    根据参数调用下列函数进行相应处理(包括参数合法性判断)
  • 终盘生成类:
    种子生成函数、交换组合函数、行列交换函数、转换输出函数
  • 数独求解类:
    初始化函数、记录函数函数、检错函数、恢复函数、深度优先遍历函数、评价函数、减少候选数函数。


单元测试:
“错误参数”:

sudoku.exe -c 20
sudoku.exe -c 1000000
sudoku.exe -s C:\Users\0\OneDrive\1.txt
sudoku.exe -s C:\
sudoku.exe -p asd

“错误数独”

sudo=[[1,2,3,4,5,6,7,8,9]for row in range(9)]
sudo=[[0 for col in range(9)]for row in range(9)]
sudo =[[8,0,0, 0,0,0, 0,0,0][0,0,3, 6,0,0, 0,0,0][0,7,0, 0,9,0, 2,0,0][0,5,0, 0,0,7, 0,0,0][0,0,0, 0,4,5, 7,0,0][0,0,0, 1,0,0, 0,3,0][0,0,1, 0,0,0, 0,6,8][0,0,8, 5,0,0, 0,1,0][0,9,0, 0,0,0, 4,0,0]]

4.性能改进

  • 把输出改为一次输出,输入改为一次读入,多次处理。
  • 在回溯时,用估值函数,让价值大的候选数先进循环
  • 在界面中,把检测数独从全部检测,改为对修改数的所在行,所在列,所在九宫格进行检测。



由图可以看出combine()和check_one_possible()消耗较大

def combine(self, c1, c2, c3, r1, r2, r3):self.table = deepcopy(self.temp)if (c1 == 1):self.colExchange(1, 2)if (c2 == 1):self.colExchange(4, 5)if (c2 == 2):self.colExchange(3, 4)if (c2 == 3):self.colExchange(3, 4)self.colExchange(4, 5)if (c2 == 4):self.colExchange(3, 5)self.colExchange(4, 5)if (c2 == 5):self.colExchange(3, 5)if (c3 == 1):self.colExchange(7, 8)if (c3 == 2):self.colExchange(6, 7)if (c3 == 3):self.colExchange(6, 7)self.colExchange(7, 8)if (c3 == 4):self.colExchange(6, 8)self.colExchange(7, 8)if (c3 == 5):self.colExchange(6, 8)if (r1 == 1):self.rowExchange(1, 2)if (r2 == 1):self.rowExchange(4, 5)if (r2 == 2):self.rowExchange(3, 4)if (r2 == 3):self.rowExchange(3, 4)self.rowExchange(4, 5)if (r2 == 4):self.rowExchange(3, 5)self.rowExchange(4, 5)if (r2 == 5):self.rowExchange(3, 5)if (r3 == 1):self.rowExchange(7, 8)if (r3 == 2):self.rowExchange(6, 7)if (r3 == 3):self.rowExchange(6, 7)self.rowExchange(7, 8)if (r3 == 4):self.rowExchange(6, 8)self.rowExchange(7, 8)if (r3 == 5):self.rowExchange(6, 8)self.sudoku_map.append(deepcopy(self.table))
# 显性候选数def _check_one_possbile(self):# 同一行只有一个数字的情况for r in range(0, 9):values = filter(lambda x: isinstance(x, list), self.value[r])for c, item in enumerate(self.value[r]):if isinstance(item, list):for value in item:if sum(map(lambda x: x.count(value), values)) == 1:self.value[r, c] = valueself.new_points.put((r, c))return True# 同一列只有一个数字的情况for c in range(0, 9):values = filter(lambda x: isinstance(x, list), self.value[:, c])for r, item in enumerate(self.value[:, c]):if isinstance(item, list):for value in item:if sum(map(lambda x: x.count(value), values)) == 1:self.value[r, c] = valueself.new_points.put((r, c))return True# 九宫格内的单元格只有一个数字的情况for r, c in self.base_points:values = filter(lambda x: isinstance(x, list),self.value[r:r+3, c:c+3].reshape(1, -1)[0])for m_r, row in enumerate(self.value[r:r+3, c:c+3]):for m_c, item in enumerate(row):if isinstance(item, list):for value in item:if sum(map(lambda x: x.count(value), values)) == 1:self.value[r+m_r, c+m_c] = valueself.new_points.put((r+m_r, c+m_c))return True

5.代码说明

# 找到下一个全排列i = len(sudo_num)-2while i >= 0 and sudo_num[i] >= sudo_num[i+1]:i -= 1j = i + 1k = len(sudo_num) - 1while sudo_num[i] >= sudo_num[k]:k -= 1(sudo_num[i], sudo_num[k]) = (sudo_num[k], sudo_num[i])sudo_num[j:] = sudo_num[:j-1:-1]

这部分属于生成数独,主要对九宫格进行变换,生成下一个非递增的全排列,以实现变换中心九宫格。

# 九宫格交叉排除规则def _check_same_num(self):for b_r, b_c in self.base_points:block = self.value[b_r:b_r+3, b_c:b_c+3]# 判断数字1~9在该九宫格的分布情况data = block.reshape(1, -1)[0]for i in range(1, 10):result = map(lambda x: 0 if not isinstance(x[1], list) else x[0] + 1 if x[1].count(i) else 0, enumerate(data))result = filter(lambda x: x > 0, result)r_count = len(result)if r_count in [2, 3]:# 2或3个元素才有可能同一行或同一列rows = map(lambda x: (x-1) / 3, result)cols = map(lambda x: (x-1) % 3, result)if len(set(rows)) == 1:# 同一行,去掉其他行的数字result = map(lambda x: b_c + (x-1) % 3, result)row = b_r + rows[0]for col in range(0, 9):if col not in result:item = self.value[row, col]if isinstance(item, list):if item.count(i):item.remove(i)# 判断移除后,是否剩下一个元素if len(item) == 1:self.new_points.put((row, col))self.value[row, col] = item[0]return Trueelif len(set(cols)) == 1:# 同一列result = map(lambda x: b_r + (x-1)/3, result)col = b_c + cols[0]for row in range(0, 9):if row not in result:item = self.value[row, col]if isinstance(item, list):if item.count(i):item.remove(i)# 判断移除后,是否剩下一个元素if len(item) == 1:self.new_points.put((row, col))self.value[row, col] = item[0]return True

这部分属于解决数独。为九宫格交叉排除规则。(九宫格交叉排除规则) 若同一个九 宫格里,某个数字 x 仅出现在格子 d1, d2,…, dn (2 ≤n ≤ 3) 的候选数集合 ω1, ω2,…, ωn 中,且 d1, d2,…, dn 是在同一行(或列),那么同一行(或列) 的其他未填入数字格子d1, d2,…, dm(1 ≤ m ≤9 - n) 候选数集合 ω’1, ω’ 2,…, ω’m可以将数字x排除。
如图

在图中,第二个九宫格中的B行出现了2,9两次。则B8可以排除2,B3可以排除9。

while True:self.dig_hole()if (self.hoels > 30):if (self.lev > level*30):  # 猜测次数0~30位简单,30~60为中等,60~无穷为困难breakif self.hoels > 30+level*10:  # 30~40空为简单 40~50为中等 50~60为困难break

这部分属于生成数独游戏,根据解数独的猜测次数来判断,如果猜测次数小于30或空为40个,则为简单。以此类推。

def check_value(self, row, col):b_r = int(row/3)*3b_c = int(col/3)*3# 行for i in range(1, 10):sum = 0record = -1for j in range(9):if self.map[row][j] == i:sum = sum + 1if sum == 1:record = j  # 有一个elif sum == 2:  # 有两个重复数字if self.isyuan[row][record] != 1:  # 是否为题目数字self.heng_base[row][record] = 1self.show_num[row][record].setStyleSheet("background-color: red;")record = -1if self.isyuan[row][j] != 1:self.heng_base[row][j] = 1self.show_num[row][j].setStyleSheet("background-color: red;")elif sum > 2:  # 有更多if self.isyuan[row][j] != 1:self.heng_base[row][j] = 1self.show_num[row][j].setStyleSheet("background-color: red;")if (record != -1):  # 无重复self.heng_base[row][record] = 0# 在其他方面没有错误if (self.shu_base[row][record] == 0) & (self.kuai_base[row][record] == 0):if self.isyuan[row][record] != 1:self.show_num[row][record].setStyleSheet("background-color: green;")# 列for i in range(1, 10):sum = 0record = -1for j in range(9):if self.map[j][col] == i:sum = sum + 1if sum == 1:record = jelif sum == 2:if self.isyuan[record][col] != 1:self.shu_base[record][col] = 1self.show_num[record][col].setStyleSheet("background-color: red;")record = -1if self.isyuan[j][col] != 1:self.shu_base[j][col] = 1self.show_num[j][col].setStyleSheet("background-color: red;")elif sum > 2:if self.isyuan[j][col] != 1:self.shu_base[j][col] = 1self.show_num[j][col].setStyleSheet("background-color: red;")if (record != -1):self.shu_base[record][col] = 0if (self.heng_base[record][col] == 0) & (self.kuai_base[record][col] == 0):if self.isyuan[record][col] != 1:self.show_num[record][col].setStyleSheet("background-color: green;")# 九宫格for i in range(1, 10):sum = 0record = [-1, -1]for jrow in range(3):for jcol in range(3):if self.map[jrow+b_r][jcol+b_c] == i:sum = sum + 1if sum == 1:record = [jrow+b_r, jcol+b_c]elif sum == 2:if self.isyuan[record[0]][record[1]] != 1:self.kuai_base[record[0]][record[1]] = 1self.show_num[record[0]][record[1]].setStyleSheet("background-color: red;")record = [-1, -1]if self.isyuan[jrow+b_r][jcol + b_c] != 1:self.kuai_base[jrow+b_r][jcol + b_c] = 1self.show_num[jrow+b_r][jcol +b_c].setStyleSheet("background-color: red;")elif sum > 2:if self.isyuan[jrow+b_r][jcol + b_c] != 1:self.kuai_base[jrow + b_r][jcol + b_c] = 1self.show_num[jrow+b_r][jcol +b_c].setStyleSheet("background-color: red;")if (record[0] != -1):self.kuai_base[record[0]][record[1]] = 0if(self.heng_base[record[0]][record[1]] == 0) & (self.shu_base[record[0]][record[1]] == 0):if self.isyuan[record[0]][record[1]] != 1:self.show_num[record[0]][record[1]].setStyleSheet("background-color: green;")

这部分为界面部分。主要为动态的显示填入数字的对错。如果是对的则是绿色,如果是错的,则是红色。


6.总结

这次的项目,第一部分完成的较快,没有出现太大的问题。但在附加题部分,我因为对qt的信号不熟练,导致出了很多Bug,耽误了很多时间。总体来说,这个项目使我的python有了一个提高。

个人项目-数独(Python实现)——从解数独到写游戏相关推荐

  1. python编程语言可以做游戏吗_python合不合适用来写游戏

    python合不合适用来写游戏 发布时间:2020-07-03 11:10:00 来源:亿速云 阅读:73 作者:清晨 这篇文章主要介绍python合不合适用来写游戏,文中示例代码介绍的非常详细,具有 ...

  2. 如何自学python到做项目-总算明白如何通过项目学习python

    在学习完Python的基础知识之后,有很多朋友为自己接下来要干什么感到迷茫.不知道应该通过什么样的项目来锻炼自己编程水平和思维能力.接下来我就给大家说几个适合Python的新手项目和练手项目,Pyth ...

  3. opencv 训练人脸对比_【项目案例python与人脸识别】基于OpenCV开源计算机视觉库的人脸识别之python实现...

    " 本项目是一个基于OpenCV开源库使用python语言程序实现人脸检测的项目,该项目将从[项目基础知识](即人脸识别的基本原理).[项目实践](人脸识别所需要的具体步骤及其python程 ...

  4. GitHub仓库项目添加Python语言类别

    1.在github项目根目录下添加文件名为 .gitattributes 的文件: 2 文件里面写入: *.py linguist-language=python //你项目内有什么文件 就使用 *. ...

  5. 软件工程基础-个人项目-数独游戏

    软件工程基础-个人项目-数独游戏 ----------------------------------------------------------------------------------- ...

  6. python+opencv别踩白块儿游戏辅助,一天一个opencv小项目(已开源)

    python+opencv别踩白块儿游戏辅助,一天一个opencv小项目(已开源) 见链接

  7. BIT软件工程个人项目——数独sudoku

    BIT软件工程个人项目--数独sudoku 目录: (点击可页内跳转) 1. 项目地址 2. PSP表格 3. 解题思路描述 --3.1初期思考 --3.2数独终局生成 --3.3求解数独 4. 设计 ...

  8. python重量转换程序_重量转换的极小项目说python查找字符串

    好了,最近我忙着研究口水歌,想着自己做一首简单的民谣先试试.项目方面真的有点水... 今天晚上被催作业了,还是我来蹭的python课程,我简单写一下!超级简单! 是一个重量转换的小题目,是将英镑转换成 ...

  9. 视频教程-Python疫情监控完整项目实战-Python

    Python疫情监控完整项目实战 数据产品讲师,人工智能探索者,15年一线IT研发经验,国内顶级互联网行业工作背景,社区达人,著有长篇连载<胖子哥的大数据之路>,<数据实践之美> ...

  10. 软件工程基础个人项目——数独(5)

    软件工程基础个人项目--数独 点击这里可看github上的具体代码 本次个人项目关于数独的生成与求解 PSP表格 PSP2.1 Personal Software Process Stages 预估耗 ...

最新文章

  1. 快速搭建第一个Mybatis程序
  2. css 联系我们,CSS3 模态窗口联系我们表单模板
  3. scrollToItemAtIndexPath使用
  4. vs中没有fstream_vs++2010 编译说找不到 fstream.h 解决方法
  5. Python编程语言学习:判断变量是否为NONE或False的几种常见写法(if not用法教程)
  6. mac基本操作技巧_6个基本设计技巧
  7. ReviewForJob——希尔排序(缩小增量排序)之塞奇威克增量序列
  8. Taro+react开发(10)--多行选择
  9. how hurt my eggs are, if two, please deep two.
  10. ssis zip压缩文件_在SSIS中处理参差不齐的正确格式的文件
  11. 拼多多否认对极兔快递“政策倾斜”;86版西游记“红孩儿”成中科院博士;AirTag遭破解 | 极客头条...
  12. C#扩展方法的理解 (转)
  13. illustrator cs5 2学习笔记
  14. MCS51 系列单片机的最小系统
  15. 思想实验及其在科学发展中的作用
  16. vivoX30是android5的吗,深度剖析揭秘opporeno5质量和vivox30区别是?选哪个更好?独家揭秘报道...
  17. useful eclipse plugins
  18. 蓝牙核心技术概述(五):蓝牙协议规范(irOBEX、BNEP、AVDTP、AVCTP)
  19. iphone苹果手机点击屏幕就亮怎么关闭
  20. 简单理解 柯理化函数

热门文章

  1. DFS判断回路及回路个数
  2. python接口自动化3-自动发帖(session)
  3. 自动配置和 thymeleaf模板引擎
  4. Lotus notes问题与处理
  5. linux开机不运行桌面快捷方式,Android 开机自动运行和添加删除桌面快捷方式
  6. ubuntu添加桌面快捷方式图标
  7. (三)基于PHP——复杂的WSDL的创建(WSDL篇)
  8. 查找攻击者ip的方法
  9. 千兆宽带网接入电脑电脑却只有百兆
  10. 台式计算机如何自动关机,台式电脑如何设置定时关机