軟件工程實踐2019第三次做業

軟件工程實踐2019第三次做業

個人GitHub MrHe-Huskyios

PSP表格:

其實我看到題目首先想到的並非怎麼去完成,而是想起了高中和先後左右桌互相傳看《意林》的時間,在看完大部分的文章後,附錄的數獨題便成了最後的樂趣點,雖然不多有人可以徹底解出,但你們還都是都樂在其中。而後想起個人Ubuntu虛擬機上正好有個自帶的數獨遊戲,因而就久違地玩了一把...c++

emmmm....雖然是最基礎的數獨,但解出的時間並不理想,並且還浪費40多分鐘的打代碼時間(>_<)git

解題思路:

我是從傳統的9階入手解題,以後再向低階擴展(以後擴寫的過程感受也比較輕鬆)github

較爲機械性的解數獨仍是得用到排除法,排除掉全部不可能的取值後惟一肯定一個數填入,以後再進行遞歸直到解出全部的能肯定的值爲止。由此咱們創建一個類,它須要作到:數組

<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方式,寫代碼過程當中大部分時間都在查資料,順帶感慨本身基礎不牢,在這些地方花了很長時間。3d

相較於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---

相關文章
相關標籤/搜索