这个作业属于哪个课程 | https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494 |
这个作业的目标 | 编写程序实现数独填充 |
作业正文 | 如下所示 |
其他参考文献 | www.baidu.com |
作业地址:
https://github.com/EngulfMiss/20177701
PSP2.1 | Personal Software Process Stages | 预估耗时 | 实际耗时 |
---|---|---|---|
Planning | 计划 | 30分钟 | 1小时 |
Estimate | 估计这个任务需要多少时间 | 16小时 | 24小时 |
Development | 开发 | 6小时 | 8小时 |
Analysis | 需求分析 (包括学习新技术) | 1小时 | 3小时 |
Design Spec | 生成设计文档 | 30分钟 | 1小时 |
Design Review | 设计复审 | 1小时 | 1小时 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30分钟 | 20分钟 |
Design | 具体设计 | 1小时 | 45分钟 |
Coding | 具体编码 | 5小时 | 14小时 |
Code Review | 代码复审 | 30分钟 | 30分钟 |
Test | 测试(自我测试,修改代码,提交修改) | 30分钟 | 1小时 |
Reporting | 报告 | 2小时 | 2.5小时 |
Test Repor | 测试报告 | 30分钟 | 30分钟 |
Size Measurement | 计算工作量 | 30分钟 | 1小时 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30分钟 | 30分钟 |
合计 | 24小时 | 41小时 |
思路描述:
1.数独可以看成是一个m*m的二维数组。2.能找到这个二维数组中为0的数字,及要填的数字。
3.能够填入合适的数字,及行列不重复出现,如果有宫还有判断宫。
实现过程:
- 1.主函数main()
- 2.遍历数独函数
- 3.插入数检测函数
- 4.读取文件函数
- 5.输出函数
解题思路
条件的数填入数组后重新遍历数组,找到下一个为0的数字,填数;如果某次填数发现可以填的数大于了数独
规模数,说明这次填数失败,将当前位置还原为0,找到上一个填数位置换成下一个满足条件的数字,重新遍历
数组,如果上一个位置也超出数独规模,就修改上上次,以此类推。递归遍历。
整体流程图
主要函数:
数字检测函数checkNum() (包括检查行,列,宫)
其中宫中的判断相对与行列会稍微复杂一些,需要通过具体的切分方法来判断确定
每个点在哪个宫中,并确定各个宫的起点才好遍历宫。
/**
*
* @param maze 需要填数的数独数组
* @param i 当前位置的行
* @param j 当前位置的列
* @param x 带宫数独是如何切分的(切成多少行)
* @param y 带宫数独是如何切分的(切分成多少列)
* @return 行,列,(宫)都满足要求返回true,有一个不满足就返回false
*/
static boolean checkNum(int[][] maze,int i,int j,int x,int y) {
int num = maze[i][j];
for (int k = 0; k < len; k++) {
if (k == j) {
continue;
} else if (maze[i][k] == num) {
return false;
}
}
for (int k = 0; k < len; k++) {
if (k == i) {
continue;
} else if (maze[k][j] == num) {
return false;
}
}
if((x != 0)&&(y != 0)){
int bi = (i / (len / x)) * (len / x);
int bj = (j / (len / y)) * (len / y);
for (int k = bi; k < bi + (len / x); k++) {
for (int l = bj; l < bj + (len / y); l++) {
if ((k == i) && (l == j)) {
continue;
}
if (maze[k][l] == num) {
return false;
}
}
}
}
return true;
}
遍历插入合适数函数insertNum()
程序的核心部分,遍历数独,找0填入合适数,再递归调用本身。
直到全部填完返回true。
/**
*
* @param maze 需要遍历的数独数组
* @param i 当前位置的行
* @param j 当前位置的列
* @param x 带宫数独是如何切分的(切成多少行)
* @param y 带宫数独是如何切分的(切分成多少列)
* @param inNum 想填入的数字
* @return 全部填完返回true,填的数字超过最大可取数返回false
*/
static boolean insertNum(int[][] maze, int i, int j, int x, int y, int inNum) {
//寻找需填充的格子
while (maze[i][j] != 0) {
if (j < len - 1) {
j++;
}
else if (i < len - 1) {
i++;
j = 0;
} else {
//填充完毕
return true;
}
}
//填充尝试
for (int k = 1; k <= inNum; k++) {
maze[i][j] = k;
if (len != 3){
if (checkNum(maze, i, j, x, y)) {
if (!insertNum(maze, i, j, x, y, inNum)) {
continue;
}
return true;
}
}else{
if (cheackNum3(maze, i, j)) {
if (!insertNum(maze, i, j, -1, -1, NUM_9)) {
continue;
}
return true;
}
}
}
maze[i][j] = 0;
return false;
}
代码规范检查:
修改方法:
1.给每个常数定义成参量。
2.把自定义函数中间的短横线'_'去掉了。
3.添加作者信息。
修改后:
性能分析:
想要提高性能,减少这两个函数的使用次数肯定对程序的整体性能提升很大。
实施的改进:
本来是行,列,宫我写了3个函数来分别判断,将三个判断函数的结果相与得到最终的结果。
其实不用这么麻烦,完全可以把它们3个结合起来,因为只要有false,最终的判断结果就
会是false。那么只要把它们3写在一个判断函数中,有不重复的数字出现直接就可以返回
false了,就不用再进行其他判断了。减少判断的次数。
测试:
程序我还没有完成对n次的处理,因此n只能是一个,后续学习再完善
心得与体会
1.一开始看到题目要求命令行输入,我第一反应是想用C或C++。
刚好在B站大学自学了几天C++,就像先试一下。但过了几天计划以失败告终,原因
好像是,我动态分配的二维数组没有及时清理缓存,发生了内存溢出。
以下是一些失败的尝试
发生内存溢出后,我决定全部重来,改用Java。虽然C++失败了,可能是我想的太复杂,但思路是可行的,重新用java写的
时候,可能是休息了一下清醒了一些。好像开悟了一点点,发现不需要很复杂的定位位置。
命令行的输入还是问了别人的,文件的输入输出也是网上参考了一些资料。C++尝试的失败也
说明自己还有很多地方需要学习。很多东西都还是不懂,需要花时间去了解。
性能分析这些工具也还要去了解一下。