这个作业属于哪个课程 | https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494 |
这个作业的目标 | 整一个解数独的玩意儿 |
作业正文 | 如下 |
其他参考文献 | 无 |
1、Github项目地址
https://github.com/Ayamegusa/20177717
2、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
Estimate | 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 950 | 685 |
Analysis | 需求分析 (包括学习新技术) | 60 | 40 |
Design Spec | 生成设计文档 | 0 | 0 |
Design Review | 设计复审 | 0 | 0 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 0 | 0 |
Design | 具体设计 | 30 | 30 |
Coding | 具体编码 | 600 | 420 |
Code Review | 代码复审 | 60 | 15 |
Test | 测试(自我测试,修改代码,提交修改) | 200 | 180 |
Reporting | 报告 | 70 | 85 |
Test Repor | 测试报告 | 30 | 25 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 50 |
合计 | 1050 | 800 |
3、思路描述
一拿到题目当场想到可以暴力解数独 但是又想到暴力解实在太可怕了x ...于是又去了解了关于求解数独的其他算法
最后我寻思我大概也只配写写回溯法了... emmm 然后就选择了回溯法(嗯 就是这样
4、设计实现过程
- 代码图
-
main_
文件的读取、打开操作 参数转换 数独求解以及异常处理
-
Parse
参数转换的具体实现
-
Solve
求解数独
-
PlaceNumber
填入数字(数独的核心函数
-
Exists
对各行各列以及各宫格的检查
5、性能测试与优化
- 性能测试
大部分时间消耗在找数字上面
-
已通过静态检查并消除警告
-
优化
看这架势要优化只能换算法了 (x_x
......所以优化暂时我也没有办法 orz
只能之后再考虑了(没错
6、异常处理说明
-
参数转换
缺少参数时提示缺少对应的某参数
for (int i = 1; i < argc; i += 2)
{
param.insert(std::make_pair(argv[i], argv[i + 1]));
}
if (param.find("-m") != param.end())
{
m = std::stoi(param["-m"]);
}
else
{
throw std::exception("缺m");
}
if (param.find("-n") != param.end())
{
n = std::stoi(param["-n"]);
}
else
{
throw std::exception("缺n");
}
if (param.find("-i") != param.end())
{
input = param["-i"];
}
else
{
throw std::exception("缺i");
}
if (param.find("-o") != param.end())
{
output = param["-o"];
}
else
{
throw std::exception("缺o");
}
-
文件打开
文件打开失败时给出相应提示
auto ifp = fopen(input.c_str(), "r");
if (ifp == nullptr)
{
throw std::exception("open input file failed");
}
auto ofp = fopen(output.c_str(), "w");
if (ofp == nullptr)
{
throw std::exception("open output file failed");
}
- 文件打开单元测试
TEST_METHOD(FileNotFound)
{
char* argv[] =
{
"",
"-mi",
"9",
"-n",
"1",
"-ii",
"test.i",
"-o",
"test.o"
};
try
{
main_(9, argv);
}
catch (std::exception& exception)
{
Assert::AreEqual("open input file failed", exception.what());
}
}
7、核心代码说明
大概整个求解程序的核心就是PlaceNumber函数辽(填数字
void PlaceNumber(Map& map, int m, int i)
{
//坐标是否位于map终点
if (i >= m * m)
{
throw std::exception("Finished!");
}
//当前坐标是否需要填入数字
if (map[i] > 0)
{
PlaceNumber(map, m, i + 1);
return;
}
//遍历待填数字
for (int n = 1; n <= m; ++n)
{
//待填数字是否存在
if (!Exists(map, m, n, i % m, i / m))
{
map[i] = n;
PlaceNumber(map, m, i + 1);
map[i] = 0;
}
}
}
- 流程图
(画这个流程图的时候我纠结了很久 因为平时也不太画流程图 都不知道该如何处理关于回溯以及Undo操作的体现 最后还是找tw神仙帮我解决了这个问题
8、单元测试
9x9单元测试
TEST_METHOD(Sudoku9x9)
{
Map mp
{
0,0,6,7,0,3,5,0,0,
0,0,0,0,4,0,0,0,0,
5,0,0,0,0,0,0,0,2,
9,0,0,0,0,0,0,0,7,
0,3,0,0,0,0,0,4,0,
8,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,4,
0,0,0,0,0,0,0,0,0,
0,5,9,2,6,7,3,1,0
};
Map res
{
2,4,6,7,1,3,5,8,9,
7,9,8,5,4,2,1,6,3,
5,1,3,6,8,9,4,7,2,
9,2,4,1,5,6,8,3,7,
6,3,1,9,7,8,2,4,5,
8,7,5,3,2,4,6,9,1,
1,6,7,8,3,5,9,2,4,
3,8,2,4,9,1,7,5,6,
4,5,9,2,6,7,3,1,8
};
Solve(mp, 9);
Assert::IsTrue(std::equal(mp.begin(), mp.end(), res.begin()));
}
除此之外 单元测试包含了以下项(共10个单元测试
- 3x3
- 4x4
- 5x5
- 6x6
- 7x7
- 8x8
- 无解
- 文件输入输出
- 文件不存在
9、运行
- 3x3
- 4x4
- 5x5
- 6x6
- 7x7
- 8x8
- 9x9
10、心路历程与收获
草莓毛巾卷真好次(我真的没有在做作业的时候吃甜点 真的
看到题目的时候就觉得巨麻烦 但迫于作业还是要着手去写 后来关于参数处理的那部分操作去请教了tw神仙 最后总算是赶在ddl之前完成了作业 整个作业大部分时间都花在了debug 老师在博客作业里的要求非常多也非常杂 一项项完成下来也不知道自己到底有没有完成所有需要完成的东西(希望我没有落下什么重要的部分
然后其实不管是什么事情大概只要着手去做好像也不会有自己想象中那么难 真正开始去做才是最难的x
(最后 感谢tw神仙提供的帮助 下次窝请你次毛巾卷