Github项目地址
解题思路描述:
心路历程
因为我是后来才补选的实践,所以在做题之前多少从周围有选课的同学口中了解到题目大概是要生成数独。所以我选课以后没有先去了解题目的具体要求,而是想说先把生成数独的算法写出来,再来满足题目里面其他的要求。我先是去网上找了一下数独的定义,然后发现用简单的递归就能实现数独的生成。因为时间紧迫加上能力有限,所以我没有再去花时间想别的算法,在基础算法实现后我才开始了解题目的具体要求,发现需要用到VS,git之类的工具,我在学习工具的使用上也花了不少的时间。。
解题思路
我的思路很简单,就是从第一个格开始,每个格子都依次尝试填入1~9,然后判断填入的数字是否满足数独的要求(行、列、宫都不重复),如果不行,就返回上一格,如果可以就填入该数字,然后开始尝试下一个格子。当填入数独的最后一格时输出这个数独。这样的话可以保证在一定数量里不会输出重复的数独(理论上讲),但是效率比较低。至于输出数独的个数问题,我定义了一个参数,在每次输出数独时记录输出的个数,等到个数满足要求时,return。
设计实现:
因为我现在比较擅长的就是c++,所以我选择用c++来编写这个程序。因为时间有限,所以只对代码进行了简单的安排。代码一共分为四个部分:主函数、生成函数、判断函数、输出函数。主函数用于判断命令行输入的格式是否正确,是的话则获取对应的参数,并调用数独生成函数,在数独生成函数中,每尝试填入一个数字时,调用一次判断函数,当数独填满时调用输出函数输出数独。
- 关系图
- 生成函数流程图
代码说明:
这个程序中关键的部分便是生成函数和判断函数,下面是两个部分的代码介绍。
- 生成函数(用递归实现数独的生成)
首先判断是否已经输出足够个数的数独,是的话return,不是则继续生成数独。然后将数字从1到9依次填入对应的格中,并且调用check函数进行判断,若该数字满足条件则将该格对应的行、列、宫标记数组对应的数字进行标记,进入下一格,不满足则填入下一个数字,当1到9都不符合条件时将此格清零,返回上一步。在最后一个填入满足条件的数字后输出对应的数独。
void setsudo(int h, int l){//生成数独
char c;
int s = 0;
int g = h / 3 * 3 + l / 3;
for (c = '1';c <= '9';c=c+1) {
if (max1>min1) {//数独个数足够后返回
return;
}
sudo[h][l] = c;
s = (int)c - 48;
if (check(h, l)) {//判断该数是否符合条件
hang[h][s] = 1;//对该数字进行行标记
lie[l][s] = 1;//对该数字进行列标记
gong[g][s] = 1;//对该数字进行宫标记
if (h == 8 && l == 8) {//数独填满后输出
outsudo();//调用输出函数
}
if (l < 8) {
setsudo(h, l + 1);//不是该行最后一格,则填该行下一格
}
else {
if(h < 8){
setsudo(h + 1, 0);//是该行最后一格,则填下一行的第一格
}
}
//清除对应标记
hang[h][s] = 0;
lie[l][s] = 0;
gong[g][s] = 0;
}
}
sudo[h][l] = '0';//该格清零
}
- 判断函数(用于判断该数字是否满足数独条件)
如果被填入的数字在所在的行、列、宫中被标记过,则填入后不满足数独条件,返回false,没有被标记过则可以填入,返回true。
bool check(int h, int l){
char c = sudo[h][l];
int k = (int)c - 48;
int g = h / 3 * 3 + l / 3;
if (hang[h][k] == 1) {//判断该行是否有重复
return false;
}
if (lie[l][k] == 1) {//判断该列是否有重复
return false;
}
if (gong[g][k] == 1) {//判断所在的宫是否有重复
return false;
}
return true;
}
测试运行:
- 运行截图
性能分析:
分析
在进行性能分析之前我没想到输出方式对时间的影响如此巨大,我最开始使用的是C++的文件输出,结果在VS上的性能探察器上面跑了八分钟(哭),后面尝试了C语言的文件输出方式,并且把数独数组由整型改为字符型,使时间缩短很多。改完输出方式后进行的第一次性能分析截图如下,发现在check函数上花费的时间比较多,于是我对其进行了改进。(两次测试的数据量为1,000,000个数独,测试条件为Release,x86)
改进check函数后的性能分析截图如下,改进后的效果很明显,check函数的时间缩短了一半。输出1000000个数独所用的时间缩短了两秒以上。思路
首先是输出方式,因为c++文件输出方式较为复杂,于是我将输出方式改为了c的输出,并且把数组改成了字符型,这大大的缩短了程序运行的时间。其次便是check函数的改进,一开始check函数的思路是利用三个循环,依次判断行、列、宫是否存在重复,后面发现这样循环次数过多,效率较低,于是我就改为使用三个数组进行标记,这样每次check只需要三个判断就能完成,不过这样的话就需要较多的空间(相比循环多了三个二维数组的空间)。
PSP表格:
|
|
| |
---|---|---|---|
Planning | 计划 | 40 | 60 |
· Estimate | · 估计这个任务需要多少时间 | 40 | 60 |
Development | 开发 | 960 | 940 |
· Analysis | · 需求分析 (包括学习新技术) | 180 | 230 |
· Design Spec | · 生成设计文档 | 60 | 50 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 50 |
· Design | · 具体设计 | 120 | 100 |
· Coding | · 具体编码 | 180 | 150 |
· Code Review | · 代码复审 | 60 | 80 |
· Test | · 测试(自我测试,修改代码,提交修改) | 240 | 220 |
Reporting | 报告 | 220 | 200 |
· Test Report | · 测试报告 | 150 | 120 |
· Size Measurement | · 计算工作量 | 30 | 40 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 40 |
合计 | 1220 | 1200 |