• 个人项目作业-数独生成与求解


    个人项目作业-数独生成与求解

    项目地址

    https://github.com/FelixCaae/SudokuGen

    解题思路

      一开始打算自己设计一个算法,因为之前也做过数独题目,对解题思路有一定的了解,打算用排除法加搜索来做,初步的思路是这样的,首先初始化一个空数独表格,给每个格子内填入可能的解,在没有任何约束条件的情况下就是1-9。然后将题目上的数字依次填入,每填入一个数字就依据约束条件对可能性做一些修改,比如(0,0)的位置填入了1,那么就从第一个宫内的其他数字单元和该行该列的其他数字单元中移除可能解1.如此反复,将题目填入完毕后,依次遍历可能解最少的单元来进行解的搜索,每次确定一个解就进一步压缩限制条件直到所有数字都被填完。

    这个算法没有来得及实施,因为感觉很可能会性能不足,所以就上百度搜索了一下数独的算法求解,了解到一种以宫为单位进行填写的算法而不是以格为单位进行填写的回溯法和一种以数独的同质性为基础的交换算法(用于生成而不是求解)。Wiki上列举的各种方法更为详细,但是没有细看,决定先试试回溯法,并且用python写了一个版本用于验证。

    数独入门解题技巧 http://ask.kedo.gov.cn/resource/stars/823575.shtml

    回溯法介绍  http://blog.sina.com.cn/s/blog_a28e3dd90101e1i2.html

    设计过程

      这部分参考了之前OO的经验,把功能分给了4个类,参数处理类(ArgumentHandler)用于处理输入的参数,文件处理类(FileHandler)用于打开关闭文件和读写数独,表类(Table)用于记录数独数据并且提供生成和求解的方法,数独缓冲类(SdkBuffer)用于临时存储生成的数独。

    类确定后,首先是设计公有方法,确定类的协同关系以及确保覆盖全部功能,其中比较重要的Table类试着使用了形式化的方法

      1 class Table
      2 
      3 {
      4 
      5 private:
      6 
      7 int cells[9][9];
      8 
      9 int total;
     10 
     11 int top;
     12 
     13 SdkBuffer* pCurrentBuffer;
     14 
     15 public:
     16 
     17 Table();
     18 
     19 /*@Modifies:this
     20 
     21 Set all elements to zero ,which stands for not being filled.
     22 
     23 */
     24 
     25 void SetZero();
     26 
     27  
     28 
     29 /*@Requires:0<=row,col<9 and 1<=num<=9
     30 
     31  @Modifies:this
     32 
     33 Set one element to the value given
     34 
     35 */
     36 
     37 void Set(int row, int col, int num);
     38 
     39  
     40 
     41 /*@Requires:length(num)>=9 and any element i in num[][] ==> 1<=i<=9
     42 
     43    @Modifies:this
     44 
     45    Set the table with the two-dimension array given.
     46 
     47 */
     48 
     49 void Set(int num[][9]);
     50 
     51  
     52 
     53 /*@Requires:total>0 and total <=100000 and sdb!=null and total<sdb->GetCapacity()
     54 
     55    @Modifies:sdb
     56 
     57    Generate sudoku solution and write to a file.
     58 
     59 */
     60 
     61 void Generate(unsigned int total, FileHandler* fh);
     62 
     63  
     64 
     65 /*@Requires:sdb!=nulls
     66 
     67 @Effects:/result==true <==> We can find at least one solution
     68 
     69 @Effects:/result==false <==> We can`t find any solution
     70 
     71 Solve the problem in the table
     72 
     73 */
     74 
     75 bool Solve(SdkBuffer* sdb);
     76 
     77  
     78 
     79 /*Solve the problems in src buffer and write the answers to the dst buffer
     80 
     81   Notice:it will clear the dst buffer firtst and clear src buffer as an effect
     82 
     83 */
     84 
     85 void Solve(SdkBuffer * sdbSrc, SdkBuffer * sdbDst);
     86 
     87  
     88 
     89 /*@Requires:total>0 and total <=100000 and sdb!=null
     90 
     91 @Modifies:sdb
     92 
     93 Generate sudoku solution to the sdb
     94 
     95 */
     96 
     97 void Generate(unsigned int total, SdkBuffer* sdb);
     98 
     99  
    100 
    101 /*@Requires:src!=null,dst!=null
    102 
    103 @Modifies:src,dst
    104 
    105 Solve the problem set given by the src and output the result to dst.
    106 
    107 */
    108 
    109 void Solve(FileHandler* src, FileHandler*dst);
    110 
    111 ~Table();
    112 
    113 private:
    114 
    115 /*@Requires : 0<rst, cst<3 1 <= num <= 9
    116 
    117    @Effects : / result == null <= = > there is one number in Palace(rst, cst) equals to num
    118 
    119    @Effects : / result == {-1 } <= = > there is no suitable place for num in Palace(rst, cst)
    120 
    121    @Effects : len(/ result) >= 1 && / result != {-1} <= = > there is one or more suitable place for num in Palace(rst, cst)*/
    122 
    123 int* lookUp(int rst, int cst, int num);
    124 
    125  
    126 
    127 void solve(int subt, int num);
    128 
    129  }

    其他类的方法设计比较简单,缓冲区采用了栈的设计,参数处理器使用一个方法接受输入参数,并返回一个枚举型变量,文件读写类是对文件操作做了一个简单的封装。

    单元测试及性能优化

    这部分没有来得及做,确实时间没有安排好,直到周六才开始编码,而且周末组队的事情耽误了一些时间,这个作业其实还是很勉强完成的,之后得补上测试,优化的话可能得结对编程的时候再说了。

    代码展示

    关键算法

    int*  Table::lookUp(int rst, int  cst, int num)
    {
        int *result=new int[10];
        int index=0;
        int ron=0,con=0;
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                ron = i + rst * 3;
                con = j + cst * 3;
                if (cells[ron][con] == num)
                {
                    return NULL;
                }
                if (cells[ron][con] != 0)
                    continue;
                bool pass=true;
                for (int t = 0; t < 9; t++)
                {
                    if (cells[ron][t] == num || cells[t][con] == num)
                    {
                        pass = false;
                        break;
                    }
                }
                if (pass)
                {
                    result[index++] = ron*9+con;
                }
            }
        }
    //    printf("%d", index);
        result[index] = -1;
        return result;
    }
    void Table::solve(int subt, int num)
    {
        //this function works recursively to fill number 1-9 one by one to all the 9 sub tables
    
        //subt index subtable ,which is a 3x3 palace. It`s index starts from 0 to 8
        //num (1-9)  is the current  number we try to fiil in  
        
        
     //    printf("%d %d
    ", subt, num);
        //if we get enough solutions ,just exit
        if (total == top)
            return;
        //It signals that all numbers are filled.   
        else if (num == 10)
        {
            total += 1;
            pCurrentBuffer->Fill(cells);
            return;
        }
        //we should try the next number while that all palaces are reached
        else  if (subt == 9)
        {
            solve(0, num + 1);
            return;
        }
        //Row or Column of SubTable
        int rst = subt / 3;
        int cst = subt % 3;
        //suitable cells 
        int * suitcells = lookUp(rst, cst, num);
        if (suitcells == NULL)
        {
            solve(subt + 1, num);
            return;
        }
        int index = 0;
        //Row or Column of Number
        int ron = 0;
        int con = 0;
        while (suitcells[index] != -1 && total!=top)
        {
            ron = suitcells[index] / 9;
            con = suitcells[index] % 9;
            cells[ron][con] = num;
            solve(subt + 1, num);
            cells[ron][con] = 0;
            index++;
        }
        delete[] suitcells;
    }

    lookUp()函数用于在某一个宫内为一指定的数寻找合适的位置,返回NULL代表该数已存在,空数组代表没有合适的位置等等。

    solve()函数递归调用自己,依次填入1-9到九个宫里,直到发现无解或解的个数达到设定值为止。

    PSP2.1

    Personal Software Process Stages

    预估耗时(分钟)

    实际耗时(分钟)

    Planning

    计划

    20

    15

    · Estimate

    · 估计这个任务需要多少时间

    20

    15

    Development

    开发

    665

    570

    · Analysis

    · 需求分析 (包括学习新技术)

    200

    120

    · Design Spec

    · 生成设计文档

    60

    90

    · Design Review

    · 设计复审 (和同事审核设计文档)

    30

    0

    · Coding Standard

    · 代码规范 (为目前的开发制定合适的规范)

    5

    30

    · Design

    · 具体设计

    100

    0

    · Coding

    · 具体编码

    120

    300

    · Code Review

    · 代码复审

    30

    30

    · Test

    · 测试(自我测试,修改代码,提交修改)

    120

    45

    Reporting

    报告

    80

    80

    · Test Report

    · 测试报告

    20

    10

    · Size Measurement

    · 计算工作量

    20

    10

    · Postmortem & Process Improvement Plan

    · 事后总结, 并提出过程改进计划

    40

    60

    合计

    765

    665

    总结:

      这次作业完成的比较失败吧,自己感觉,没有一开始就认真着手这些工作导致时间不充裕,容错率低是一个很大的原因。

      其次是查询资料的环节,由于偷懒没有仔细比较各种算法的优劣,而是快速地凭印象选择,如果不是看到其他人的交流很可能不知道那些比较有效率的算法。

      设计阶段没有很到位,只是花了几分钟画了一个草图,可能之后会表现出来一些问题吧。

      代码复审,嫌麻烦,时间短,没有做,结果在调试上浪费了很多时间在一些小细节上.. 主要的算法明明先用python实现了一遍,几乎一模一样的写到VS里,本以为这块会是出错率最低的地方,实际上花了最多的时间来调。如果仔细审查一遍会好很多吧。

      单元测试..没有做,所以可能还有很多潜在的漏洞。设计的时候就没有设计单元测试,真是一团糟..

      

  • 相关阅读:
    JDK8 Optional类使用
    Kafka RocketMQ
    Dubbo,ElasticSearch,JVM,多线程/高并发,消息中间件 常问问题
    Redis
    java jvm 虚拟机
    25 岁做什么,可在 5 年后受益匪浅?
    设计模式
    并发与并行的理解
    多线程学习
    FireFox 如何在当前页面打开书签
  • 原文地址:https://www.cnblogs.com/felixcaae/p/7599203.html
Copyright © 2020-2023  润新知