2020软件工程作业03
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494 |
这个作业的目标 | 数独的实现 |
作业正文 | 如下 |
其他参考文献 | baidu |
一、GitHub项目地址 https://github.com/HuyJe/20177593
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
Estimate | 估计这个任务需要多少时间 | 10 | 20 |
Development | 开发 | 300 | 300 |
Analysis | 需求分析 (包括学习新技术) | 100 | 200 |
Design Spec | 生成设计文档 | 30 | 30 |
Design Review | 设计复审 | 30 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 10 |
Design | 具体设计 | 30 | 30 |
Coding | 具体编码 | 200 | 250 |
Code Review | 代码复审 | 40 | 20 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 10 |
Reporting | 报告 | 20 | 20 |
Test Repor | 测试报告 | 20 | 20 |
Size Measurement | 计算工作量 | 10 | 5 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 10 |
合计 | 880 | 975 |
三、解题思路描述
数独题,最让我头痛的算法题,在认真读完作业要求后发现这可之前的要求有些不太一样,需要用TXT文件当做输入,然后可以在DOS窗口控制。这个题目的难度对我来说有点大,一个人并不能很好的完成编程。然后这个数独的程序,必须要弄清用什么方法去解数独。一开始我是想用简单的摒除法,然而这个在实行的时候发现工作量很大。然后我考虑使用候选法,再用搜索法辅助,遮掩确保能得出数独的解。
设计思路
主要流程
这里主要涉及两个功能,摒除法和搜索法。
先使用摒除法,如果摒除法不可继续求解的时候使用搜索,搜索之后使用摒除法探求正确性,一旦有错回溯并搜索下一个可能解。
程序流程图:
四、设计实现过程
在计算候选数是,由又不干扰到原来的数独,所以用到两个数组:一个是用来记录数独,一个是用来记录候选数。
填充数独的时候先使用摒除法base_solve()
然后删除不可能的数clear_solv_sudoku(int, int, int)
再填写唯一的数到数独中update_ques_sudoku()
最后在不能继续摒除的时候使用搜索search()
五、性能改进
反正一顿操作猛如虎,性能好像也没怎么提升(总之就是我菜)。。。
六、主要功能
采用分离实现和接口的方式。
(1)头文件sudoku.h
class sudoku
{
public:
sudoku();//读取文件,初始化数独
void printsudo();//打印数独
int solve();//求解
void outputsudoku();//输出解文件
int checksudoku();//检查数独是否正确
private:
int search();//搜索求解
int checkclear();//返回未填写的格子数
void base_solve();//摒除求解
void clear_solv_sudoku(int, int, int);//删除不可能的候选数
void update_ques_sudoku();//填写唯一候选数到数独
int ques_sudoku[9][9];//数独
int solv_sudoku[9][9][9];//候选数
};
(2)测试文件sudokutest.cpp
#include <iostream> using std::cout; using std::endl; #include <fstream> using std::ifstream; using std::ofstream; using std::ios_base; #include "sudoku.h" #include <Windows.h> int main() { //预先测试文件 cout << "Opening \"input.txt\"" << endl; ifstream infile; infile.open("sudoku.txt"); if (infile.fail()) { cout << "Fail opening \"input.txt\"" << endl; system("pause"); return 0; } infile.close(); sudoku n; if (!n.checksudoku()) { cout << "Illegal sudo!" << endl; system("pause"); return 0; } n.printsudo(); cout << endl; n.solve(); cout << "Answer" << endl; n.printsudo(); ofstream os("output.txt"); n.outputsudoku(); system("pause"); return 0; }
功能实现
自定义函数模块:
(1)填写唯一候选数到数独void sudoku::update_ques_sudoku()
void sudoku::update_ques_sudoku() { for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { int count = 0; for (int k = 0; k < 9; k++) { if (solv_sudoku[i][j][k] == 1) { count++; } } if (count == 1) { for (int k = 0; k < 9; k++) { if (solv_sudoku[i][j][k] == 1) { ques_sudoku[i][j] = k + 1; } } } } } for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (ques_sudoku[i][j]) { for (int k = 0; k < 9; k++) { solv_sudoku[i][j][k] = 0; } } } } }
(2)删除不可能的候选数void sudoku::clear_solv_sudoku(int num1, int num2, int key)
void sudoku::clear_solv_sudoku(int num1, int num2, int key) { //清理自身 for (int k = 0; k < 9; k++) { solv_sudoku[num1][num2][k] = 0; } for (int i = 0; i < 9; i++) { solv_sudoku[i][num2][key - 1] = 0;//清理行 solv_sudoku[num1][i][key - 1] = 0;//清理列 } //清理区块 if (num1 >= 0 && num1 <= 2) { if (num2 >= 0 && num2 <= 2) { for (int i = 0; i <= 2; i++) { for (int j = 0; j <= 2; j++) { solv_sudoku[i][j][key - 1] = 0; } } } if (num2 >= 3 && num2 <= 5) { for (int i = 0; i <= 2; i++) { for (int j = 3; j <= 5; j++) { solv_sudoku[i][j][key - 1] = 0; } } } ... if (num2 >= 6 && num2 <= 8) { for (int i = 6; i <= 8; i++) { for (int j = 6; j <= 8; j++) { solv_sudoku[i][j][key - 1] = 0; } } } } }
(3)摒除求解void sudoku::base_solve()
void sudoku::base_solve() { for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (ques_sudoku[i][j] != 0) { clear_solv_sudoku(i, j, ques_sudoku[i][j]); } } } update_ques_sudoku(); }
(4)搜索求解int sudoku::search()
int sudoku::search() { //搜索第一个空格 int blank_x, blank_y;//第一个空格地址 for (int i = 0, count = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (ques_sudoku[i][j] == 0) { blank_x = i; blank_y = j; count++; break; } } if (count) { break; } } //清理第一个数字此key可能性 int test_key;//第一个要测试的key int test_key_count = 0; for (int k = 0; k < 9; k++)//没有其余选择跳出 { if (solv_sudoku[blank_x][blank_y][k] == 1) { test_key = k; solv_sudoku[blank_x][blank_y][k] = 0; test_key_count++; break; } } if (test_key_count == 0) { return 1; } //记录数独 int mark_ques_sudoku[9][9]; int mark_solv_sudoku[9][9][9]; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { for (int k = 0; k < 9; k++) { mark_ques_sudoku[i][j] = ques_sudoku[i][j]; mark_solv_sudoku[i][j][k] = solv_sudoku[i][j][k]; } } } //清理该解所有可能性 for (int k = 0; k < 9; k++) { solv_sudoku[blank_x][blank_y][k] = 0; } //试填该解 ques_sudoku[blank_x][blank_y] = test_key + 1; //摒除求解 int result = solve(); //返回值判断 if (result == 1) { return 0; } //回溯 for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { for (int k = 0; k < 9; k++) { ques_sudoku[i][j] = mark_ques_sudoku[i][j]; solv_sudoku[i][j][k] = mark_solv_sudoku[i][j][k]; } } } //搜索求解 search(); return 0; }
以上是自定函数主要模块。
七、实验结果
至于3~8阶的数独,在源代码上修改一下数字和某些判断循环的代码也可以实现。
但是我没有给出3~8阶的数独的实验结果的原因是我太懒了,这工作量太太太大了。。。
八、心路历程
这也太难了。。。
在这次作业里我查了好多资料,对C++掌握不熟,所以这要我花费的时间有点多。
我也请教了别人来帮助我完成代码,这个题让我知道了一个算法就是摒除法。
摒除法:
区块摒除法是基础摒除法的提升方法,是直观法中使用频率最高的方法之一.区块摒除法分为 4 个不同的形式.1.宫对行的区块摒除:某数字在九宫格中的可填位置仅存在其中一个区块时,因为某数一定会在本区块,所以包含该区块...
摒除法包括宫区块(pointing)与行列区块(claiming)。
在基础题里,利用区块摒除可以替代一些基础解法的观察,或辅助基础解法寻找焦点。
在非基础题里,区块可以隐藏任何其他结构,简单的可以把基础解法隐藏起来,难的可以隐藏数对等等其他进阶技巧。
矩形摒除法的原理类似于组合摒除法,是专门针对某个数字可能填入的位置刚好构成一个矩形的四个顶点时使用的摒除法.
如果一个数字在某两行中能填入的位置正好在同样的两列中,则这两列的其他的单元格中将不可能再出现这个数字;
如果一个数字在某两列中能填入的位置正好在同样的两行中,则这两行的其他的单元格中将不可能再出现这个数字.
然后搜索法辅助了摒除法
网络资源能很好的辅助学习,必要时也要开口问别个。各种新东西确实让我学到了很多,比如熟悉了github的使用,熟悉了C++的基本语法等等,总的来说搜获还是蛮大的!
九、自评表
作业名称 | 2020软件工程作业03(https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494) | ||||||||||||||||
作业内容 | https://www.cnblogs.com/zihuangdeboke/p/12583704.html | ||||||||||||||||
姓名 | 学号 | 作业头 | GitHub项目地址 | CodeQualityAnaliysis分析代码 | PSP表格 | 解题思路描述 | 设计实现过程 | 改进程序 | 代码说明 | 结合构建之法谈心路历程和想法 | 总分 | ||||||
估计 | 实际 | 代码如何组织 | 关键函数流程图 | 单元测试的设计 | 找出性能瓶颈 | 改进 | 展示关键代码 | 解释思路与注释说明 | |||||||||
2 | 1 | 2 | 0.5 | 0.5 | 1 | 0.5 | 1 | 0.5 | 0.5 | 0.5 | 0.5 | 0.5 | 1 | 12 | |||
胡雨婕 | 20177593 | 2 | 1 | 1.5 | 0.5 | 0.5 | 1 | 0.5 | 1 | 0.5 | 0.5 | 0.5 | 1 | 10.5 |