Github:Sudoku

项目相关要求

项目需求

利用程序随机构造出N个已解答的数独棋盘 。

输入

数独棋盘题目个数N(0<N<=1000000)。

输出

随机生成N个不重复的已解答完毕的数独棋盘,并输出到sudoku.txt中,输出格式见下输出示例。

[2017.9.4 新增要求] 在生成数独矩阵时,左上角的第一个数为:(学号后两位相加)% 9 + 1。

以上要求摘抄自第二次作业博文,详细内容如:输出示例和测试须知以及选做附加题,请参见:作业原文

过程记录

以为下次作业是开学布置,看到消息多少有点措手不及,点进去又看到那么多字,开始有点慌张,立马升级为首要任务。稍微冷静之后,又认真地看了几遍作业,罗列下主要要做什么,又该怎么安排时间。需要做的事情主要有:看《构建之法》,VS的安装及单元测试和效能分析的使用,以及生成数独的算法,其他一些细节感觉问题不大,至于附加题,有空再说吧。安排上,想争取去学校前做完附加题之外的内容,因为到学校还有些事要处理,具体来说,打算4到8号每天控制在3小时左右,总计15小时,PSP 2.1(附:Markdown页内跳转)。考虑到其他课程和自身等因素,15小时也是每周能够保证的投入,平均大致一天两小时。所以不仅是建立在工作量上,也是测试,估计得视情况调整,毕竟不想经常看到凌晨三四点的福大。

《构建之法》没精读,有效阅读时间大致只有两小时,主要了解到有单元测试和效能分析这两个东西。单元测试就是测试自己写的代码是不是表现得和预期一样,是代码正确性的保证,应该由最熟悉代码的作者本人来写,毕竟代码的作者最了解代码的目的、特点和实现的局限性。效能分析是改进代码的重要途径,可以很有针对性地对性能瓶颈进行改进,进一步提高代码的质量。工具VS的安装倒是挺顺利,至于单元测试和效能分析的使用,作业原文最后都很贴心地给出了参考链接,都照着例子试一试,到时候再捣鼓一下用到自己的代码上,应该问题不大吧。抛去安装不算,学习单元测试和效能分析的使用的有效时间也是两小时左右。

生成数独的算法最头疼,很长一段时间都没有思路,觉得专门去想也想不出来,这需要灵感,所以只是在心里惦记着时不时想一想。最初打算一个个随机,不符合要求就再随机,实在不行就回退再随机原来的,直到最后一个。觉得太复杂,写单元测试也很麻烦吧,就没有继续想实现的细节。但是也想不出其它的,开始抱怨数独怎么这么麻烦,怀疑是不是没有多少种情况。可是题目中的N最大到100w,还能不重复,那该是有很多种。看着数独的条条框框,觉得有点像魔方,感觉可以转一转变成另一个数独表,比如旋转,所以才有那么多情况。然后就想到了很简单的变换方法:在九宫格范围内变换行或列,举例来说第1,2,3行可以互相交换顺序,仍然符合要求,还和原来不一样。再结合和上列,多交换几次,也没深想,感觉可以变出所有情况了。

于是我就兴冲冲地开始测试,想写出主要的生成部分看看效果。太久没打代码,有种一夜回到解放前的感觉,前前后后折腾了3小时左右才实现想法。然后,终于发现自己还是太天真了,输出的数独长得超像,感觉多换几次就回去了。其实仔细想想也很好理解,在九宫格范围内交换行和列,原来九宫格同一行的三个数字还是在同一行,顶多顺序不一样,也就六种情况。加上题目增加要求左上角固定,我就让第一行第一列不参与交换,那就只有(2×6×6)^2 = 5184种情况,N可是有100w啊,肯定会重复的。这就很尴尬了,没想清楚就打代码,还得想新法子。又想了下最初的一个个生成,果然还是复杂,感觉应该还有其它变换方式。于是我试着交换同一个九宫格的数字,发现在其它九宫格也交换相同的数字就可以,办法傻倒确实有用啊。除去固定的左上角,那就有8!=40320种可能,可惜还是比100w小。但是,我可以把二者结合起来啊!5184×40320就足够了,理论上100w不重复是有可能的,具体内容参见下文设计实现及关键代码。

设计实现

代码组织结构很简单,总共三个类,分别是“Scan”类,“Print”类以及“Generator”类,功能也很简单。“Scan”类处理输入,只有一个检查输入是否合法的函数“checkInput(int argc, char * argv[])”,合法返回要求生成的数独个数N,不合法返回0。不合法的输入调用“Print”类的报错函数“printError()”,合法则调用“Print”类的另一个函数“printSudokuToFile(int N, char * filepath)”,在这个函数里会调用N次“Generator”类的公共函数“getNewSudoku()"。

稍微复杂的是“Generator"类,有个私有的初始数独表,还有些函数用来支持生成新的数独,像上面提到的行列交换,数字交换,而且考虑到可能要写单元测试,随机获取交换的行列号和交换的数字也单独写了函数,尽可能使函数功能明确可验,具体组织在下面的关键代码讲。

把各个代码组织起来,解决一些不熟悉的细节问题,像文件输入输出,再加上前前后后的调试,捣鼓了得有三小时左右。然后借西瓜学长的博客:Git和Github简单教程以及git教程再熟悉了下git(至少也有一小时其实),终于上传了第一个版本,感觉别说附加题了,单元测试和效能分析再改进都紧张。时间安排果然得调整,15小时不够的啊,一边做一边写博客也要花好多时间。

关键代码

int * Generator::getNewSudoku()
{ int * random;
int random1, random2; random = getRandomRorC();
random1 = random[0];
random2 = random[1];
swapRows(random1, random2); random = getRandomRorC();
random1 = random[0];
random2 = random[1];
swapColumns(random1, random2); int cnt = rand() % 81 + 1;
for (int i = 0; i < cnt; i++)
{
random = getRandomNumber();
random1 = random[0];
random2 = random[1];
swapNumbers(random1, random2);
} return &initSudoku[0][0];
}

这就是关键的生成代码,逻辑很简单,先生成随机交换的行号,然后交换行,类似的交换列,最后再交换随机数字。其中,因为交换数字一次只交换两个数字,可能性远没有潜力的8!那么多,于是就多交换几次,九九八十一再随机下,问题不大,理论上可能性超过100w种了。当然,作业要求左上角固定为学号后两位之和对九取余加一(我是42,也就是7),于是随机生成的行列号不包括1,随机交换的数字不包括7,初始数独的左上角为7。

感觉本可以写得优雅些,比如把随机生成行列号集成在交换行列的函数里,可这样结果不确定,考虑到可能的单元检测及代码覆盖率,最终还是写成这样。生成两个随机的行列及随机数字感觉用数组传出来方便些,然后把数组元素当函数参数传入就又有点尴尬,应该是有更好的写法。而且只是理论上有超过100w种的可能性,并没有实际运行去检验。但现在是既没想法也没时间,能够实现基础功能就好。至于里面被调用的那些函数,技术含量不高,顶多情况多点,不再细说。

测试运行

附上测试运行的截图:

可以发现长得有点像,因为每个数独都是在上个数独的基础上变化出来的,但是又不至于非常像,毕竟可能性算多了吧,不过你变着变着再变回去,那我也只能摊手了。

单元测试

最终我只会写一个很简单的函数的测试,这个类里面还只有这么一个函数,原来在Calculator里把函数写得简单点的考虑并没有什么用。当初看作业里给的例子就觉得自己的函数没有这么简单(而且例子这么简单为啥要测试),查了下Assert类的一些函数,还是不会写对复杂函数的测试。而且程序要求随机生成数独,有些生成随机数的函数,不能预料结果不是么,然后输出到控制台的函数又要怎么检验。照理应该可以测稍复杂的函数的吧,不过不管了,就先这样吧。

效能分析与改进

直接把N设置为100w,采用性能向导中的CPU采样分析了一下,附上结果相关截图:

额,和我想象的不大一样,这个“msvcp140d.dll”是什么鬼。没查到具体是什么,不过.dll又让我想起了先前纠结的静态编译问题,查了资料,改成静态编译,重新生成解决方案。新的.exe比原来的大得多,不过本来就没几KB,现在也还在KB的程度,想着问题不大,上传Github更新。于是继续CPU采样,结果如下:

这个看起来就比较符合我的预期,主要的时间花在了输出到文件上,不过这次分析的时间也比上次长,1000多秒,差不多得有三倍,静态编译的会比较慢么。输出没办法,就是很耗时,主要看下“getNewSudoku()”函数里的情况:

为了增加随机性而多交换几次数字的处理,不意外地成了里面最耗时的部分。交换次数在1-81随机,而不是固定的81,大概也算一种改进吧,感觉跑100w也不是特别久。其它改进就是代码加了点注释,函数命名比较好懂,尽量少点意义不明的magic数字,把很多的if-else换成switch-case让代码好看点吧。

说点题外话,不过也是相关的。前面用的都是CPU采样,就CPU时不时过来看下程序,收集一下数据,最后分析。还有种“检测”就是一直盯着程序,函数调用次数都清清楚楚,数据比较准确(也多),分析时间自然也长。看了点构建之法,本打算先用CPU采样,再用检测检查耗时的部分,我这个也就一个模块吧,就想着把N设小点然后再检测一遍看看。但是事情并没有这么顺利,出现了些少见的警告(没找到中文资料),就“警告 VSP2347:...”,然后“停止运行”。按网友所说以管理员身份运行VS,解决了这个又出现另一个“警告 VSP2317:...”,继续停止运行。最后并没有找到靠谱的解决方法(大概也是英语菜的关系),虽然CPU采样好像也能说明一些问题了,但是折腾了好久提下也好,说不定有人知道咋整呢。


9.10 更新: release模式下的效能分析

知道release模式没有调试信息,还会进行优化啥的,运行起来比较快,可我当初的debug和release运行的结果不一样(可能原因),debug可以,release不行,就先用debug来效能分析。

模式release,项目右键->属性->配置属性->调试->命令行参数,先设为“-c 100”,调试结果截图:

就是上面关键代码getNewSudoku出了问题,有些用不到的变量都被优化掉了(果然可以写得更优雅点吧),比如for (int i...)中的i就在其中(那要怎么改啊)。本来就是不会写得更好看,于是“项目右键->属性->配置属性->c/C++->优化->已禁用”(额,还会比debug快么),调试成功,把命令行参数改成“-c 1000000”,性能向导CPU采样模式结果如下:

看来还是比debug快(输出到文件好像变快了),于是上传Github更新,不过性能分析“检测”模式依然“警告...”,依然崩溃(摊手)。


个人软件开发流程(PSP)

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 20
▪ Estimate ▪ 估计这个任务需要多少时间 30 20
Development 开发 840 1160
▪ Analysis ▪ 需求分析 (包括学习新技术) 360 300
▪ Design Spec ▪ 生成设计文档 0 0
▪ Design Review ▪ 设计复审 (和同事审核设计文档) 0 0
▪ Coding Standard ▪ 代码规范 (为目前的开发制定合适的规范) 30 20
▪ Design ▪ 具体设计 60 120
▪ Coding ▪ 具体编码 60 180
▪ Code Review ▪ 代码复审 30 60
▪ Test ▪ 测试(自我测试,修改代码,提交修改) 180 480
Reporting 报告 60 20
▪ Test Report ▪ 测试报告 0 0
▪ Size Measurement ▪ 计算工作量 10 10
▪ Postmortem & Process Improvement Plan ▪ 事后总结, 并提出过程改进计划 50 10
合计 930 1200

稍微总结一下。果然15小时对我是不够的,基础要求都做不到,从第一次打代码失败就可以预见。PSP的实际用时其实并不准确,因为没有像表那样一个阶段一个阶段,只能估计,像生成数独的算法就是闲暇时的想法。但还是能看出一些东西,比如我在设计上投入的时间明显比打代码的时间短,这也导致了没发现第一次算法的缺陷,然后是更长的打代码时间。而且,对语言还不够熟悉,很多时候在解决低层次上的问题,也让打代码时间变长,其实看了下也就300多行而已。另外一点是越发觉得效率低下,找资料找着找着就不知道自己在干啥,回过神来已经过了好久,进展缓慢。关于改进的计划,一个是多打代码的念头更加坚定,另一个主要是提高效率。再具体一点就是打算,明确时间段的小目标,到时间就写点博客回顾收获,发现问题,适当取舍,确保进度(毕竟写博客也很耗时)。还有其它事情,附加题估计是没戏了,以上。

软工实践作业2:个人项目实战之Sudoku的更多相关文章

  1. 2018软工实践作业八之UML设计

    1. 团队信息 队名:小白吃队 成员: 后敬甲 031602409 卢泽明 031602328 蔡文斌 031602301 葛亮 031602617 刘浩 031602423 黄泽 031602317 ...

  2. 软工实践第五次作业-WordCount进阶需求

    软工实践作业(五) GitHub 作业链接 结对博客 031602240 具体分工 PSP表格 代码规范 解题思路与设计说明 爬虫使用 代码组织与内部实现设计(类图) 算法关键 实现方法 流程图 附加 ...

  3. 第二次作业——个人项目实战(sudoku)

    第二次作业--个人项目实战(sudoku) 一.作业要求地址 第二次作业--个人项目实战 二.Github项目地址 softengineering1--sudoku 三.PSP表格估计耗时 PSP2. ...

  4. 【2017集美大学1412软工实践_助教博客】团队作业10——项目复审与事后分析(Beta版本)

    写在前面的话 转眼轰轰烈烈本学期的软工实践就结束了,这个过程中想必在熬夜敲代码,激烈讨论中留下诸多回忆的同时,也收获了不少.恭喜所有团队完成了本阶段冲刺,此外,由于大家的贡献分给的都很平均,将个人贡献 ...

  5. 《软工实践》第零次作业 - 一些QA

    <软工实践>第零次作业 - 一些QA Q&A (1)回想一下你初入大学时对计算机专业的畅想 当初你是如何做出选择计算机专业的决定的? 你认为过去两年中接触到的课程是否符合你对计算机 ...

  6. 软工实践——结对作业2【wordCount进阶需求】

    附录: 队友的博客链接 本次作业的博客链接 同名仓库项目地址 一.具体分工 我负责撰写爬虫爬取信息以及代码整合测试,队友子恒负责写词组词频统计功能的代码. 二.PSP表格 PSP2.1 Persona ...

  7. 福州大学2020年春软工实践W班第一次作业

    作业描述 这个作业属于哪个课程 福州大学2020年春软工实践W班 这个作业要求在哪里 寒假作业(1/2) 这个作业的目标 建立博客.回顾,我的初心.当下和未来.学习路线 作业正文 福州大学2020年春 ...

  8. 福州大学2020年春软工实践W班第二次作业

    作业描述 这个作业属于哪个课程 福州大学2020年春软工实践W班 这个作业要求在哪里 寒假作业(2/2) 这个作业的目标 开发一个疫情统计程序 作业正文 福州大学2020年春软工实践W班第二次作业 其 ...

  9. 软工实践周六实践课安排(2017秋学期) | K 班

    软工实践周六实践课安排(2017秋学期) | K 班 周数 截止时间 工作内容 阶段成果展示形式 验收方式 备注 4之前 2017.10月前 组队 随笔(提供组队名单.组队队员的介绍--包括擅长的地方 ...

随机推荐

  1. Jenkins之使用Pyinstaller构建Python应用程序

    目录 1. 极简概述 2. Jenkins配置 2.1 安装JDK 2.2 安装Jenkins 3. 安装Docker 4. 使用PyInstaller构建Python应用程序 4.1 Fork 一个 ...

  2. 使用Java代码自定义Ribbon配置

    很多场景下,需要实现不同的微服务采用不同的策略,例如修改Ribbon的负载均衡规则等.Spring Cloud允许使用Java代码自定义Ribbon的配置. 在Spring Cloud中,Ribbon ...

  3. Java并发编程笔记之LinkedBlockingQueue源码探究

    JDK 中基于链表的阻塞队列 LinkedBlockingQueue 原理剖析,LinkedBlockingQueue 内部是如何使用两个独占锁 ReentrantLock 以及对应的条件变量保证多线 ...

  4. git第七节---git merge和git rebase

    # git merge和git rebase 都可以进行分支合并 #git merge 合并后保留记录两个分支的记录 #git rebase合并后会展示成一个分支的记录,另一个分支的提交实际生成了一个 ...

  5. 动态规划法(四)0-1背包问题(0-1 Knapsack Problem)

      继续讲故事~~   转眼我们的主人公丁丁就要离开自己的家乡,去大城市见世面了.这天晚上,妈妈正在耐心地帮丁丁收拾行李.家里有个最大能承受20kg的袋子,可是妈妈却有很多东西想装袋子里,已知行李的编 ...

  6. c# 匿名方法几种表现形式

    delegate int del(int a); static void Main(string[] args) { //匿名方法的几种表现形式 del del = delegate (int x) ...

  7. 三层+EasyUI+Ajax 提交Form表单

    源代码下载:http://download.csdn.net/download/qq_25237531/10267746

  8. Java框架之Struts2(四)

    一.ComboGrid 扩展自$.fn.combo.defaults和$.fn.datagrid.defaults.使用$.fn.combogrid.defaults重写默认值对象.数据表格下拉框结合 ...

  9. Java基础——Oracle(七)

    一.概述 pl/sql (procedural lanaguage/sql)是 oracle 在标准 sql 上的扩展 .不仅允许嵌入sql 语言,还可以定义变量和常量,允许使用条件语句和循环语句,允 ...

  10. CDN使用心得:加速双刃剑

    文章图片存储在GitHub,网速不佳的朋友,请看<CDN 使用心得:加速双刃剑> 或者 来我的技术小站 godbmw.com 本文以腾讯云平台的 CDN 服务为例,记录下在个人网站开发和公 ...