第二次作业——个人项目实战
1).项目地址
github地址:https://github.com/xiezhe124/sudoku
使用语言:c++
运行环境:WIN10
开发平台:Visual Studio Professional 2017 15.3.5
2).解题思路
其实在我看到第二次作业已经发布的时候,我的内心是极其崩溃的,因为我前一天才赶着在假期开始之前把第一次作业做完,第二次作业就已经悄无声息的来到了我的面前。在我第一次看到这个题时,这个题目给我的感觉是“应该”不难。我第一时间想到的是这个题目需要三个判断函数,用来满足填入数字的规则,即同一行无重复,同一列无重复,同一九宫格无重复。但我那时一直有一个很大的疑惑,即这三个函数如何实现一次性判断无重复。后来发现我对这个题目的理解还停留在一种“静态”的思维,即一种填完之后再判断正确性的思维当中。之后我开始通过搜索引擎查找了关于“数独”的很多题目。其中有很多方法有参考价值,如最基本的单个填写再判断。生成随机行之后做置换等等。其中有一种方法比较有趣,即利用一个填写好的数独来进行变换,得到其他数独。受上述方法启发,我决定使用先填写一个子块再填写其余块的方法来实现数独的生成。
3).设计实现
采用一个静态二维数组来实现这个9*9的宫格,其中由题目所设,宫格中的数字为1-9,且行不重复,列不重复,九宫格不重复。也就是说,每行每列每个九宫格都是1-9的组合。数独每填完一个,就循环输出一个数独组合。然后初始化棋盘。上述都是基本的步骤,最重要的步骤是填入方法。在初始化过的棋盘中,生成不重复的随机数字来填写第一个九宫格。然后循环填写后续九宫格。在填写不进去时,尝试其他随机组合,但有可能现在的组合已经是无解的,所以设置一个尝试次数用来防止程序陷入死循环中。这里将其设置为100,即尝试填写100次失败之后就更换第一个九宫格中的随机组合。
4).代码说明
主函数部分
···
int C = atoi(argv[argc - 1]);
answerout.open("sudoku.txt");
if (C >0)
{
bool back;
int TryTimes = 0;
srand((unsigned)time(NULL));
int i = 0;
while(i < C)//数独输出循环
{
back = false;
if (TryTimes > 0&&TryTimes<100)//尝试次数较少
{
InitRest(answer);
}
else if (TryTimes==0)//开始状态
{
InitAll(answer);
SubBuild(answer, 0);
}
else if(TryTimes>=100)//尝试次数大于100次后变更第一个九宫格组合
{
InitAll(answer);
SubBuild(answer, 0);
}
for (int j = 1; j < 9; j++)//尝试构建后续八个九宫格
{
bool success = SubBuild(answer, j);
if (!success)
{
back = true;
break;
}
}
if (back)//构建失败,尝试次数加一,根据尝试次数选择策略
{
TryTimes++;
}
else//构建完成,输出数独
{
for (int x = 0; x < 9; x++)
{
for (int y = 0; y < 9; y++)
{
answerout << answer[x][y] << ' ';
}
answerout << endl;
if (x == 8)
answerout<< endl;
}
TryTimes = 0;
i++;
}
}
answerout.close();
}
//错误输出
else cout << "Please input a natural number" << endl;
return 0;
···
5).测试运行
6).性能分析
使用VS自带的性能探查器中的CPU采样分析方法,输入为5000.得到如图报表
使用VS自带性能探查器中的监测分析方法,输入为1000,得到如下报表
两种分析方法使用后,可知程序在构建九宫格时所消耗的时间和资源最多。所以可以尝试减少构建九宫格所需的时间和资源,最直接的方式便是减少尝试次数,由于题目的要求只是生成指定数量的数独,对于一初始九宫格和其后续可能存在无解情况,我们可以控制其尝试次数以求获得更多的组合来进行有意义的尝试,而减少对一无解问题的尝试。还有一点便是优化“放弃”该组合并初始化的后续九宫格的退回范围,即尝试次数过多后只初始化最后两个九宫格,如不行,再初始化三个,依次类推。由于时间有限,只对减少尝试次数这一优化方法进行了探索。发现在减少尝试次数的同时,程序有可能放弃本来有效的解,尤其是在尝试次数过低时,也就是说。这个尝试次数应该存在一个近似最优的值。
下图为函数耗时和函数调用次数
7).PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 100 |
· Estimate | · 估计这个任务需要多少时间 | 60 | 100 |
Development | 开发 | 600 | 730 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 200 |
· Design Spec | · 生成设计文档 | 60 | 10 |
· Design Review | · 设计复审 (和同事审核设计文档) | 0 | 0 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 0 |
· Design | · 具体设计 | 60 | 100 |
· Coding | · 具体编码 | 200 | 300 |
· Code Review | · 代码复审 | 120 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 60 |
Reporting | 报告 | 160 | 100 |
· Test Report | · 测试报告 | 120 | 50 |
· Size Measurement | · 计算工作量 | 20 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 40 |
合计 | 820 | 930 |