zoukankan      html  css  js  c++  java
  • 软工作业2

    数独作业

    github链接

    解题思路

    • 其实数独与八皇后问题是很类似的,八皇后要求的是每一行每一列不可以出现2个皇后,那么也就相当于数独中的每一行每一列不可以出现2个相同的数字,已经填好的数字就相当于已经放置的皇后(或是障碍物),所以应该想到要通过dfs求解
    • 那么确定了大致算法之后,需要思考如何具体的实现,减少递归的层数
    • 首先把9*9的大矩阵划分成9个3*3的小矩阵,我们按数字顺序填格子,而不是按照格子顺序去填数字。也就是说,我们先把数字1填到这9个小矩阵中,填好了之后把数字2填到9个小矩阵中,以此类推。
    • 这样就可以有效的减少递归层数。

    设计实现

    • 主要有1个generator类去生成数独,在generator中主要的函数有4个,分别是init,work,dfs,Myprint
    • main函数传入n给init函数,work函数调用dfs生成数独,对于每一个合法方案,dfs中调用Myprint
    • 其中最核心的函数是dfs(Depth-First Search)
    • 伪代码如下
    void generator::dfs(int num, int area)
    {
        if (now == rep) exit(0); //数独数量达到了要求的n
        if (cnt == 81) 输出,并return;
        if (当前小矩阵area有当前的数字num)
        {
            if (最后一个area都有了num) dfs(num + 1, 1)//填下一个数字 
            else dfs(num, area + 1); //填下一个小矩阵
        }
        for (枚举当前小矩阵内9个格子,i,j表示坐标)
       {
            if ([i,j]这个位置可以填num)
            {
                [i,j]填入num;
                if (最后一个area都有了num) dfs(num+1, 1)//填下一个数字 
                else dfs(num, area + 1); //填下一个小矩阵
                清空[i,j]上的num;
            }
        }
    }
    

    代码说明

    • 按照数字的顺序,往9*9的矩阵内填写数字,也就是一开始先填入9个1,使得这9个1不矛盾,然后填入9个2,9个3。以此类推
    • 如果按照常规的想法,按照一个格子一个格子的填数字过去,有可能出现当前的格子无论填什么数字都不合法的情况,在这种情况下需要回溯,有可能回溯1步之后继续填这个格子,仍然无解,需要回退若干步,白白浪费了时间。
    • 而如果按数字去填,可以减少递归的层数,一般情况下,只要回退1层就可以纠正
    • 甚至在填入1和9的时候,直接就可以填过去,不需要回溯
    • 核心代码如下(其实就是翻译了一下上面的伪代码)
    void generator::dfs(int num, int area)
    {
    	if (now == rep) exit(0);//数独数量达到了要求的n
    	if (cnt == 81) //生成了一个数独
    	{
    		++now;
    		Myprint();
    		return;
    	}
    	if (InA[area][num])//当前小矩阵已经有数字
    	{
    		if (area == 9)//最后一个小矩阵已经处理
    		{
    			dfs(num + 1, 1);//处理下一个数字
    		}
    		else
    		{
    			dfs(num, area + 1);//处理下一个小矩阵
    		}
    	}
    	int xleft = (area - 1) / 3 * 3 + 1;
    	int xright = xleft + 2;
    	int yup = (area - 1) % 3 * 3 + 1;
    	int ydown = yup + 2;
    	//分别表示在area内x,y坐标的上下限
    	for (register int i = xleft; i <= xright; ++i)
    	{
    		for (register int j = yup; j <= ydown; ++j)
    		{
    			if (mat[i][j] == 0 && row[j][num] == 0 && col[i][num] == 0)//当前格子可以填入num
    			{
    				mat[i][j] = num;
    				row[j][num] = col[i][num] = 1;
    				++cnt;
    				if (area == 9)
    				{
    					dfs(num + 1, 1);//处理下一个数字
    				}
    				else
    				{
    					dfs(num, area + 1);//处理下一个小矩阵
    				}
    				mat[i][j] = 0;
    				row[j][num] = col[i][num] = 0;
    				--cnt;
    			}
    		}
    	}
    }
    

    测试运行

    • 输入的参数数量必须恰好2个,并且第一个是“-c”,第二个是1~100W之间的一个整数,否则均会报错
    • 如果用DEV运行,跑100W个数据,实际上速度还是挺快的
    • 正确性验证,写了一个check程序暴力判断一个数独是否合法,并用map进行判重,没有出现问题
    • 虽然验证100W个速度跑的还是慢了一点。。
    • 验证程序链接

    改进性能


    第一次改进

    • 一开始知道输出占据很大一部分时间,因为如果不输出的话,生成100w个数独大约只需要2~3s,加上输出之后大约需要10~20s,所以应该想办法优化输出
    • 那么用putchar输出会比printf快很多

    第二次改进

    • 减少代码中的常数时间,把所有的i++改成++i,for循环加上register,void函数加上inline,发现其实并不会快很多。。。。

    第三次改进

    • 引用了网络上吉司机的输出外挂,主要原理是用fwrite存下来,然后一次性输出
    • 快了挺多的。。

    第四次改进

    • 把数独改成char类型的数组,直接对字符进行操作,输出的时候直接结合了字符输出以及fwrite
    • 又快了一点的。。
    • 用vs也不会太慢了。。

    最新更新,改成release,原来VS也可以飞!

    PSP 2.1表格

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 30 60
    · Estimate · 估计这个任务需要多少时间 300 500
    Development 开发 80 80
    · Analysis · 需求分析 (包括学习新技术) 40 60
    · Design Spec · 生成设计文档 15 15
    · Design Review · 设计复审 (和同事审核设计文档) 2 2
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
    · Design · 具体设计 20 10
    · Coding · 具体编码 80 60
    · Code Review · 代码复审 20 10
    · Test · 测试(自我测试,修改代码,提交修改) 30 200
    Reporting 报告 50 50
    · Test Report · 测试报告 30 30
    · Size Measurement · 计算工作量 10 10
    · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 20
    合计 737 1047
  • 相关阅读:
    损失函数 代价函数 评分函数 目标函数
    python目录索引
    机器学习/深度学习资料合集
    Git笔记
    目标检测中的正负样本分配
    map计算
    nms
    08shell脚本
    07makefile文件
    05-STL
  • 原文地址:https://www.cnblogs.com/Coolaaa/p/7487860.html
Copyright © 2011-2022 走看看