软工第二次作业——数独生成器

数独代码点这里

一、阅读《构建之法》前三章

大致浏览了一下第一章,第一章通常只是介绍一下,本书的结构,以及软件工程的概念。

期中我认为比较重要的一点是:软件工程的目标

第二章主要说的是对代码性能的测试,大概讲了有,单元测试,代码覆盖率。用了一个统计文本文件中最频繁出现的单词数,这个例子很简单,却非常的适合向读者讲述这个问题。

收获最大的一点:不要盲目的“改进”代码,最好在改进之前做一做本章的性能测试,找到花费时间最长的那一部分的函数,对其进行优化会比对排序这样的虽然改进后排序的效率提升,但是对项目的整体并没有太大的影响,从而做了无用功!!

第三章也稍微重点看了一下,本章主要说的是个人的开发流程,作者在里面给读者们用了各种的比喻,把一个软件开发团队,比喻成足球队,把之间的各个流程都进行了相对应的阐述,非常的易于理解。

保存了书中的对个人能力的一张进阶图,非常有帮助!

二、 个人PSP

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 30
· Estimate · 估计这个任务需要多少时间 180 360
Development 开发 40 40
· Analysis · 需求分析 (包括学习新技术) 30 25
· Design Spec · 生成设计文档 20 20
· Design Review · 设计复审 (和同事审核设计文档) 10 10
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 15 20
· Design · 具体设计 30 60
· Coding · 具体编码 30 50
· Code Review · 代码复审 20 30
· Test · 测试(自我测试,修改代码,提交修改) 30 60
Reporting 报告 10 8
· Test Report · 测试报告 10 5
· Size Measurement · 计算工作量 180 270
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 10
合计 645 988

三、代码作业

本次作业,是随机生成N个已解答完毕的数独棋盘。按照新增加的要求,我的棋盘的左上角是7.

示例结果:

7 1 2 3 4 5 6 8 9 
3 4 5 6 8 9 1 2 7 
6 8 9 1 2 7 3 4 5 
1 2 3 7 5 4 9 6 8 
9 7 8 2 6 1 4 5 3 
4 5 6 8 9 3 2 7 1 
2 3 1 5 7 6 8 9 4 
5 6 4 9 1 8 7 3 2 
8 9 7 4 3 2 5 1 6 

解题思路

本体采用回溯法来生成数独棋盘,算法原理来自网上阅读。一开始想到用交叉变换来做,但是发现交叉变化所要做的分治会比较困难,之后网上搜索之后发现,回溯法生成的数独会比较快,也容易产生不同的解

找资料的过程也比较艰难,一开始用百度搜索,但是代码写的都不是很清楚,很难了解到回溯法的使用。最后向同学请教了如何使用谷歌搜索,体验之后才发现有时候英文的搜索会比中文的更加合适。

回溯法介绍(摘自CSDN博客):

回溯法有“通用的解题法”之称。用它可以系统地搜索一个问题的所有解或任一解。回溯法是一个既带有系统性又带有跳跃性的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根节点出发搜索解空间树。算法搜索至解空间树的任一节点时,总是先判断该节点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该节点为根的子树的系统搜索,逐层向其祖先节点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根节点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可结束。这种以深度优先的方式系统地搜索问题的解算法称为回溯法,它适用于解一些组合数较大的问题。

代码设计

主要有以下几个函数:

  • addelement 向数组中加入元素
  • recoverElement 回溯查找函数
  • Init 初始化数组
  • F——Best 找到最优的解
  • check_resultShudu 检测数独代码
  • Solve 解数独,采用递归调用回溯

代码说明

关键代码
solveSHudu 判断是否需要回溯,填完一个数字之后,递归进行此操作

bool SolveShudu()
{
    int row,column;
    if(!F_Best(row,column))
        return true;
    for(int i=1;i<10;++i)
    {
        if(!candidate[row][column].test(i))
            continue;
        AddElement(row,column,i);
        if(SolveShudu())
        {
            if(currentIndex==80 && check_resultShudu())
            {
                cout<<endl<<"Result:"<<++resultNum<<endl;
                printResult();
                if(resultNum>=maxNum)
                    return false;
            }
        }
        else
            return false;
        RecoverElement(row,column,i);    
    }
    return true;
}

代码的运行结果图

  • 分别输入纯数字,非纯数字的运行结果

正确输入的结果:

错误输入的结果:

除了以上的运行结果,最后测试了1000组,一万组的数据,都能够正常的运行。

性能分析:

CPU利用率

  • 生成100个数独的cpu利用率

  • 生成1000个数独的cpu利用率

四、中途遇到的困难

1. 读取命令行参数。

这个问题一开始没有注意到,到了最后一天才知道,不使用控制台进行输入,而是直接用控制台运行带命令行参数的方法来执行。一开始并不知道如何增加命令行参数,最后向同学请教了一下,告诉我要去搜索一下argv argc,我新建了一个小的本地测试项目,花了大约一个小时的时间掌握了读取命令行参数

2. git push 没有把BIN文件夹的exe文件提交上去。

这个问题是在最后写博客贴代码项目的时候发现的,bin文件夹里面有两个文件,一个exe。一个txt,但是之后txt提交上去了,这点让人匪夷所思。多次git重复操作之后发现并没有上传成功。最后通过网页端,直接拖拽上传,把exe文件上传到github上面。

3. 识别非法输入。

一开始识别非法输入,是采取网上的现成代码isnum,对纯数字,纯字母的效果比较好,但是,测到一组混合数字字母的字符串的时候如:123afa,会把这个非纯数字当成123,最后过段放弃使用这个代码。

** 用最基本的遍历判断是否是非法字符**,最后通过基本的遍历,然后通过math函数中的atoi函数把字符串数组转换为数值。

4.文件输入输出

一开是没有用vs敲,先用了以前的dev编译环境来编程。一开始用的是freopen来写入文件,最后发现vs运行调试的时候报错了,要我使用更加安全的freopen_s函数来写入文件。这个的网上教程也是写的非常的生涩,最后找了同学支招,叫我去网上搜索ofstream文件输出流,把文件定向输出到txt中。

posted @ 2017-09-10 22:06  豆腐抹上墙  阅读(463)  评论(2编辑  收藏  举报