软件工程—个人项目(四)最终总结

项目地址:https://github.com/MrFasl/1120172188

PSP表格

PSP2.1
Personal Software

Process Stages


预估耗时

(分钟)


实际耗时

(分钟)

Planning 计划 300  
Estimate 估计这个任务需要多少时间 4000  
Development 开发 1200  
Analysis 需求分析(学习新技术) 700  
DesignSpec 生成设计文档 200  
DesignReview 设计复审 60  
CodingStandard 代码规范 60  
Design 具体设计 200  
Coding 具体编码 900  
CodeReview 代码复审 60  
Test 测试 360  
Reporting 报告 60  
TestReport 测试报告 60  
SizeMeasurement 计算工作量 15  
  合计 8175  

程序需求

生成不重复的数独终局到文件

读取文件内的数独问题,求解并将结果输出到文件

功能建模:

顶层图

一层图

程序实现

数独的生成:

看到这个题目的时候起初感觉暴力做就OK了,但是回头看见一个1e6发现事情并不简单,数量是可以达到的,

每个数独终局包含9*9个数字,第一个字母为(8+8)%9+1 = 8,即每个数独终局第一个数字均为8

数量要求为1<=N<=1000000 = 1e6  在首个数字固定的情况下,第一行可用的组合有8!= 40320种 <1e6。

现在考虑如何扩充数量,由于第一行不能改变,所以考虑456行和789行的行内交换,每个三行全排列个数为3! = 6种  则每个终局由后六行的排序而增加的数量为  6*6种,于是总的组合由6*6*8!>1e6种。数独终局数量问题解决。可以把第一行向右平移一定的位数,只要每一行平移的次数和之前的任意一行不一样,那么每一列也不会有重复的数字。接下来,考虑九宫格的不重复问题,我们只需要将每三行看成一组,组内向右移动都移动相差3的倍数个单位时就可以保证每宫内没有重复的数字。

生成终局

首先以8开头生成第一行,使用全排列函数,然后对每个第一行进行右移操作,生成一个基础终局,随后对这个基础终局进行行变换操作。

主要函数如下:

 1 void BuildMove(int N)
 2 {
 3     char rule1[10][5] = { "036","063" };
 4     char rule2[10][5] = { "258","285","528","582","825","852" };
 5     char rule3[10][5] = { "147","174","417","471","714","741" }; //变换规则
 6     while (1)
 7     {
 8         if (next_permutation(origin, origin + 8))
 9         {
10             origin[8] = 8;
11             for (int i = 0; i < 2; ++i)
12             {
13                 for (int j = 0; j < 6; ++j)
14                 {
15                     for (int t = 0; t < 6; ++t)
16                     {
17                         BuildSudoku(rule1[i], rule2[j], rule3[t]);
18                         countnum++;
19                         if (countnum == N)
20                             return;
21                     }
22                 }
23             }
24         }
25         else
26             break;
27     }
28 }

函数中调用的BuilSudoku()函数即为行变换操作。

行变换关键代码

void ChangeMap(char *rule) //进行变换
{
    for (int i = 0; i < 3; ++i)
    {
        output[datacount++] = origin[(8 + rule[i] - ‘0‘) % 9] + ‘0‘;
        for (int j = 1; j < 17; ++j)
        {
            output[datacount++] = ‘ ‘;
            j++;
            output[datacount++] = origin[((16 - j) / 2 + rule[i] - ‘0‘) % 9] + ‘0‘;
        }
        output[datacount++] = ‘\n‘;
    }
}

数独问题的解决:

一开始我真的不知道怎么解数独,我都没玩过数独,所以就老老实实地去看博客找资料了。找了不少博客,决定使用回溯法解决。

采用暴力回溯方法,对于每个数独问题,进行回溯方法解决,参考了好几个博客。

参考博客:https://blog.csdn.net/u012787710/article/details/77372897

       https://blog.csdn.net/qq_32216775/article/details/79710137

     https://blog.csdn.net/fdaixu/article/details/86307180

还有一个很有用的知乎专栏:https://zhuanlan.zhihu.com/p/31865810?utm_source=qq&utm_medium=social

回溯算法

void TraceBack(int n)
{
    if (suc == 1)
        return;

    if (n > 80)  //代表解完当前数独
    {
        prt();
        suc = 1;
        return;
    }

    if (res[n / 9][n % 9] != 0)   //当前格子有数字,跳到下一格
    {
        TraceBack(n + 1);
    }
    else
    {
        for (int i = 1; i <= 9; i++)
        {
            int f = CheckCanVis(n / 9, n % 9, i);

            if (f == 0)
                continue;
            else
            {
                res[n / 9][n % 9] = i;
                SetVis(n / 9, n % 9, res[n / 9][n % 9]);

                TraceBack(n + 1);

                ResetVis(n / 9, n % 9, res[n / 9][n % 9]);
                res[n / 9][n % 9] = 0;
            }
        }
    }
}

单元测试


对内部函数使用了白盒测试,主要是对一些判定函数的正确性和对一些返回值较多的函数进行了测试,发现在判定函数中存在一个错误的返回值,改进后通过测试。

性能分析

生成数独终局
 在最开始的版本中,终局的生成速度简直慢的令人发指,我在VS中做了100000的测试,结果等了三四分钟都没有结果,然后就手动结束了。

于是上网搜索改进方法,发现好多学长的博客中都提到了文件操作和大缓存区。然后我就对代码进行了改进。

首先是文件输出,这是不可替代项,所以要尽量减少使用次数,和大缓存区结合起来,我开了一个巨大的缓存区,储存得到的数独终局,为了少点空间开成了一维。

当数独终局生成生成执行完毕后,调用ofstream输出数独终局,结果快了很多倍。

可以看出时间的大头仍然是输出函数printf1和生成函数BuildMove但是总时间降到了最低的1.03秒。

求解数独

这部分的时间主要是算法的设计,吸取了数独终局生成的教训,又开了一个比较大的缓存区来记录结果,随后一并输出。剪枝的操作比较麻烦。

我在网上找到了数独残局1000个作为测试用例,性能分析如下

可以看出文件操作还是占了一定的时间,回溯算法TraceBack是算法时间的大部分。

代码质量分析

visual studio2017无错误和警告

注:会遇到预编译(表现为fopen_s报错)问题,解决方案是打开项目属性页,在C/C++选项中选择预处理器,并在右边的预处理器定义中加上 _CRT_SECURE_NO_WARNINGS (注意用分号隔开前面的定义)

PSP表格

PSP2.1
Personal Software

Process Stages


预估耗时

(分钟)


实际耗时

(分钟)

Planning 计划 300  200
Estimate 估计这个任务需要多少时间 4000  2000
Development 开发 1200  100
Analysis 需求分析(学习新技术) 700 800 
DesignSpec 生成设计文档 200 300 
DesignReview 设计复审 60 30 
CodingStandard 代码规范 60 30 
Design 具体设计 200 120 
Coding 具体编码 900  700
CodeReview 代码复审 60  120
Test 测试 360  300
Reporting 报告 60  120
TestReport 测试报告 60  60
SizeMeasurement 计算工作量 15 10
  合计 8175 4890

 

项目感受:

1.开发一款软件绝不是简单的写写代码就OK的,需要分析需求,建立模型等一系列步骤。程序文档和程序一样重要。

2.写函数一定要把所有分支都写好,否则会遇到很大麻烦。

3.程序运行时间不仅取决于算法的设计,还有工程的设计。

4.软件开发和算法设计是不同的两回事。

5.开发时间要尽量紧凑一些。

原文地址:https://www.cnblogs.com/fasl/p/12210036.html

时间: 2024-10-10 04:54:10

软件工程—个人项目(四)最终总结的相关文章

关于软件工程第一次项目的一些感想

在奋斗了24+小时之后,终于完成了软件工程第一次项目.中间有很多曲折,也收获了很多经验. 预计完成时间:复习C++(一小时):构思(半小时):编码(四小时):调试(二小时):其他(半小时).合计(八小时). 实际完成时间:复习C++(两小时):构思(半小时):编码(三小时):调试(五小时+):优化(三小时).合计(十三小时+). 首先是审题方面. 这个要求里面最需要注意的有三点: 1.单词的构成,前三个必须是字母,后面可以接任意多的字母或数字,其他字符均视为分隔符. 2.忽略大小写 3.123f

现代软件工程 第十四章 练习与讨论

15.3.1 有些成功人士或公司认为不需要独立的测试角色(Test),你怎么看? 我猜想和踢足球类似,还是那几个原因: 人太牛: 不世出的天才,例如高德纳写书时发现排版软件不好用,就自己写了一个.也没听说他为这个软件项目请了什么独立测试人员.对了,他不读Email,有秘书帮他处理这些事——这也是一种分工! 有些软件工程师是在后台钻研和开发高难度的算法,或者做某种后台的处理工作,这个工作本身的难度较高,测试主要是自己通过工具完成.如果一定要找一个测试人员,这个测试人员的水平要相当高才行,如果水平那

对于软件工程整个项目的自我感受

软件工程是一门研究用工程化方法构建和维护有效的.实用的和高质量的软件的学科.整个软件开发过程分为问题定义->可行性研究->需求分析->概要设计->详细设计->编码->测试->运行维护计算机软件等步骤(摘自百度知道). 软件工程的学习,是一块大项目,具体通过项目的制作实现,通过项目学习该门课程也是老师给我们的任务. 就目前的学习情况来看,软件工程在项目开发前的准备,开发时的设计和开发后的维护,都是一套需要一起考虑到的,所以,软件工程不单单是一个软件制作,更是软件设计

android 实践项目四

android 实践项目四 本周主要是开发android baidumap实现公交的查询 1.权限的取得和对屏幕的支持 1 <uses-permission android:name="android.permission.BAIDU_LOCATION_SERVICE" > 2 </uses-permission> 3 <uses-permission android:name="android.permission.ACCESS_NETWORK

【目标】我的软件工程实践项目

1.这次的软件工程实践项目是要我们开发移动应用的软件,而我本人对于Android较感兴趣,所以对到最后能学习到的能力的预期当然是: · 有能力独自开发能稳定运行功能简单的应用,比如教务处查成绩软件等: · 懂得如何团队协作,想要开发功能稳定,强大的软件,单干肯定不行,所以要积累团队协作的经验: · 当然,如果有能力,能顺便了解下IOS或微信的开发自然最好. 2.而对课程的期望:希望老师能先给我们规划好大概的学习方向,让我们知道从哪开始入手学习,学习哪个方向的知识,不至于走太多歪路.当然,学习过程

16周(项目四 动态数组)

/* *copyright(c) 2014,烟台大学计算机学院 *All rights reserved. *文件名称:16周(项目四 动态数组) *作者:王忠 *完成日期:2014.12.15 *版本号:v1.0 * *问题描述:新增一个恰当长度的动态数组,将原数组中的数据"复制"到新数组,接着再输入新的数据.增加新数据后的数组,成为保存数据的新数组. *输入描述:输入成绩,学生数 *程序输出:新增的学生数 成绩 #include <iostream> using nam

MVC4商城项目四:应用Bundle捆绑压缩技术

从MVC4开始,我们就发现,项目中对Global.asax进行了优化,将原来在MVC3中使用的代码移到了[App_Start]文件夹下,而Global.asax只负责初始化.其中的BundleConfig类就有个很牛X的功能:合并与压缩.想到以前做ASP.NET的时候要通过工具压缩,手动合并,很麻烦.通过BundleConfig可以大大的提高工作效率和项目性能. 一.基本的使用 1.1.Global.asax文件的初始化 protected void Application_Start() {

现代软件工程团队项目贝塔阶段_开发日志_2018.01.15-2018.01.19

现代软件工程团队项目贝塔阶段_开发日志_2018.01.15-2018.01.19 2018.01.15 已完成: 1.版本上线 1.1上线操作步骤 1.1.1将网站整体文件夹拷贝到keqi1 1.1.2修改microDefinition.php的数据库名和根目录名 1.1.3修改注册界面发送验证码后直接填充功能 1.1.4修改del的主id的class为hide 1.1.5重新运行根目录的111.php(消除红点,也就是消除服务器返回的多余字符) 1.2修复上线过程中遇到的bug  1.2.1

docker 运行jenkins及vue项目与springboot项目(四.docker运行nginx)

docker 运行jenkins及vue项目与springboot项目: 一.安装docker 二.docker运行jenkins为自动打包运行做准备 三.jenkins的使用及自动打包vue项目 四.docker运行nginx 五.jenkins打包springboot服务且在docker中运行 准备配置 创建 /home/jenkins/docker/nginx/nginx.conf 文件及/home/docker/nginx/log文件夹 其nginx.conf 文件为在原nginx.co