软件工程实践2017第二次作业
软件工程实践2017第二次作业
1.Github项目地址
项目地址为:https://github.com/GannonOne/sudoku
2.开始前用PSP表格预估在程序各个模块的开发上耗费的时间
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | |
· Estimate | · 估计这个任务需要多少时间 | 20 | |
Development | 开发 | 1020 | |
· Analysis | · 需求分析 (包括学习新技术) | 240 | |
· Design Spec | · 生成设计文档 | 60 | |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | |
· Design | · 具体设计 | 90 | |
· Coding | · 具体编码 | 240 | |
· Code Review | · 代码复审 | 120 | |
· Test | · 测试(自我测试,修改代码,提交修改) | 180 | |
Reporting | 报告 | 120 | |
· Test Report | · 测试报告 | 60 | |
· Size Measurement | · 计算工作量 | 20 | |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | |
合计 | 1160 |
3.解题思路
一开始拿到题目的时候我的想法是:既然是随机生成数独,有没有可能利用C++自带的随机数来生成?比如每次生成一个数然后判断接下来生成的同一行或者同一列的其他数,使得它不能与之前的数相同。一番考虑之后我认识到数独数量要求是100w,按我这个方法效率是太低太低了。之后我又想到有没有可能一开始找到一个原始数独输出,然后每次改变其中的一小部分,使得在不违反数独的规则下不断地生成不同的数独然后输出呢?然后我发现,得到一个原始数独后,把它分割成为9行3列,每个单元格包含该横向的3个数字。于是,①第2,3行可以交换,第4,5,6行可以相互交换,第7,8,9行业可以相互交换。②各列的第1,4,7行可以随意交换,2,5,8行可以随意交换,3,6,9行也可以随意交换。(因为题目要求所以此处第1列第1行不能参与交换)另外两列同理。所以就得出了仅凭这些交换可以产生2亿+个不同的数独,不过鉴于我的这个方法,最后有一个缺陷就是在输出完指定数量的数独之后必须得用exit(0)来结束(我一直没有想到办法)。这就是我的解题思路。
4.设计实践过程
这是流程图:
我的代码只有一个类,类中一共有上述6个函数。按我的解题思路,每次生成数独的顺序是一样的,所以这就导致了只能完成不重复但是不能实现随机。于是我使用random()函数来实现随机性,然后用changeLong()函数配合changeNo()函数改变9行的顺序,利用changeShort()函数和changeArr()函数交换各列上的单元格,最后用output()函数输出。(具体见代码说明)
5.代码说明
①random()函数实现随机:
int Soduku::random()
{
int random[16];//不包含第一列第一行交换,所以2*8=16
int x = 2;
int y = 1;
srand(unsigned(time(0)));
for (int i = 0; i < 16; i++)
{
random[i] = rand() % 3;//产生随机数0,1或者2
}
for (int j = 0; j < 16; j=j+2)
{
if (random[j] != random[j + 1])
{
changeArr(random[j], random[j + 1], x, y);//交换单元格
}
x++;
if (x == 4)
{
y++;
x = 1;
}
}
return 0;
};
利用rand函数产生0,1和2中的随机数保存于数组中用于交换。(例如:random[0] = 1, random[1] = 2, 则表示第一列第4行与第7行交换,以此类推)若是数组前后两个元素相等则跳过交换进入下一个交换。
②changeNo()函数实现换行:
int Soduku::changeNo(int a, int b)
{
int n = showNum[a];
showNum[a] = showNum[b];
showNum[b] = n;
return 0;
};
changeNo()函数非常的简单,换行就是改变数独行的显示顺序,所以我用一个数组showNum[]记录行的显示顺序,调用此函数时改变showNum[]数组的元素值实现行的顺序的改变。
③changeLong()函数实现交换行:
int Soduku::changeLong(int a)
{
//递归嵌套
switch (a)
{
case 0:
changeLong(1);
changeNo(1, 2);//交换行
changeLong(1);
break;
case 1:
changeLong(2);
changeNo(4, 5);
changeLong(2);
changeNo(3,5);
changeLong(2);
changeNo(4, 5);
changeLong(2);
changeNo(3, 5);
changeLong(2);
changeNo(4, 5);
changeLong(2);
break;
case 2:
changeShort(0, 1);//交换7,8,9行时,调用进入改变单元格的changeShort()函数
changeNo(7, 8);
changeShort(0, 1);
changeNo(6, 8);
changeShort(0, 1);
changeNo(7, 8);
changeShort(0, 1);
changeNo(6, 8);
changeShort(0, 1);
changeNo(7, 8);
changeShort(0, 1);
break;
default:
break;
}
return 0;
};
(其实在这一版代码之前我还有一版代码,因为那一版代码我尝试利用函数指针把函数抽象化,结果反而把自己绕乱了,很痛苦。所以这一版代码我没有再尝试函数抽象化。)
这个函数使用了递归调用(?)的思想(这个算法中基本就是以这个思想写的),将实现行的交换这个行为分段,使用changeNo()函数交换2,3行时再次调用这个函数本身(改了参数),使得继续去交换4,5,6行,以此类推,继续交换7,8,9行,之后就是调用了changeShort()函数接着交换单元格(因为changeShort()函数的实现方法与此函数差不多所以就不贴出来了)。
6.测试运行
输入正常时:
输入错误时:(不知道英文有没有写对...)
7.改进思路
这是跑10000个数独的性能图:
其中changeShort()函数耗时最多。
因为刚拿到题目的时候没有什么花样比较多的想法,就是想到了就直接一条路走,所以即使明白了这个方法不够好还是硬着头皮走下去了......这道题目我一共写了有两个版本:
①初始数独矩阵是init[27][3],即每个单元格是竖的3个元素。changeLong()和changShort()函数并不是直接调用,而是通过一个名为changeAll(),参数包含一个函数指针的抽象化函数来使用的。同时在单元格交换的方法里,3个单元格一共有6种交换,每次交换完之后再通过交换把它还原为交换前的状态。这个版本有几个问题:首先,若每个单元格是竖的3个元素那么在最后输出时比较麻烦;其次,使用抽象化函数容易搞混对各个函数的调用,以及为函数产生不必要的参数;最后,每次交换完之后复原会使得效率下降。(这个版本代码没有保存下来也没有git...)
②修改初始数组为init[9][9],每个单元格为横向三个,交换以及输出比较方便简单。函数直接调用,不使用抽象化函数,优点是简单易懂,缺点是代码冗长。而6种交换通过观察之后,发现了规律:第2行和第3行交换,之后第1行和第3行交换......重复这两种交换就可以很简单的得到6种结果。
8.用PSP表格记录实际花费时间
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 30 |
Development | 开发 | 1020 | 1160 |
· Analysis | · 需求分析 (包括学习新技术) | 240 | 270 |
· Design Spec | · 生成设计文档 | 60 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
· Design | · 具体设计 | 90 | 120 |
· Coding | · 具体编码 | 240 | 330 |
· Code Review | · 代码复审 | 120 | 90 |
· Test | · 测试(自我测试,修改代码,提交修改) | 180 | 240 |
Reporting | 报告 | 120 | 130 |
· Test Report | · 测试报告 | 60 | 90 |
· Size Measurement | · 计算工作量 | 20 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 30 |
合计 | 1160 | 1320 | |
算法一直以来都是我的弱项,完成这个作业对我来说很吃力了...... |