• 软工实践2017第二次作业


    软工实践2017第二次作业

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


    Github项目地址


    解题思路

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

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

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

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

    然而..

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

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


    设计实现

    设计说明

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

    代码组织

    • generator类:实现生成数独棋盘的功能

      • isRowColLegal():判断填入数值在行列上是否合法
      • isBlockLegal():判断输入数值在小九宫格内是否合法
      • resetMatrix():开始生成数独棋盘前先重置棋盘
      • clearFile():输出到目标文件之前先清空目标文件
      • outputFile():输出数独棋盘至目标文件
      • generate():回溯法生成数独棋盘
    • main类:对输入输出进行处理

      • check():对命令行输入进行检验和报错
      • main():主函数,实现整个生成和输入输出过程

    主要函数流程图


    代码说明

    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);
                        }
                    }
                }
            };
    

    测试结果

    代码覆盖率


    测试运行

    命令行测试

    1.未输入参数

    2.输入参数错误

    3.整数输入错误

    4.输入正确

    运行结果(部分)


    性能分析


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


    PSP表格

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

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

  • 相关阅读:
    【DWT笔记】基于小波变换的降噪技术
    【DWT笔记】傅里叶变换与小波变换
    Matlab命令——目录操作(windows&Linux)
    【DCT笔记】DCT变换、DCT反变换、分块DCT变换
    JSOI2018 防御网络
    NOI2018 屠龙勇士
    CRT&EXCRT学习笔记
    CF662C Binary Table
    HNOI2017 礼物
    ZJOI2014 力
  • 原文地址:https://www.cnblogs.com/zhengshuhao/p/7496802.html
Copyright © 2020-2023  润新知