软工作业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
    《软工作业2》
  • 其中最核心的函数是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之间的一个整数,否则均会报错
  • 《软工作业2》
  • 如果用DEV运行,跑100W个数据,实际上速度还是挺快的
  • 《软工作业2》
  • 正确性验证,写了一个check程序暴力判断一个数独是否合法,并用map进行判重,没有出现问题
  • 虽然验证100W个速度跑的还是慢了一点。。
  • 《软工作业2》
  • 验证程序链接

改进性能

《软工作业2》
《软工作业2》

第一次改进

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

第二次改进

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

第三次改进

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

第四次改进

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

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

《软工作业2》

PSP 2.1表格

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划3060
· Estimate· 估计这个任务需要多少时间300500
Development开发8080
· Analysis· 需求分析 (包括学习新技术)4060
· Design Spec· 生成设计文档1515
· Design Review· 设计复审 (和同事审核设计文档)22
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)1010
· Design· 具体设计2010
· Coding· 具体编码8060
· Code Review· 代码复审2010
· Test· 测试(自我测试,修改代码,提交修改)30200
Reporting报告5050
· Test Report· 测试报告3030
· Size Measurement· 计算工作量1010
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划2020
合计7371047
    原文作者:coolaaa
    原文地址: https://www.cnblogs.com/Coolaaa/p/7487860.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞