数独——分析、设计与测评
1、github地址:https://github.com/kinglc/sudoku1943
2、解题思路:项目包括 输入与输出(文件读写、命令行),生成不重复终局,解数独三个部分。
(1)输入与输出
文件读写和命令行虽然都不熟,但稍微百度就完全可以解决。
(2)生成不重复终局
先随便写了一个数独终局
1 2 3 4 5 6 7 8 9
4 5 6 7 8 9 1 2 3
7 8 9 1 2 3 4 5 6
2 3 4 5 6 7 8 9 1
5 6 7 8 9 1 2 3 4
8 9 1 2 3 4 5 6 7
3 4 5 6 7 8 9 1 2
6 7 8 9 1 2 3 4 5
9 1 2 3 4 5 6 7 8
只需要生成一定数量的数独终局从简单的入手就好,上图第一行是随手写,后8行都可以看做根据第一行左移得到,第一行全排列有8!=40320种,然后交换2-3(因为数据只要求1e6,可以不 考虑),4-6,7-9行,再乘3!×3!,结果略大于1e6。
(3)解数独
本来试着看了一下dancing links算法,但十字链表似乎好麻烦的样子,于是老老实实用dfs暴力写完了。
3、代码设计
(1)函数功能
int main(int ,char *) :获取命令行参数,进入不同处理函数
void createFinal():参数为-c时:生成终局存入数组,并调用output(int,int)函数
void workout(int):参数为-s时:通过深搜得出终局,用check(int,int)函数判断,并调用output(int,int)函数
bool check(int,int):判断在该处填的数字是否合理
void output(int,int):根据参数所代表的输出顺序输出终局
(2)关键函数
生成终局:
void createFinal() { int tmp[9] = { 8,9,1,2,3,4,5,6,7 };//tmp表示第一行数字 int i, j, k, moveleft[8] = { 3,6,1,4,7,2,5,8 }; //moveleft表示2-9行在第1行基础上整体左移位数 for (i = 0; i < 40320; i++) //8!=40320 { memcpy(numBoard[0], tmp, sizeof(tmp)); //将第1行左移生成2-9行 for (j = 0; j < 8; j++) for (k = 0; k < 9; k++) numBoard[j + 1][k] = numBoard[0][(k + moveleft[j]) % 9];//初始图完成 //调换4-6,7-9行 for (j = 0; j < 6; j++) for (k = 0; k < 6; k++) { output(j, k); if (!--testnum) return; } next_permutation(tmp + 1, tmp + 9);//重置第一行 } return; }
解数独:
void workout(int pos)//深搜 { if (pos == 81) { output(0, 0); flag = 1; //标记是否输出过 return; } int i, x, y; x = pos / 9; y = pos % 9; if (!numBoard[x][y]) { for (i = 1; i <= 9; i++) { if (cnt[i - 1] == 9) continue; numBoard[x][y] = i;//填充数字 cnt[i - 1]++; if (check(pos, i)) workout(pos + 1); if (flag) //该数独已解完 return; cnt[i - 1]--; numBoard[x][y] = 0; } } else workout(pos + 1); }
(3)单元测试
命令行参数判定:-c, -s, -abc
运行情况判定:-c 1, -c 1000, -c 1000000, -s 文件路径(其中包含1、1000、1000000个用例)
4、运行分析
(1)生成1000个数独终局
(2)解1000个20-40个空格的数独
(3)优化
在生成终局中,本来是用了三次全排列分别对第一行8个数字、第4-6行、7-9行处理,后优化为用print[6][3]={ 0,1,2,0,2,1,1,2,0,1,0,2,2,1,0,2,0,1 }存入全排列数,在output函数中处理输出。
void output(int mid,int last) { int i, j, k, row; int order[3] = { 0 }; order[1] = mid; order[2] = last; for (i = 0; i < 3; i++)//输出第i个三行 for (j = 0; j < 3; j++) { row = print[order[i]][j] + 3 * i; for (k = 0; k < 8; k++) output_file << numBoard[row][k] << " "; output_file << numBoard[row][8]<<endl; } output_file << endl; }
5、PSP
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
30 |
30 |
·Estimate |
·估计这个任务需要多少时间 |
30 |
30 |
Development |
开发 |
1080 |
1080 |
·Analysis |
·需求分析(包括学习新技术) |
120 |
120 |
·Design Spec |
·生成设计文档 |
120 |
90 |
·Design Review |
·设计复审 |
30 |
30 |
·Coding Standard |
·代码规范 |
30 |
30 |
·Design |
·具体设计 |
180 |
180 |
·Coding |
·具体编码 |
540 |
540 |
·Code Review |
·代码复审 |
30 |
30 |
·Test |
·测试 |
30 |
60 |
Reporting |
报告 |
90 |
90 |
·Test Report |
·测试报告 |
40 |
40 |
·Size Measurement |
·计算工作量 |
10 |
10 |
·Postmortem&Process Improvement Plan |
·事后总结并提出过程改进计划 |
40 |
40 |
|
合计 |
1200 |
1200 |