软工实践2017第二次作业

软工实践2017第二次作业

标签(空格分隔): c++ 软工实践


Github项目地址


解题思路

第一个想法是暴力,填一格判断一次,不行就返回上一个重新填,直到最后生成结果。

后来想想暴力方法一般都是耗时很大的,后来看到这篇文章Swing数独游戏(二):终盘生成之随机法

大致思路如下:9X9的数独中满足要求的单独一行的可能性有9!=362880种,如果每次随机生成一行,填入棋盘中判断是否合法,不行则返回上一部。并增加一个阈值,如果执行次数过多则清空当前棋盘重新开始填入。

由于每行的可能情况较多,比较容易出现合法的情况。这样看起来效率会比逐格填入要高一些。

然而..

照着这个思路去写了一下,却发现生成速度奇慢,改了很久后也没什么变化,就放弃了这个思路,决定还是用暴力回溯解决问题..

回溯法主要的思路就是逐格填入数字并进行判断,在某一格无法填入有效数字时进行回溯。写下来还算比较顺利,遇到麻烦的点是在回溯。一开始在回溯的处理上犯了错误,在判断之后需要回溯时,只写了把已经赋值的格子重置,没有添加回溯代码。导致在测试运行的时候,无法得出最后的矩阵。发现这个问题之后想了一个取巧的办法,把生成函数声明为bool类型,然后每次如果能生成当前格子就返回true值(具体描述不清,详见代码= =),最后也能达到成功回溯的效果。其他的地方也没什么问题了。


设计实现

设计说明

用回溯法生成数独棋盘,按从左至右、从上至下的顺序填入随机数字,每次填入后进行合法判定,若合法则对下一格进行随机填入,若不合法则重置当前格,回溯至前一格重新填入。
重复操作直到生成一个数独棋盘。

代码组织
  • generator类:实现生成数独棋盘的功能
    • isRowColLegal():判断填入数值在行列上是否合法
    • isBlockLegal():判断输入数值在小九宫格内是否合法
    • resetMatrix():开始生成数独棋盘前先重置棋盘
    • clearFile():输出到目标文件之前先清空目标文件
    • outputFile():输出数独棋盘至目标文件
    • generate():回溯法生成数独棋盘
  • main类:对输入输出进行处理
    • check():对命令行输入进行检验和报错
    • main():主函数,实现整个生成和输入输出过程
主要函数流程图

886317-20170914160913157-2016375878.png


代码说明

generate()函数代码及注释

//generate函数:将数独棋盘看作81个连续空格,用回溯法生成数独
bool generator::generate(int m) {

    //m为当前生成的空格标号(0-80)
    //m=81说明此时数独已经生成结束,结束生成
    if (m == 81) {
        return true;
    }

    //通过标号求得当前行列号
    int r, c;
    r = m / 9;
    c = m % 9;

    //如果当前位置已经填入数字则继续生成下一个位置
    if (sudoku[r][c] != 0) {
        if (generate(m + 1)) {
            return true;
        }
    }

    //一般空格生成过程
    else {
        //cnt用来计数确保生成1-9所有的随机数字
        int cnt = 0;
        int rd;
        //array数组用来标记1-9中已经生成的数字
        int array[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        while (cnt < 9) {
            //生成1-9随机数字直到array中没有该数字的生成记录
            while (1) {
                rd = rand() % 9 + 1;
                if (array[rd - 1] == 0) {
                    array[rd - 1] = 1;
                    cnt++;
                    break;
                }
            }
            //对当前位置赋值
            sudoku[r][c] = rd;
            //判断当前赋值是否合法
            //合法则继续生成下一位置
            if (isRowColLegal(r, c, rd) && isBlockLegal(r, c, rd)) {
                if (generate(m + 1)) {
                    return true;
                }
            }
        }
        //不合法则将当前位置赋值为0,回溯
        sudoku[r][c] = 0;
    }
    return false;
}

main()函数代码及注释

//main函数
int main(int argc, char* argv[]) {
    //先对命令行输入进行处理
    while (!check(argc, argv)) {
        exit(0);
    }
    generator generator;
    const int first = ((4 + 3) % 9 + 1);
    int n;
    //将输入的整数转化为int类型
    n = atoi(argv[2]);
    srand((unsigned)time(NULL));
    //清空目标文件
    generator.clearFile();
    //生成数独棋盘并输出
    for (int i = 0; i < n; i++) {
        generator.resetMatrix(first);
        if (generator.generate(1)) {
            generator.outputFile();
        }
    }
    //提示输出信息
    cout << "成功生成" << n << "个数独棋盘!" << endl;
    return 0;
}

单元测试

isRowColLegal_Test()测试代码及注释

//测试isRowColLegal()函数
        [TestMethod]
        void isRowColLegal_Test()
        {
            generator gTest;
            //将矩阵左上角置为8,其余置为0
            gTest.resetMatrix(8);
            //测试在第一行,第六列填入8(结果应为false)
            bool test1 = gTest.isRowColLegal(0, 5, 8);
            //测试在第五行,第一列填入8(结果应为false)
            bool test2 = gTest.isRowColLegal(5, 0, 8);
            //测试在第一行,第二列插入1(结果应为true)
            bool test3 = gTest.isRowColLegal(0, 1, 1);
            Assert::AreEqual(test1, false);
            Assert::AreEqual(test2, false);
            Assert::AreEqual(test3, true);
        };

isBlockLegal_Test测试代码及注释

//测试isBlockLegal()函数
        [TestMethod]
        void isBlockLegal_Test()
        {
            generator gTest;
            //将矩阵左上角置为8,其余置为0
            gTest.resetMatrix(8);
            //测试在第二行,第二列填入8(结果应为false)
            bool test1 = gTest.isBlockLegal(1, 1, 8);
            //测试在第二行,第二列填入1(结果应为true)
            bool test2 = gTest.isBlockLegal(1, 1, 1);
            Assert::AreEqual(test1, false);
            Assert::AreEqual(test2, true);
        };

resetMatrix_Test测试代码及注释

//测试resetMatrix()函数
        [TestMethod]
        void resetMatrix_Test()
        {
            generator gTest;
            //将矩阵左上角置为5,其余置为0
            gTest.resetMatrix(5);
            int** p = gTest.returnMatrix();
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    if (i == 0 && j == 0) {
                        //测试第一行,第一列是否为5
                        Assert::AreEqual(p[i][j], 5);
                    }
                    else {
                        //测试其余位置是否为0
                        Assert::AreEqual(p[i][j], 0);
                    }
                }
            }
        };
测试结果

886317-20170910125538819-972001106.png

代码覆盖率

886317-20170910125547179-1499204632.png


测试运行

命令行测试

1.未输入参数

886317-20170909234328163-1318910316.png

2.输入参数错误

886317-20170909234332507-550433035.png

3.整数输入错误

886317-20170909234342163-1752258747.png

4.输入正确

886317-20170909234347772-1724639990.png

运行结果(部分)

886317-20170909234352460-82826491.png


性能分析

886317-20170910122251272-758981198.png
886317-20170910122618476-1679996961.png

性能分析后发现输出结果占了大部分的时间,但是并没有找到有效方法来进行优化..


PSP表格

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

ps:没有精确的计时只能填个大概的时间= =

转载于:https://www.cnblogs.com/zhengshuhao/p/7496802.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值