软件工程第二次作业
0.相关链接
1.解题思路
- 胡思乱想
- 大致思路有两条,一个是先生成数独终盘,再矩阵转换,(感觉会比较有限,不考虑),二个是随机填数,可以按照数字填(将1填完,再填2这样),按行/列填,按九宫格填
- 难度瓶颈
- 考虑随机填数的思路,难点就是填数要随机,但是同行,同列,同格都有限制,自由又很不自由,一开始打算按照数字填,因为觉得数独,1只跟1有冲突,与其他数字,不在一个位置就好了。但是这样满盘随机放数字,就不知道怎么代码实现。
- 最终选择
- 网上看了随机的思路,最后选择按行填数,这里参考silenpy的数独终盘生成算法(java)很暴力,但是很容易实现。相同点:第一行数字无限制,1-9随机放,第二行到第九行,一个一个数字填,每次用SET存1-9,然后将同行,同列,同格数字踢出去,(可能将1-9全部踢出去了)剩余数字随机选择。这样保证当前填数正确。不同点:对无数可选情况的处理,我考虑整行重新填数,但是存在整行数字无论怎么填,都不可能正确的情况。解决方法:设置一个整行重新生成的最大限值,超过此值,整个数独重新生成。
- 改进版本
- 最后测试的时候,发现上个思路先判断再放数字太慢,SET会重复插入,删除,而且随机选数必须迭代。改进是先放数字,再判断是否正确,这里参考Swing数独游戏(二):终盘生成之随机法,PS:已经确定一位同学吐槽这个算法,一位同学貌似是吐槽这个算法,但是算法是真的,思路与算法有出入,我也被坑了。
2.设计实现
只是生成数独终盘,不考虑附加作业,就没有考虑类,只是函数。
-
版本一:
getShudu首行调用getfirst,剩余72个空格调用getone,最后调用print输出
-
版本二:
getshudu首行调用creatArray,首行数字直接等于随机生成的数组数字,第二行到第九行,依旧调用creatArray,但是不可直接填入,checkbtnArray用于判断随机生成一组数字是否可用,for循环从0到8,当前行一个一个填数,checkone判断当前位置能否从数组中取得合法数字,如果有一个位置无法取得合法数字,则此随机数组不可用。需要再次随机生成,最后调用print打印。
3.代码说明
-
版本一:
- 对首个数字处理,只需要在getfirst先将7从集合移除,放入第一个数字置,剩余数字再随机
Array[0][0] = 7; basic.erase(7);//basic为集合名称
- getone踢出同行/列/格数字后,如果集合中仍有数字,随机选择一个,如果没有数字,看当前getone调用次数times是否超过最大限制,如果没有,此行重新生成,如果超过,不做处理,getshudu会进行处理
if (basic.size() == 0) {//无数可选 if(times <= MAX_Time){ if(col/3==0){ for(int k=0;k<3;k++){ name[0].insert(Array[col][k]); } } for (int k = 0; k <= row; k++) { getone(col, k);//此行重新生成 } } }else { int num = rand() % basic.size(); set<int>::iterator it;//定义前向迭代器? int j; for (it = basic.begin(), j = 0; it != basic.end(); it++, j++) { if (j == num) { Array[col][row] = *it; //basic.erase(Array[0][i]); break; } } }
-
版本二:
- 随机生成数组,对首个数字的处理,数组btnArray,初始化放入1-9,将1与7交换,然后循环产生随机数字(1-8),将该位置数字与btnArray[1]交换,得到一组随机数字
void creatArray() { times++; for (int i = 0; i<9; i++) { btnArray[i] = i + 1; } btnArray[0] = 7; btnArray[6] = 1; for (int i = 0; i<20; i++) { int t = rand() % 8 + 1; int temp = btnArray[1]; btnArray[1] = btnArray[t]; btnArray[t] = temp; } }
- checkbtnArray
bool checkbtnArray(int row){ int judge; for(int i = 0 ;i < 9;i++){ judge = 0; for(int j = 0;j < 9;j++){ Array[row][i]= btnArray[j]; if(checkone(row,i)){//如果数字合适,填入,换下个位置 judge = 1; break; } } //数组数字选遍,仍无合适数字,则此数组不可用 if(judge == 0){ for(int k = 0;k<=i;k++){ Array[row][k]=0; } return false; } } return true; }
4.测试运行
-
命令行输入:
-
生成文件部分截图
5.效能分析以及改进
仅针对版本二,版本一无力回天了。
- 第一次改进:100万当时运行了15分钟,发现约50%时间耗费在输出上,(忘记截图,后续补上)原本输出函数
char* path = "./sudoku.txt"; // 创建文件的相对路径
ofstream fout(path);
void print(){
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
fout << Array[i][j] << " ";
}
fout << endl;
}
fout << endl;
}
看了大佬代码,用的putchar,果断换,也用printf试过,后者慢一些
freopen("./sudoku.txt", "w", stdout);
void print() {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char num = '0'+Array[i][j];
putchar(num);putchar(' ');
}
puts("");
}
puts("");
}
运行截图,100万数据:七分多钟
printf函数所占比例:
- 第二次改进,一次运行后,print函数消耗时间大大减少,但是checkone,与checkbtnArray函数比重增大。
查看代码发现:数组里面的数字如果合适,会被填入,但是下次依旧会再次被检验。
for(int j = 0;j < 9;j++){
Array[col][i]= btnArray[j];
if(checkone(col,i)){
judge = 1;
break;
}
}
将已经正确填入的数字置0,加以判断
for (int j = 0; j < 9; j++) {
if(btnArray[j]!=0){
Array[col][i] = btnArray[j];
if (checkone(col, i)) {
judge = 1;
btnArray[j] = 0;//表示已经使用过
break;
}
}
}
这样也保证同行不会有重复,checkone注释掉同行检查
/*
//同行不可重复
for (int i = 0; i < row; i++) {
if (Array[col][row] == Array[col][i])return false;
}
*/
//同格不可重复
for (int i = (col / 3) * 3; i < col; i++) {
for (int j = (row / 3) * 3; j < (row / 3) * 3 + 3; j++) {
if (Array[col][row] == Array[i][j])return false;
}
}
//同列不可重复
for (int i = 0; i < (col / 3) * 3; i++) {
if (Array[col][row] == Array[i][row])return false;
}
运行截图,100万数据,跑了六分多,快了一分多,checkone与checkbtnArray占比有所下降,但是依旧很大,其中消耗最大的函数是checkone
- 意外发现,本来构建之法上面写了,使用效能分析工具确保编译的程序是Release版本,但是当时不知道release是什么,后面发现Debug可以切换成Release,又跑了一次100万,三分多,不去想大佬程序耗时,就知足了。
6.PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 10 | 20 |
Development | 开发 | 630 | 960 |
· Analysis | · 需求分析 (包括学习新技术) | 150 | 200 |
· Design Spec | · 生成设计文档 | 10 | 0 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 10 |
· Design | · 具体设计 | 120 | 120 |
· Coding | · 具体编码 | 180 | 240 |
· Code Review | · 代码复审 | 60 | 300 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 80 |
Reporting | 报告 | 40 | 70 |
· Test Report | · 测试报告 | 20 | 60 |
· Size Measurement | · 计算工作量 | 20 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结,并提出过程改进计划 | 120 | 180 |
合计 | 680 | 1050 |