软件工程基础大项目——数独问题
Github项目地址:https://github.com/WX78yyj/sudoku(由于自己有不爱命名的坏习惯,所以忘记命名了,结果提交的文件名称是未命名3,发现还改不了,郁闷)
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
60 |
|
Estimate |
估计这个任务需要多长时间 |
15 |
|
Development |
开发 |
400 |
|
Analysis |
需求分析(包括学习新技术) |
115 |
|
Design Spec |
生成设计文档 |
60 |
|
Design Review |
设计复审(和同事审核设计文档) |
30 |
|
Coding Standard |
代码规范(为目前的开发制定合适的规范) |
15 |
|
Design |
具体设计 |
200 |
|
Coding |
具体代码 |
210 |
|
Code Review |
代码复审 |
200 |
|
Test |
测试(自我测试,修改代码,提交修改) |
15 |
|
Reporting |
报告 |
30 |
|
Test Report |
测试报告 |
30 |
|
Size Measurement |
计算工作量 |
15 |
|
Postmortem & Process lmprovement Plan |
事后总结,并提出过程改进计划 |
15 |
|
|
合计 |
1000 |
简单说一下预计的时间安排,两个小时制定计划,估计时间什么的,其实也没分这么细,由于有很多软件没用过甚至很多东西听都没听说过,所以一开始的时候内心其实是很崩溃的,所以刚开始做的时候进度特别慢,很多时候都不知道要去做什么,就算有老师详细的规划,本来打算每天一小时,但其实有效利用时间可能不到一半。上表中有很多部分的时间都是重叠的所以真正的总用时大概在700分钟左右,再加上那么低的时间利用效率,其实很快就能完成的工作最后做了四个多星期,最后还是让舍友教我才做出来的,所以内心其实是很难受的,自己明明认真做了却还是什么都不会,真的觉得学下去没什么意思了。
刚开始拿到题目之后,哇,一头雾水。上面的各项要求完全看不懂,pass,pass。到具体问题来了,数独问题,可以,明白要干什么了,但是文件是怎么回事啊,以前完全没有涉及过。而且还要求必须用Visual Studio。虽然一直要求我们用Visual Studio,但是我之前一直都是用DEV c++也没出现什么问题,又去下载,又要适应新的编程工具,可以说很多时间在都是浪费在Visual Studio的使用学习上了。之后还有那么多不懂的,神烦。于是就决定先解决数独的问题。一开始想了想,有一些思路,但发现实现起来都很麻烦。于是先去网上收集了一些关于生成数独和解数独的方法。关于生成数独方面,方法有很多,但是很难用代码实现,而且还有一个问题,就是很难保证生成的终局是不重复的。后来在网上看到了一种简单的方法实现106种不同终局的方法。将首位固定(题目要求),剩下8个数字全排列,这样有8!=40320种不同的首行,再将首行分别往后推3,6,1,4,7,2,5,8位,就能生成一个符合条件的终局。接下来可以对每个终局进行变换,发现,将终局的竖行相互交换之后其实就是改变全排列的顺序,并不能生成新的终局,这样一来只有交换行或者3*3块了,相对于块的交换,行的交换更容易,将每个块中的三行相互交换,仍能保证生成的是数独终局,而且不会重复。由于第一行不能动。第二三行排列有2种情况,四五六有6种,七八九同理,所以每一个终局又可变换为2*3*3=72种情况。72*40320=2903040种情况,完全满足要求。
void quanpai(int x)//生成8位全排列,首位固定,x为第几位数字,每次从还没被选用的数字里选用新数字
{
if (x == 9)
{
for (int i = 1; i <= 9; i++)
Fist[m][i] = F[i];
m++;
return ;
}
else
{
int k;
for (int i = x; i <=9; i++)
{
k = F[x];
F[x] = F[i];
F[i] = k;
quanpai(x + 1);
k = F[i];
F[i] = F[x];
F[x] = k;
}
return ;
}
}
关于每个数独终局生成其他种终局的方法简单地使用循环就能够实现,但是全排列的生成确实难倒我了,最后在同学的帮助下,通过递归实现了。
关于求解数独,数独求解的方法和技巧有很多,刚开始我一直把它当作游戏,试图通过一些规律,小技巧实现数独的简单求解,但最后还是放弃了,有些技巧确实很有用,但是不是电脑容易实现的,类似于宫摒余解,数字摒除,实现起来相当麻烦。尤其是要把那些方法结合起来,而且这些方法大多数都是找所有解,而我们的要求就只要找出一组就可以了,因此采用了回溯的方法,每次发现新的空处就填写一个数字,符合条件继续,格子全满就算找到一个可行解,输出,否则改变数字,或者回溯回去改变之前的不合理部分。
void result(int i,int j)
{
if (i == 9 && j == 0)
{
flag = 1;
return;
}
if (a[i][j] != 0)
{ ans[i][j] = a[i][j];
if (j < 8)
result(i, j + 1);
else
result(i + 1, 0);
if (flag)
return;
}
else
{
for (int k = 1,x,y; k <= 9; k++)
{
for (x = 0; x <= 8; x++)
if (a[x][j] == k)
break;
for (y = 0; y <= 8; y++)
if (a[i][y] == k)
break;
if (y == 9 && x == 9)
{
for (x = (i / 3) * 3; x <= (i / 3) * 3 + 2; x++)
{
for (y = (j / 3) * 3; y <= (j / 3) * 3 + 2; y++)
{
if (a[x][y] == k)
y = x = 100;
}
}
if (x == (i / 3) * 3 + 3 && y == (j / 3) * 3 + 3)
{
a[i][j] = k;
ans[i][j] = a[i][j];
if (j < 8)
result(i, j + 1);
else
result(i + 1, 0);
if (flag)
return;
a[i][j] = 0;
}
}
}
}
}
一共用了五个函数,分别完成全排列的生成以及数独终局的生成,实现回溯以及判断是否满足规则,主函数调用这些函数,实现最终的效果,以及从文件中读取与写入文件的操作。由于没有画流程图的习惯,在写函数前通常是设想大概完成需要的步骤,初步确定后直接写,并且在写的过程中不断优化,改进,最后写出能够完成任务的代码。
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
60 |
100 |
Estimate |
估计这个任务需要多长时间 |
15 |
30 |
Development |
开发 |
40 |
100 |
Analysis |
需求分析(包括学习新技术) |
115 |
150 |
Design Spec |
生成设计文档 |
60 |
50 |
Design Review |
设计复审(和同事审核设计文档) |
30 |
50 |
Coding Standard |
代码规范(为目前的开发制定合适的规范) |
15 |
10 |
Design |
具体设计 |
200 |
290 |
Coding |
具体代码 |
210 |
250 |
Code Review |
代码复审 |
200 |
210 |
Test |
测试(自我测试,修改代码,提交修改) |
15 |
30 |
Reporting |
报告 |
30 |
60 |
Test Report |
测试报告 |
30 |
30 |
Size Measurement |
计算工作量 |
15 |
20 |
Postmortem & Process lmprovement Plan |
事后总结,并提出过程改进计划 |
15 |
20 |
|
合计 |
1000 |
1360 |