Sudoku 个人项目1
Github项目地址:Github
项目相关要求
- 随机构造出N个不重复的已解答的数独棋盘(0 < N <= 1000000)
- 在生成数独矩阵时,左上角的第一个数为:(学号后两位相加)% 9 + 1
- 相对路径生成sudoku.txt文件,结果输出到该文件
工具清单
- 编程语言:C/C++
- 编程IDE:Visual Studio 2017
- 源代码管理平台:Github
解题过程
刚看到题目的时候,是有点懵的,主要还是时间久了,对代码生疏了,也没有大佬的水准看一眼题目就有各种思路出来。思路也是慢慢才有一些的,想到的方法就是很暴力的,一个一个的判断填入,百度上说的也基本都是回溯方法。其实包括到现在,我代码的思路也是差不多的,只是在一些细节部分调整改动的多些。可能我出现的问题都不那么高大上,但都是一步步写代码的过程中所遇到的。
首先,简单的输出问题。在很low的版本的时候,就想先试试一些细节和最后数独棋盘的输出,结果棋盘输出都成了问题。这真的就只能怪自己马虎,浪费了很多不该浪费的时间,就很生气了。。。(其实这个问题根本不算是困难,但就是想写出来,当做一个提醒!因为真的很浪费时间。。)
接着,就是处理随机性的问题了。基于最先的直白暴力的做法,然后自己脑子都没拐拐弯,就直接采用了
rand()
函数来产生随机数,加点小操作来产生随机的1-9的数字,为了这个rand()
函数还要用srand(seed)
设置随机数种子。之后又是因为这个函数跟循环较上了劲,因为srand(seed)
函数不能够放在循环内(百度了解的).
考虑到没办法保证9次随机产生的1-9数能不重复的,所以次数问题卡了很久。然后就想为什么不先把1-9的数先确定(次数也就确定了),通过随机变换位置来到获得随机且不重复的数呢?所以就改用了数组arr[],初始存入9个数,然后又学习了一个好像很厉害的新函数random_shuffle(&arr[0],&arr[8])
,能够随机打乱数组(来自舍友的分享)。
到此,生成N个随机数独棋盘是没有问题了,就是在加上一些命令参数和文件输出的要求,整体就差不多完成了。
关键代码
-
定义数独棋盘为二维数组map[9][9],bool型的参数m用于待填入时的临界判断,初始为false
-
主函数外,代码中有五个函数:
void set()
赋初值函数
bool check(string str)
检查部分指令
void fileoutput()
文件输出
bool judge(int k, int t)
对待填入数字判断
void insert(int k)
填入 -
承担了比较大的任务,代码如下:
//根据判断情况确定是否填入
void insert(int k)
{
int row, col;
row = k / 9;
col = k % 9;
if (k == 81) //按格走的范围在0-80,若超过则退回
{
m = true;
return;
}
if (map[row][col] != 0)
insert(k + 1);
else
{
if (k % 18 == 0) //每个数独局面要5次随机
random_shuffle(&(arr[0]), &(arr[9])); //随机打乱数组arr[]
for (int i = 0; i<9; ++i)
{
if (judge(k, arr[i])) //允许插入
{
map[row][col] = arr[i];
insert(k + 1);
if (m)
return;
map[row][col] = 0; //如果找不到可插入的值则恢复初值0
}
}
}
}
//判断准备填入的数值是否符合要求
bool judge(int k, int t)
{
int row, col;
row = k / 9; //取行值
col = k % 9; //取列值
for (int j = 0; j < 9; ++j) //对同行异列进行判断
if (map[row][j] == t)
return false;
for (int i = 0; i < 9; ++i) //对同列异行进行判断
if (map[i][col] == t)
return false;
int a, b;
a = row / 3 * 3; //确定所在3*3九宫格的首位map[a][b]
b = col / 3 * 3;
for (int i = a; i < a + 3; ++i) //对所在3*3九宫格进行判断
for (int j = b; j < b + 3; ++j)
if (map[i][j] == t)
return false;
return true;
}
测试
1、 运行测试
- 命令行报错及正常运行测试结果截图如下:
2、效能分析
- 最初版本的时间问题就比较尴尬了,那速度不敢恭维。。。所以,在阅读《构建之法》的第二章内容后,也看到了老师对项目测试要求的一些内容。我个人觉得效能分析比单元测试简单些,就先去做了效能分析。
- 在最原始的版本,当时只测试了N=1000时的情况(当时没注意,用的是Debug版本的),截图如下:
-
上面有些东西都不知道是啥,主要就看了几个函数的情况,主要的消耗都在调用上了。
-
另外
random_shuffle()
函数相对于其他几个定义的函数,其自身的使用还是比较频繁的。我曾尝试改为生成一个数独棋盘就用同一个随机数组,通过计算发现这种情况下所能产生的不重复棋盘数仅为9!=362880个,对于1000000的测试数据来说,必然会有重复的棋盘。因此,我将调用次数改为一个棋盘需用三个随机数组,我计算的结果应该是有A39!=9!×(9!-1)×(9!-2)个数独棋盘。在保证随机数独棋盘数目足够的前提下,减少了对这个函数的使用。 -
中间改进的版本还有两个,然而效能分析的时候却无法显示数据。。。
(大概只有我这个特例吧。。。有借舍友的电脑尝试过,一样的无法显示,我们使用的都是VS 2017社区版,有让同学帮忙用VS 2015试过,也不行。。。有查过百度,却没有查到类似的问题,所以至今原因不明!) -
“无法显示数据”的情况截图如下:
测试数据N为100000(Release版本):
(因为中途有换过主题颜色,导致上下图片颜色的区别,但确实是自己的图)
因为这个问题,我基本都是通过诊断时间大概判断的。。。
最终的这个测试版本相对于之前的版本改进了输出。原先使用C++文件流的方法,之后在舍友那得知,在她的效能分析中这种输出方法占了近一半的时间,所以我也跟着做了改动。改为了更有效的重定向输入输出流的方法。
- 这个最终版本我有再发给同学再尝试效能分析,居然可以。。。可我自己的电脑上依然不行!!!所以下列图片是同学帮忙截的图:
测试数据N为100000(Release版本):
我的收获
总的来说,这次的个人项目体验总结就是很紧张刺激。其实遇到的问题有很多,包括VS和Github的使用问题,都是第一次使用,特别VS给我留下了“深刻印象”,那个效能分析无法显示数据的坑也不知道该怎么填上,有点小无奈。
这次的个人项目中的测试方面,我目前只做了效能分析(连效能分析都不能算完整)。因为时间比较赶,晚上的时间就来赶博客了(Markdown的使用还在边用边学中,加上有点调整版面的小强迫症,速度慢了些),单元测试就没弄,有点可惜。很多工具,和测试方法都是第一次从《构建之法》书中或是一些方法链接了解的。这次通过这个项目,接触了很多工具,但一个小小的个人项目也只是触及皮毛而已,接下去相信会学习到更多的。
这几天宿舍里的学习氛围极强,每个人都在努力敲敲写写,每次也都想把程序的运行速度再提高一些,再一些,每一次进步都有事一份动力,所以继续加油,然后我去再赶赶单元测试吧。。。
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 20 | 30 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 60 | 100 |
· Design Spec | · 生成设计文档 | 50 | 50 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 75 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
· Design | · 具体设计 | 75 | 100 |
· Coding | · 具体编码 | 180 | 160 |
· Code Review | · 代码复审 | 360 | 400 |
· Test | · 测试(自我测试,修改代码,提交修改) | 400 | 720 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 60 | 80 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 50 |
合计 | 1365 | 1815 |