2019软件工程第三次作业

数独游戏

戳这里进入Github项目
第一眼看到要做数独的时候,脑海里的第一反应就是用深搜。现在好了,确定了算法,接下来就是要去实现它,可是对于将近半年没写过深搜的我要写一个如此经典的深搜还是有些难度(花了一个下午写bug改bug,弱鸡实锤)。

psp表格
PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 10min 10min
Estimate 估计这个任务需要多少时间 25h 27h
Development 开发 5h 5h
Analysis 需求分析 (包括学习新技术) 30min 30min
Design Spec 生成设计文档 1h 45min
Design Review 设计复审 1h 1h
Coding Standard 代码规范 (为目前的开发制定合适的规范) 2h 3h
Design 具体设计 30min 15min
Coding 具体编码 4h 4h
Code Review 代码复审 5h 5h
Test 测试(自我测试,修改代码,提交修改) 1h 1h
Reporting 报告 3h 3h
Test Report 测试报告 30min 30min
Size Measurement 计算工作量 30min 30min
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 1h 2h
合计 25h 27h

由于之前没有做计划评估的习惯,所以对这些工作流程的时间观念还是有些差。第一次做出了计划,实施完之后发现还是有些高估了自己,实际用时超出了预计。

计算模块接口的设计与实现过程

对于这个题目首先想到的就是深搜+回溯的算法,算法复杂度约O($n^{2n}$),我打算将大体结构分为一个求解数独的函数、一个查找当前位置可能存在的解的search()函数用于在数独求解过程中,当查找不到解时进行回溯操作然后再继续搜索…

先是头文件和全局变量:

#include "stdafx.h"
#include<iostream>
#include<fstream>
#include<string.h>
#include<math.h>
using namespace std;
int mark[10][10][10] = { 0 };
int n_size = 0;
int sgle[10][10][10] = { 0 };
int sudo[10][10] = { 0 };
bool success = false;
int times;

以下为查找可能存在的解的函数:

bool search(int x, int y)//查找当前位置可能存在的解{
for (int i = 0; i < n_size; i++) {mark[x][y][sudo[i][y]] = 1;//判断列
}
for (int i = 0; i < n_size; i++) {mark[x][y][sudo[x][i]] = 1;//判断行
}
if (n_size == 4 || n_size == 6 || n_size == 8 || n_size == 9) {if (n_size == 4 || n_size == 9) {int n = (int)sqrt(n_size);int k1 = x / n;int k2 = y / n;k1 *= n;k2 *= n;for (int i = k1; i < k1 + n; i++)for (int j = k2; j < k2 + n; j++) {mark[x][y][sudo[i][j]] = 1;}}else if (n_size == 6) {int k1 = x / 2;int k2 = y / 3;k1 *= 2;k2 *= 3;for (int i = k1; i < k1 + 2; i++)for (int j = k2; j < k2 + 3; j++) {mark[x][y][sudo[i][j]] = 1;}}else if (n_size == 8) {int k1 = x / 4;int k2 = y / 2;k1 *= 4;k2 *= 2;for (int i = k1; i < k1 + 4; i++)for (int j = k2; j < k2 + 2; j++) {mark[x][y][sudo[i][j]] = 1;}}
}
for (int i = 1; i <= n_size; i++) {if (mark[x][y][i] == 0) {return true;}
}
return false;
}

其中我将这个search函数设置为bool类型,目的是查找到当前格不存在解的时候就返回false告诉数独求解函数,使求解函数进行回溯从而尝试其他可能存在的解。

以下为数独求解函数::

void sudoku(int x, int y, char*o) //深度优先搜索,数独求解函数{
for (int i = 1; i <= n_size; i++)
{mark[x][y][i] = 0;
}
if (x == n_size) {success = true;ofstream InFire(o, ios::app);for (int i = 0; i < n_size; i++) {for (int j = 0; j < n_size; j++)InFire << sudo[i][j] << ' ';InFire << endl;}InFire << endl;
}
else if (x < n_size&&y < n_size && !sudo[x][y]) {if (search(x, y)) {for (int k = 1; k <= n_size; k++) {if (!mark[x][y][k]) {sudo[x][y] = k;if (y < n_size - 1) {sudoku(x, y + 1, o);}else if (x < n_size) {sudoku(x + 1, 0, o);}sudo[x][y] = 0;}}}elsereturn;
}
else if (x < n_size&&y < n_size&&sudo[x][y]) {if (y < n_size - 1) {sudoku(x, y + 1, o);}else if (x < n_size) {sudoku(x + 1, 0, o);}
}
}

比较经典的深度优先搜索题目(如开头所说,改了好久的BUG)这里就不在赘述了。我用了一个bool变量success来判断数独是否有解,由于之前作业上写明数独确保仅有一个解所以我在程序找到一个解以后就通过success的值判断退出,后来想要尝试求解多解数独,于是将if(x==n_size)里的return;删除,这样就求出了数独的多解。

接下来就是主函数:

int main(int argc, char*argv[]) {
FILE *fp;
char *file_path = { "./" }, *output = { "./" };
for (int i = 0; i < argc; i++)//读取命令行参数(这里借鉴了李承泽同学的方法)
{if (strlen(argv[i]) == 1){if (i == 2)n_size = atoi(argv[i]);if (i == 4)times = atoi(argv[i]);}if (i == 6)file_path = argv[i];if (i == 8)output = argv[i];
}
fopen_s(&fp,file_path, "r");
if (fp == NULL)return -1;
for (int t = 1; t <= times; t++)//读入盘面
{for (int i = 0; i < n_size; i++) {for (int j = 0; j < n_size; j++)fscanf_s(fp, "%d", &sudo[i][j]);}cout << endl;single();//判断初始盘面中存在的唯一解ofstream InFire(output, ios::app);InFire << "盘面" << t << ":" << endl;sudoku(0, 0, output);if (!success)InFire << "No answer!!" << endl;success = false;//完成一次盘面计算后数据重新初始化memset(sudo, 0, sizeof(sudo));memset(mark, 0, sizeof(mark));memset(sgle, 0, sizeof(sgle));
}
return 0;
}
优化

能不能在现有的基础上进一步优化算法呢?如果在现有的盘面上有的格子有且仅有一个解,则直接将唯一解填入,数独是可以产生连锁反应的,一个地方填入唯一解就可能使得其他地方所存在的可能解的数量减少,从而大大减少求解时间。于是我写了一个判断地方格唯一解的函数。

void single() {
int count = 0;
for (int i = 0; i < n_size; i++) {for (int j = 0; j < n_size; j++) {for (int k = 0; k<n_size; k++)//判断行if (sudo[k][j] && !sgle[i][j][sudo[k][j]]) {count++;sgle[i][j][sudo[k][j]] = 1;}for (int k = 0; k < n_size; k++) //判断列if (sudo[i][k] && !sgle[i][j][sudo[i][k]]) {count++;sgle[i][j][sudo[i][k]] = 1;}if (n_size == 4 || n_size == 6 || n_size == 8 || n_size == 9)//判断宫 {if (n_size == 4 || n_size == 9) {int n = (int)sqrt(n_size);int k1 = i / n;int k2 = j / n;k1 *= n;k2 *= n;for (int k = k1; k < k1 + n; k++)for (int l = k2; l < k2 + n; l++)if (sudo[k][l] && !sgle[i][j][sudo[k][l]]) {count++;sgle[i][j][sudo[k][l]] = 1;}}else if (n_size == 6) {int k1 = i / 2;int k2 = j / 3;k1 *= 2;k2 *= 3;for (int k = k1; k < k1 + 2; k++)for (int l = k2; l < k2 + 3; l++)if (sudo[k][l] && !sgle[i][j][sudo[k][l]]) {sgle[i][j][sudo[k][l]] = 1;count++;}}else if (n_size == 8) {int k1 = i / 4;int k2 = j / 2;k1 *= 4;k2 *= 2;for (int k = k1; k < k1 + 4; k++)for (int l = k2; l < k2 + 2; l++)if (sudo[k][l] && !sgle[i][j][sudo[k][l]]) {sgle[i][j][sudo[k][l]] = 1;count++;}}}if (count == n_size - 1)\\即只剩余一个解 {for (int k = 1; k <= n_size; k++){if (!sgle[i][j][k] && !sudo[i][j]) {sudo[i][j] = k;break;}}}count = 0;}
}

现在来将改良版与初始版本进行对比:

以下是没有加入single()函数的性能测试结果:

以下是加入single()函数之后的性能测试结果:


可以看出加入single()函数后程序CPU占用增加但是运行的时间成功减少,程序在一定程度上得到了优化。

样例测试结果

首先是九宫格的求解:

接下来就是3、4、5、6、7、8宫格的求解结果辽。先附上cmd运行图例:

下面是数据集和程序得出的数据集解:

(在这里真的要感谢助教大大辛苦做的数据集,阿里嘎多)

最后来总结一下

之前大一大二没有那么注重代码的规范问题,遇到了编译成功后编译器提出的警告也是选择性无视2333,都是一切以编译成功,能正确运行就“大功告成”了,这是与现在最大的区别之处,通过这次的作业我也学到了不仅要确保代码正确运行,提升代码的鲁棒性也是同样重要的,这在以后的学习或是工作过程中都是不可或缺的环节。

转载于:https://www.cnblogs.com/yeqiyi/p/11585067.html

2019软件工程第三次作业相关推荐

  1. 软件工程第三次作业——软件质量保证鄙见

    阅读教材第14章及课后参考文献  写一篇关于软件质量保障的博文 参考文献: 两种不同的声音(1)https://coolshell.cn/articles/6994.html (2)https://w ...

  2. 北航2022软件工程第三次作业——结对编程(最长英语单词链)

    软件工程第三次结对编程作业 项目 内容 这个作业属于哪个课程 北京航空航天大学2022春季软件工程(罗杰 任健) 这个作业的要求在哪里 结对编程项目-最长英语单词链 我在这个课程的目标是 学习软件工程 ...

  3. 软件工程(2019)第三次作业

    一.作业题目描述 最大连续子数组和: 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],-,a[n],求该序列如a[i]+a[i+1]+-+a[j]的子段和的最大值.当所给的整数均为负 ...

  4. test软件工程第三次作业

    零.前言 本次作业要求个人编写程序,截止日期2019年9月25日23:00. 请先阅读<构建之法>第一章至第三章的内容,并在下方作业里体现出阅读后的成果.特别是第2章中的效能分析及个人软件 ...

  5. 软件工程第三次作业(微软小娜案例分析)

    第一部分 调研, 评测 1.上手体验  我使用的是win10系统,所以自带Cortana,之后又在手机上下载了安卓版本.第一感受不管是在电脑上还是在手机上都比较流畅,优化的比较好.电脑上使用也比较方便 ...

  6. 2017年软件工程第三次作业-2效能分析

    要求0 以 战争与和平 作为输入文件,重读向由文件系统读入.连续三次运行,给出每次消耗时间.CPU参数       首先,我下载ptime.exe,不知道什么原因我下载下来以后运行老出现闪退现象.一直 ...

  7. 软件工程第三周作业:微软必应词典案例分析

    0x01 :微软必应词典案例分析 0x0104 :微软必应词典功能性BUG说明       0x010404 : BUG – 1 – 模块功能未实现 运行环境或平台 iOS 9.0.1 必应词典软件版 ...

  8. 现代软件工程第三周作业——Word Frequence

    本博文是现代软件工程第二次结对编程Word Frequence(作业要求)的总结,本项目的源码在这里 how you collaborate: working separately? pair pro ...

  9. 软件测试2019:第三次作业

    一.单元测试的任务有哪些? 单元测试处于软件测试初期阶段,是对软件基本组成单元进行的测试,而且软件单元是与程序的其他部分相隔离的情况下进行独立的测试: 任务主要包括对单元功能.逻辑控制.数据和安全性等 ...

最新文章

  1. 实战:人脸识别的Arcface实现 | CSDN博文精选
  2. ubuntu 安装 Terminator
  3. Rabbit的Windows安装
  4. 语言zzuli链表遍历_趣味图解算法之链表
  5. 使用IDEA 创建SpringBoot项目
  6. 信息与数据科学大会征文通知
  7. Java包的命名规则
  8. 美的集团:董事长减持两千万股套现13亿属个人资产配置需要
  9. JavaScript:判断当前浏览器是否为微信浏览器
  10. one loop per thread
  11. 怎样用html播放喜马拉雅音频文件,如何将喜马拉雅音频文件导出mp3,教你一键完成操作...
  12. 测试用例设计方法--正交表法(工具allpairs)
  13. android 华为 imei,华为手机怎么查看IMEI码?华为手机查询IMEI串号两种方法,华为imei...
  14. Horizon 桌面用户会话10h后超时断开
  15. 您的php似乎没有安装运行wordpress所必需的mysql扩展_“您的 PHP 似乎没有安装运行 WordPress 所必需的 MySQL 扩展”处理方法...
  16. 迅为4418开发板-驱动-以模块的方式编译内核驱动
  17. 一个清华保送生妈妈对竞赛的感受,自主招生家长都要看看!
  18. Ubuntu查看Cuda是否全部安装成功
  19. html格式错误检测,HTML格式错误
  20. 匿名内部类会导致内存泄露

热门文章

  1. 如何用 C 语言写一个生日蛋糕?
  2. 小米盒子增强版ROOT以及实现通过wifi进行adb
  3. php 星际争霸 面向对象,星际争霸之php面向对象(一)
  4. MANIFEST.MF的文件的作用
  5. 向量场的散度和旋度_矢量场,标量场,散度,梯度,旋度的理解
  6. 湘潭大学信息安全课作业答案7
  7. ant-design-vue中自定义a-tree的打开与折叠图标
  8. vue 路由 不同动画 翻页 插件
  9. 微信小程序夜间模式,实现更换皮肤,切换白天黑夜模式,简单易懂
  10. 群晖java安装失败_群晖NAS安装Jenkins