软件工程实践2019第三次作业

软件工程实践2019第三次作业

我的GitHub MrHe-Husky

PSP表格:

其实我看到题目首先想到的并不是怎么去完成,而是想起了高中和前后左右桌互相传看《意林》的时间,在看完大部分的文章后,附录的数独题便成了最后的乐趣点,虽然很少有人能够完全解出,但大家还都是都乐在其中。然后想起我的Ubuntu虚拟机上正好有个自带的数独游戏,于是就久违地玩了一把...

emmmm....虽然是最基础的数独,但解出的时间并不理想,而且还浪费40多分钟的打代码时间(>_<)

解题思路:

我是从传统的9阶入手解题,之后再向低阶扩展(之后扩写的过程感觉也比较轻松)

较为机械性的解数独还是得用到排除法,排除掉所有不可能的取值后唯一确定一个数填入,之后再进行递归直到解出所有的能确定的值为止。由此我们建立一个类,它需要做到:

<1> 能储存本元素所剩的所有可能取值
<2> 能够根据同行、列、块进行可能取值排除
<3> 进行自我检查,若自身的值能够确定则调用同行、列、块的元素排除掉新确定的元素并自我检查(递归)能否确定值。

类定义:

class element {
public:
     int num;                                //值,未定则为0
     int row_num;                            //行号
     int column_num;                         //列号
     int block_num;                          //宮号
     int possible_num[10];                   //可能取值(possible_num[0]为是否确定,1:不定,0:确定,1~9分别对应9个数,0代表不可能,1可能)
     int rm_row(int i, int j, element ele[][10], int m);            //根据行进行可能取值排除
     int rm_column(int i, int j, element ele[][10], int m);         //根据列进行可能取值排除
     int rm_block(int i, int j, element ele[][10], int m);          //根据宫进行可能取值排除
     int set(int rnum, int cnum, int number, int m);                //第一次设定(初始化对象数组)
     int selfcheck(int i, int j, element ele[][10], int m);         //自我检查(如果possible_num的值和为2,把possible_num[0]设为0,num设为值,返回num)
     element() {
          num = 0;
          row_num = 0;
          column_num = 0;
          block_num = 0;
          for (int i = 0; i < 10; i++) possible_num[i] = 1;
     }
};

主要递归函数:

int element::selfcheck(int i, int j, element ele[][10], int m)
{
     int s = 0;
     int t, k;
     int ci, cj;  //c:check,作为行,列,块排除循环参数
     for (t = 0; t <= m; t++) {
          s = s + ele[i][j].possible_num[t];
          if (ele[i][j].possible_num[t] != 0) {  //找最后一个可能的数值k
               k = t;
          }
     }
     if (s == 2) {    //如果可能数值唯一,则对其所在行,列,块排除该数并判断是否能确定元素值(element::selfcheck()),并设定num
          ele[i][j].num = k;
          ele[i][j].possible_num[0] = 0;//设为0则不可能再进入判断循环
          for (cj = 1; cj <= m; cj++) {
               ele[i][cj].rm_row(i, cj, ele, m);
               ele[i][cj].selfcheck(i, cj, ele, m);//递归
          }
          for (ci = 1; ci <= m; ci++) {
               ele[ci][j].rm_column(ci, j, ele, m);
               ele[ci][j].selfcheck(ci, j, ele, m);
          }
          if (m == 4 || m == 6 || m == 8 || m != 9) {  //4,6,8,9之外的阶数不存在块,不执行块排除
               for (ci = 1; ci <= m; ci++) {
                    for (cj = 1; cj <= m; cj++) {
                         if (ele[i][j].block_num == ele[ci][cj].block_num) {
                              ele[ci][cj].rm_block(ci, cj, ele, m);
                              ele[ci][cj].selfcheck(ci, cj, ele, m);
                         }
                    }
               }
          }
     }
     return 0;
}

以上为主要函数,完整代码见GitHub <MrHe-Husky/031702327/Sudoku.cpp>

做到这里完成初步代码后再去玩虚拟机上的数独游戏,速度明显得到了提升(

当然,在真正的游戏中这会石锤成作弊行为,并且与追尾黑色高级车同等需道歉三回来求得宽恕,因此不推荐这么做。

完成主体后接着是文件的io,这一部分功能由于之前很少使用到由此十分陌生,在反复翻看书本和看了很多网上的资源后才学会了如何使用命令行参数和文件io方式,写代码过程中大部分时间都在查资料,顺带感慨自己基础不牢,在这些地方花了很长时间。

相较于c不同,c++进行文件io操作简化了很多;
c语言需要先定义文件指针FILE*,然后再使用open函数来关联指针和文件,之后再使用read()和write()函数来读写,操作完之后再调用close来关闭文件防止出错,较为麻烦;接着来看c++;

/*此段只为样例说明,不可直接执行*/
fstream infile(**char, ios::in);       //指定输入文件流对象infile(可以自己定义名字,ios::in 为操作方式)
fstream outfile(**char, ios::out);     //指定输出文件流对象outfile(可以自己定义名字,ios::out为操作方式)
infile >> 输入对象;
outfile << 输出对象;
/*和普通cin,cout完全相同的操作,可读入自动识别读入多种数据类型十分方便,头文件<fstream>*/

代码测试:

使用了3~9阶的数据进行测试,以下为检测结果

使用visual studio的性能探测器进行分析

高耗能段:

本为了方便直接在cmd中观察结果的测试代码居然占了21%的消耗,这点还是比较出乎意料的。。

《构建之法》读后与本次作业:

首先体会最深的就是按时交付这项,超时直接0分,为了这点熬了两次夜..
程序可扩展性:从9阶解1个数独往3~9阶解多个数独扩展,同时增加文件io功能,中间有许多变更,不过我的初稿和最后完成版本也只有50行的差距,这或许得归功于使用面向对象方法并且类函数分化,改动时只需要变动一点增加些开关就足够。
注释很重要。
---END---

posted @ 2019-09-25 18:00  MrHe&Husky  阅读(263)  评论(0编辑  收藏  举报