项目地址
GitHub项目地址:https://github.com/julianyong/personalpro.git
PSP
PSP2.1 | Personal Software Process Stage | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
·Estimate | ·估计这个任务需要多少时间 | 60 | |
·Development | 开发 | ||
·Analysis | ·需求分析(包括学习新技术) | 600 | |
·Design Spec | ·生成设计文档 | 120 | |
·Design Review | ·设计复审(和同事审核设计文档) | 0 | |
·Coding Standard | ·代码规范(为目前的开发制定合适的规范) | 120 | |
·Design | ·具体设计 | 180 | |
·Coding | ·具体编码 | 300 | |
·Code Review | ·代码复审 | 60 | |
·Test | ·测试(自我测试,修改代码,提交修改) | 300 | |
·Reporting | ·报告 | 180 | |
·Test Report | ·测试报告 | 60 | |
·Size Measurement | ·计算工作量 | 30 | |
·Postmortem&process Improvement Plan | ·事后总结并提出过程改进计划 | 30 | |
合计 | 2040 |
思路
针对任务的第一个功能——生成N个不重复的数独终局,我最初的想法是通过回溯法暴力生成终局,但是由于最多可生成1000000个数独终局,回溯法无疑会耗费大量的时间,因此我又在网上搜索了相关的生成数独终局的方法。最终选择套用已生成的数独终局模板,通过对1~9进行全排列和数独行列之间的顺序变换生成数独终局。那么使用此方法能否保证可生成的数独终局数量大于1000000且不重复呢?
首先贴出我套用的数独模板
i | g | h | c | a | b | f | d | e |
---|---|---|---|---|---|---|---|---|
c | a | b | f | d | e | i | g | h |
f | d | e | i | g | h | c | a | b |
g | h | i | a | b | c | d | e | f |
a | b | c | d | e | f | g | h | i |
d | e | f | g | h | i | a | b | c |
h | i | g | b | c | a | e | f | d |
b | c | a | e | f | d | h | i | g |
e | f | d | h | i | g | b | c | a |
接下来生成1到9的全排列序列,并分别将a到i与1到9对应起来,由于要求数独左上角的数字固定为(8+9)%9+1 = 9,因此将i与9对应,a到h与1到8对应起来。只需生成1到8的全排列,共有:1×2×3×4×5×6×7×8 = 40320种对应方法。通过观察不难发现将数独的第2、3行互换,第4、5、6行互换,第7、8、9行互换之后数独仍然符合规则且不重复,因此接下来在一种数字-字母的对应方式下,通过行顺序变换能产生1×2×1×2×3×1×2×3 = 72种不同的数独终局。采用这种方法一共可以产生2903040(大于1000000)种不重复的数独终局,符合题目要求。
解数独则采用了回溯的方法求解。
设计实现过程
使用VS2015自动生成的类图如下:
核心代码包括生成数独终局的函数int create_Sudoku(int n)和解数独函数void solve_Sudoku(FILE *fp)。
生成n个不重复的数独终局时首先judge_Num(n)函数判断n的合法性,接下来调用int create_Sudoku(int n),函数int create_Sudoku(int n)需要一些辅助函数,比如:void Initial()初始化origin存入以9开头的1~9的全排列序列,void creatModel(MAP &m, int n)根据已有模板针对不同的全排列序列生成盘面,void print(MAP m, int flag)以盘面为单位将数独终局输入到文件。
解数独时调用solve_Sudoku(FILE *fp)比如:bool isPlace(int count, MAP &m) 判断赋值是否符合数独规则,void backtrace(int count, MAP &m) 回溯求解数独终局,void print(MAP m, int flag)以盘面为单位将数独终局输入到文件。
测试
生成数独终局,当输入合法时:
输入N不合法时:
输入的存储数独题目的文件地址不正确时
输入合法时
性能分析
生成数独终局:
由图可知在生成数独终局的过程中消耗最大的函数是向文件中输出数独终局的print()函数。
解数独:
可知占用最高的函数是回溯函数(回溯过程中需要进行输出),实际上仍是输出占用大部分时间
代码说明
生成数独终局
int create_Sudoku(int n)//生成n个不重复的终局
{
fout.open(filepath, ios::trunc);
int count = 0;
MAP m;
if (origin[0][0] == 0) {//如果origin未初始化,则对origin初始化存入全排列序列
Initial();
}
for (int i = 0; i < 40320; i++) {
memset(m.map, 0, sizeof(m.map));
creatModel(m, i);//根据字母-数字对应关系生成数独盘面
MAP backup;
for (int j = 0; j < 2; j++) {//实现行顺序变换
for (int ii = 0; ii < 9; ii++)
{
backup.map[0][ii] = m.map[general_1[j][0]][ii];
backup.map[1][ii] = m.map[general_1[j][1]][ii];
backup.map[2][ii] = m.map[general_1[j][2]][ii];
}
for (int k = 0; k < 6; k++)
{
for (int ii = 0; ii < 9; ii++)
{
backup.map[3][ii] = m.map[general_2[k][0]][ii];
backup.map[4][ii] = m.map[general_2[k][1]][ii];
backup.map[5][ii] = m.map[general_2[k][2]][ii];
}
for (int l = 0; l < 6; l++)
{
for (int ii = 0; ii < 9; ii++)
{
backup.map[6][ii] = m.map[general_3[l][0]][ii];
backup.map[7][ii] = m.map[general_3[l][1]][ii];
backup.map[8][ii] = m.map[general_3[l][2]][ii];
}
print(backup, count);//以数独盘面为单位输出到文件中
count++;
if (count == n)
{
fout.close();
return 1;
}
}
}
}
}
return 0;
}
求解数独
void solve_Sudoku(FILE *fp)//生成并输出填好的数独终局
{
fout.open(filepath, ios::trunc);
MAP m;
int tmp;
while (~fscanf(fp, "%d", &tmp)) {//从文件中读出数独题目内容
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (i == 0 && j == 0) {
m.map[i][j] = tmp;
}
else {
fscanf(fp, "%d", &m.map[i][j]);
}
}
}
backtrace(0, m);//回溯解数独
flag = 1;
}
fclose(fp);
fout.close();
}
回溯求解数独
void backtrace(int count, MAP &m) {//回溯求解数独终局
if (count == 81) {
print(m, flag);
return;
}
int row = count / 9;
int col = count % 9;
if (m.map[row][col] == 0) {
for (int i = 1; i <= 9; ++i) {
m.map[row][col] = i;//赋值
if (isPlace(count, m)) {//检查赋值是否合法
backtrace(count + 1, m);//递归进入下一层
}
}
m.map[row][col] = 0;//回溯
}
else {
backtrace(count + 1, m);
}
}
PSP
PSP2.1 | Personal Software Process Stage | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
·Estimate | ·估计这个任务需要多少时间 | 60 | 90 |
·Development | 开发 | ||
·Analysis | ·需求分析(包括学习新技术) | 600 | 360 |
·Design Spec | ·生成设计文档 | 120 | 180 |
·Design Review | ·设计复审(和同事审核设计文档) | 0 | 0 |
·Coding Standard | ·代码规范(为目前的开发制定合适的规范) | 120 | 150 |
·Design | ·具体设计 | 180 | 240 |
·Coding | ·具体编码 | 300 | 360 |
·Code Review | ·代码复审 | 60 | 30 |
·Test | ·测试(自我测试,修改代码,提交修改) | 300 | 300 |
·Reporting | ·报告 | 180 | 120 |
·Test Report | ·测试报告 | 60 | 60 |
·Size Measurement | ·计算工作量 | 30 | 30 |
·Postmortem&process Improvement Plan | ·事后总结并提出过程改进计划 | 30 | 30 |
合计 | 2040 | 1950 |
遇到的问题
在开发过程中遇到了无法创建.txt文件的问题,使用fopen打开文件时,虽然返回值显示已成功打开文件,但在文件资源管理器中却找不到对应的文件。最终选择使用ofstream打开文件解决问题。