• 软件工程第二次作业


    第二次作业个人项目实战


    github 代码地址


    泛泛而谈的理解

    我刚开始打代码的时候觉得打完就好,能过样例就ok。经历过一段时间后会发现有可能样例过了其他测试点全错,所以就会开始多测试几组数据,希望自己的代码能够尽量准确。当准确性开始有保障后,我就会去思考程序本身是不是可以进一步改进,使代码运行速度变的更快。在我看来自己出数据测试就相当于书中说的单元测试,回归测试。不过这种测试变得更加的规范化。而自己对代码的不断改进的过程则相当于书中的效能分析,不过通过效能分析工具使我们对代码有更细致的了解,从而是我们能快速的发现代码中的瓶颈从而改进。

    关于个人软件开发流程(psp)这个东西我是第一次了解到。通过书中的大学生和工程师的psp的对比。我发现工作越久,需求分析以及测试花费的时间会越来越多。所以我想我们应该更注重这些方面。而不只是专注于具体的编码。


    遇到的困难及解决方法

    看到这个题目,第一反应就是搜索。接着我就去网上搜索了一些有关数独的资料,发现构造数独基本上就是两种方法,一个就是深度优先搜索,另一个则是先填中间的宫然后通过置换行列得出所有得宫的数字。我个人觉得深度优先搜索的写法更简洁明了,所以我就用了搜索去写。

    整个代码写下来基本上没有什么问题。但是运行后就发现跑的特别慢,用了一分钟左右才跑完。通过性能测试分析,输出函数占用了大部分时间。根据以前的经验知道puts,gets,putchar,getchar这些方法的输入输出会比scanf,printf快很多,所以我就修改了输出函数,使用puts把一整个数独当作一个字符串来进行输出。

    代码如下

    void generator::print()
    {
    	int cnt = 0;
    	for (int i = 1; i <= 9; i++)
    	{
    		for (int j = 1; j <= 9; j++)
    		{
    			ch[cnt++] = shudu[i][j];
    			ch[cnt++] = ' ';
    		}
    		ch[cnt++] = '
    ';
    	}
    	puts(ch);
    }
    

    经过修改后的代码需要大概6秒钟的时间可以输出1000000组答案。下图是最终代码的性能测试分析的一张截图,输出函数print只占用了很小的一部分了。

    修改后代码的性能分析图,耗时最大的部分就是后面展示的关键代码。


    关键代码or设计说明

    运行成功的截图,生成了170m大小的sudoku.txt文件

    关键代码

    for (int i = 1; i <= 9; i++)
    {
    	if (!c[x][i] && !r[y][i] && !b[box][i])
    	{
    		c[x][i] = true; r[y][i] = true; b[box][i] = true;
    		shudu[x][y] = i + 48;
    		get(x, y + 1);
    		c[x][i] = false; r[y][i] = false; b[box][i] = false;
    	}
    }
    

    这是深度搜索中的关键代码,这个搜索函数只有两个参数表示当前在数独的哪个位置。c,r,b三个数组则分别表示的是行,列,宫里面的数字存在情况,例如c[1][1] = true 就表示第一行的数字1已经存在。

    对这个代码我进行了单元测试,测试成功了,但是不知什么原因一直代码覆盖率一直为0,显示生成的二进制文件为空,到现在暂时还没有解决...
    下面给出单元测试的代码

    #include "stdafx.h"
    #include "CppUnitTest.h"
    #include "E:/软件工程/sudokuproject/sudokuproject/sudokuproject/Generator.h"
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    using namespace Microsoft::VisualStudio::CppUnitTestFramework;
    
    int res[10][10];
    int c[10][10], r[10][10], b[10][10];
    
    namespace UnitTest1
    {
    TEST_CLASS(UnitTest1)
    {
    public:
    	int getbox(int x, int y)
    	{
    		int box;
    		if (x >= 1 && x <= 3 && y >= 1 && y <= 3) box = 1;
    		if (x >= 1 && x <= 3 && y >= 4 && y <= 6) box = 2;
    		if (x >= 1 && x <= 3 && y >= 7 && y <= 9) box = 3;
    		if (x >= 4 && x <= 6 && y >= 1 && y <= 3) box = 4;
    		if (x >= 4 && x <= 6 && y >= 4 && y <= 6) box = 5;
    		if (x >= 4 && x <= 6 && y >= 7 && y <= 9) box = 6;
    		if (x >= 7 && x <= 9 && y >= 1 && y <= 3) box = 7;
    		if (x >= 7 && x <= 9 && y >= 4 && y <= 6) box = 8;
    		if (x >= 7 && x <= 9 && y >= 7 && y <= 9) box = 9;
    
    		return box;
    	}
    	bool check()
    	{
    		memset(c, 0, sizeof(c));
    		memset(r, 0, sizeof(r));
    		memset(b, 0, sizeof(b));
    		for (int i = 1; i <= 9; i++)
    			for (int j = 1; j <= 9; j++)
    			{
    				c[i][res[i][j]]++;
    				r[j][res[i][j]]++;
    				b[getbox(i, j)][res[i][j]]++;
    			}
    		for (int i = 1; i <= 9; i++)
    			for (int j = 1; j <= 9; j++)
    			{
    				if (c[i][j] != 1) return false;
    				if (r[i][j] != 1) return false;
    				if (b[i][j] != 1) return false;
    			}
    		return true;
    	}
    	TEST_METHOD(TestMethod1)
    	{
    		// TODO: 在此输入测试代码
    		freopen("input.txt", "w", stdout);
    		generator gen(6);
    		int n = 10000;
    		gen.getshudu(n);
    
    		fclose(stdout);
    		freopen("input.txt", "r", stdin);
    
    
    		bool pd = true;
    		for (int i = 1; i <= n; i++)
    		{
    			for (int j = 1; j <= 9; j++)
    			{
    				for (int k = 1; k <= 9; k++)
    				{
    					scanf("%d", &res[j][k]);
    				}
    			}
    			if (!check()) pd = false;
    		}
    
    		Assert::IsTrue(pd);
    	}
    
    	TEST_METHOD(TestMethod2)
    	{
    		// TODO: 在此输入测试代码
    		freopen("input.txt", "w", stdout);
    		generator gen(6);
    		int n = 1000;
    		gen.getshudu(n);
    
    		fclose(stdout);
    		freopen("input.txt", "r", stdin);
    
    
    		bool pd = true;
    		for (int i = 1; i <= n; i++)
    		{
    			for (int j = 1; j <= 9; j++)
    			{
    				for (int k = 1; k <= 9; k++)
    				{
    					scanf("%d", &res[j][k]);
    				}
    			}
    			if (!check()) pd = false;
    		}
    
    		Assert::IsTrue(pd);
    	}
    
    };
    

    }

    测试成功的截图


    psp + 学习进度条更新

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

    学习进度条


    附加题

    附加题我只做了第二个部分,除了本来的要有30个空,以及唯一解的需求外,我还满足了第一部分的每个宫至少有两个空格的要求。
    代码的命令行运行方式同基础作业

    解题思路:
    第一种:通过构造数独的程序生成一部分数独当作该程序的输入,先分别对9个宫,每个宫随机挖3个空。再对整体的数独矩阵挖3个空,达到30个。然后用搜索判断这个数独的唯一性。从30个开始可以每多挖一个空判断一次唯一性,从而一个数独就可以生成多个挖空后的数独。这种方法需要大概17秒左右。

    第二种:第二种方法是和同学讨论过后才知道的。当一个数独已经具备唯一解,则这个数独少挖一个空也依然是唯一解,所以我们只需要准备一个数独,随机挖出有40个或者更多的空的数独,并且保证具有唯一解。然后我们从这些空中选出一些空去掉,就可得到不同的数独。因为C(40,9)= 273438880 远远超过了1000000。所以很容易就可以得出所有结果。因为满足了每个宫最少有两个空格的需求,所以我需要7秒,如果不需要满足则2秒多就可以了。

    下图为代码运行成功的截图


    个人总结

    这次的作业,加强了我对vs这个工具的使用,知道了如何进行单元测试,如何进行效能分析。虽然还是有很多不懂的地方,但是比一开始什么都不懂好多了。这次的作业如果只算写代码,以及测试的时间并不是很长,但是花在鼓捣vs这个软件上的时间及其漫长,很多东西都是各种百度,到最后却依然不得其解。并且很多文件资料都是英文,应该多加强这方面的阅读能力。

  • 相关阅读:
    kioptrix-1
    4.4 CSRF
    upload-labs 练习笔记
    4.3 XSS
    外国人是怎样读编程书的呢?
    如何快速学习新语言
    Go开发环境配置
    Golang Package I
    MVC模式小结
    Flask基础知识
  • 原文地址:https://www.cnblogs.com/starset/p/7499999.html
Copyright © 2020-2023  润新知