### 代碼地址:
Github : https://github.com/O-VIGIA/031702414
PSP表格:
PSP2.1 | Personal Software Process Stages | 預估耗時(小時) | 實際耗時(小時) |
---|---|---|---|
Planning | 計划 | 1h | 0.5h |
Estimate | 估計這個任務需要多少時間 | 25h | 26h |
Development | 開發 | 5h | 1h |
Analysis | 需求分析 (包括學習新技術) | 1h | 1h |
Design Spec | 生成設計文檔 | 1h | 1h |
Design Review | 設計復審 | 1h | 0.5h |
Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 1h | 0.5h |
Design | 具體設計 | 1.5h | 0.5h |
Coding | 具體編碼 | 5h | 5h |
Code Review | 代碼復審 | 1h | 0.5h |
Test | 測試(自我測試,修改代碼,提交修改) | 1.5h | 1h |
Reporting | 報告 | 2.5h | 3h |
Test Repor | 測試報告 | 0.5h | 1h |
Size Measurement | 計算工作量 | 0.5h | 0.5h |
Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 2.5h | 3h |
合計 | 25h | 19h |
編寫歷程:
第一階段:算法思考
作業發布的第二天才看到竟然又有作業了,然后就用手機打開博客看一下題目。大致掃了一眼發現是數獨的題目,然后就想到了自己之前做過的一道極其相似的深搜回溯OJ題目,感覺常規思路應該不會太難打。果不其然,當天我就面向過程打了個深搜做出了簡單的九階數獨問題。思考后我發現原來麻煩的是文件讀寫和命令行傳參,和寫博客分析。然后就開始了。
思考:最常規的數獨引擎填寫算法
大致的思維導圖如下
Check函數用來判斷是否合法
1 行合法性
2 列合法性
3 對於某些特殊階數獨的小宮格合法性
4 需要判斷小宮格合法性的數獨通過思考發現計算小宮格左上角坐標的公式
n--格子編號 || level--數獨階數 || height--小宮格高度
width--小宮格寬度 || x--小宮格左上角橫坐標 || y--小宮格左上角縱坐標
x=n/level /height *height
y=n %level /width *width
DFS+回溯 用來填寫空白的數獨部分
1 對所有格子進行編號n 從0開始從左到右從上到下
2 以公式法計算每個編號的所在列所在行 以及 所在小宮的左上角坐標
3 遍歷編號 並對為0的格子 嘗試填入數字1~9
4 通過Check函數判斷填入的數字是否合法 如果合法對編號n+1繼續深搜下去 進行步驟三
5 不合法 退后一位 並將編號第n位的格子 還原為0
代碼如下
/* Check函數:判斷key填入n時是否滿足條件 */ /* ---Check Begin--- */ bool Check(int n,int key) { int x, y, height, width; for (int i=0;i<level;i++)// 判斷n所在橫列是否合法 { int j=n/level;// j為n豎坐標 if (num[j][i]==key) return false; } for (int i=0;i<level;i++)//判斷n所在豎列是否合法 { int j=n%level;//j為n橫坐標 if (num[i][j]==key) return false; } /* 若為9,8,6,4階數獨則判斷小宮格 */ if (level==9||level==8||level==6||level==4) { /* x為n所在的小九宮格左頂點豎坐標 */ /* y為n所在的小九宮格左頂點橫坐標 */ /* height為小宮格高度 */ /* weidth為小宮格寬度 */ switch (level) { case 9: { x = n / 9 / 3 * 3; y = n % 9 / 3 * 3; height = 3; width = 3; break; } case 8: { x = n / 8 / 4 * 4; y = n % 8 / 2 * 2; height = 4; width = 2; break; } case 6: { x = n / 6 / 2 * 2; y = n % 6 / 3 * 3; height = 2; width = 3; break; } case 4: { x = n / 4 / 2 * 2; y = n % 4 / 2 * 2; height = 2; width = 2; break; } } for (int i=x;i<x+height;i++)//判斷n所在的小九宮格是否合法 { for (int j=y;j<y+width;j++) { if (num[i][j]==key) return false; } } } return true;//全部合法,返回正確 } /* ---Check End--- */ /* DFS函數 :深搜+回溯 解決數獨*/ /* ---DFS Begin--- */ int DFS(int n) { if (n>(level*level-1))//所有的都符合,退出遞歸 { sign = true; return 0; } if (num[n/level][n%level]!= 0) //當前位不為空時跳過 { DFS(n+1); } else { for (int i=1;i<=level;i++)//否則對當前位進行枚舉測試 { if (Check(n,i)==true)//滿足條件時填入數字 { num[n/level][n%level] = i; ``` DFS(n+1);//繼續搜索 if (sign==true) return 0;//返回時如果構造成功,則直接退出 num[n/level][n%level] = 0;//如果構造不成功,還原當前位 } } } ``` } /* ---DFS Begin--- */ /* init_num 函數:初始化num數組 */ /* ---init_num Begin--- */ void init_num() { for (int i = 0; i < 20; ++i) for (int j = 0; j < 20; ++j) num[i][j] = 0; } /* ---init_num end--- */
第二階段:命令行傳參
寫完了主要引擎,就考慮到了命令行傳參的問題,於是 百度。
百度之后發現原來
int main(int argc, char *argv[])
main函數里面的參數原來是 則個意思
簡單來說 argc 就代表命令行參數的個數 argv[]數組里面是命令行的參數內容(字符串類型)
然后我就開始了自己的思考:
我們的標准命令行是長
Suduku.exe -m 8 -n 10000 -i input.txt -o ouput.txt
這樣用命令行傳參我需要 階數8 個數10000 讀入文件名input.txt 輸出文件名output.txt 這四個參數
測試后我發現 argv[0]是Suduku.exe
我想那么以此類推 argv[2] argv[4] argv[6] argv[8] 不就正是我想要的參數
測試后果真如此
int main(int argc, char *argv[]) {
/*從命令行接收參數 */
level = atoi(argv[2]);
int m = atoi(argv[2]);
int n = atoi(argv[4]);
```
/*對多個數獨進行操作*/
for(int kk=1;kk<=n;++kk){
sign=false;
init_num();
sudu sudu1(m);//構造出m階數獨
sudu1.Martrix_input((int*)sudu1.Martrix, m, (char*)argv[6]);//從命令行指定的文件名中讀入一個m階數獨
sudu1.Martrix_num_sudu((int*)sudu1.Martrix, m);//數獨->數組
DFS(0); //數獨數組計算引擎
sudu1.Martrix_sudu_num((int*)sudu1.Martrix,m);//數組->數獨
sudu1.Martrix_output((int*)sudu1.Martrix, m, (char*)argv[8]);//在命令行指定的文件名中輸出數獨的解
}
```
}
第三階段:文件流操作
解決了命令行傳參問題,下面就是文件流操作。
百度了一下發現 C++ fstream 這個庫提供了簡便的文件流操作
例如:
ifstream infile;
infile.open(filename);
infile>>“software”;
infile.close();
但是為了讀入完一個數獨后 還需要讀入下一個數獨
經過極度瘋狂思考之后
我還是去百度了,百度之后發現了tellg()和seekg()這兩個文件指針操作
簡單來說tellg()就是把讀完一個數獨后的位置傳出來
seekg(offset,ios::beg)就是從你指定的地方偏移offset位 當前位ios::cur 起始位ios::beg 末位ios::end
(這個東西我嘗試了好久,好多坑 大坑就是打開文件要用二進制 不如可能會出現一點偏移問題)
我開始了自己的思考:
如果我把先把offset初始化為0
每一次讀完數獨后的位置用tellg()傳出來賦值給offset然后從起始位偏移offset讀文件 天命hhhh
寫入就簡單多了直接用附加寫的方式 可以在指定文件名后面附加寫入下一個數獨的解
outfile.open(filename,ios::app);//以后繼方式打開文件以便繼續寫
第四階段:數獨類的建立
這時我忽然需要建立起自己的數獨類 因為接受完參數和數據 我就能初始化類了
然后就開始了
不知道為什么我會把這個命令為term
反正就開始了他就長這樣(某種原因不能全發代碼)
class sudu
{
public:
int row;//row=col行列相等
int *Martrix = new int[row*row];//創建時new一個數組
void Martrix_input(int *Martrix,int row,const char* filename);
void Martrix_output(int *Martrix,int row,const char* filename);
void Martrix_sudu_num(int *Martrix,int row);
void Martrix_num_sudu(int *Martrix,int row);
sudu(int a);
~sudu();
};
思想:
sudu(int a) 用level->a來構造一個level階的數獨
*Martrix 用來存放從文件中讀出的一個數獨數據 在析構函數~sudu()里delete[]掉
Martrix_input 從文件中讀取一個等級位level的數獨數據
Martrix_output 向文件中寫入經過引擎后數獨的解
Martrix_sudu_num 咸魚函數一號 將構造好的數獨數據從Martrix傳入num 以便本豆芽菜解決問題
Martrix_num_sudu 咸魚函數二號 將num中已經解決完的數獨數據傳入Martrix 以便調用類輸出文件函數
第五階段:整理代碼 性能分析 和 單元測試
一 整理代碼
理順cpp和.h的互相調用,也就是
stdafx.cpp
stdafx.h
Sudoku.cpp
這三個東西的關系
stdafx.h是用來定義各種資源
stdafx.cpp用來實現stdafx.h中的資源
Sudoku.cpp就跟引用庫函數一樣引用stdafx.h中的各種資源
然后提交到Git的時候注意建一個 .gitignore 文件用來忽略上傳的其他文件
二代碼性能分析
三 單元測試 《菜雞 --->吃素的雞》
四 所有解決警告
編譯時發現 DFS函數不是所有控件都有返回值 問題
解決問題后:
### 各種測試.png
先上大的:
未測先飄
一 數獨:因卡因之問
不知道是哪個就兩個一起
芬蘭數學家因卡拉,花費3個月時間設計出了世界上迄今難度最大的數獨游戲,而且它只有一個答案。因卡拉說只有思考能力最快、頭腦最聰明的人才能破解這個游戲。這是英國《每日郵報》2012年6月30日的一篇報道。
8 0 0 0 0 0 0 0 0
0 0 3 6 0 0 0 0 0
0 7 0 0 9 0 2 0 0
0 5 0 0 0 7 0 0 0
0 0 0 0 4 5 7 0 0
0 0 0 1 0 0 0 3 0
0 0 1 0 0 0 0 6 8
0 0 8 5 0 0 0 1 0
0 9 0 0 0 0 4 0 0
0 0 5 3 0 0 0 0 0
8 0 0 0 0 0 0 2 0
0 7 0 0 1 0 5 0 0
4 0 0 0 0 5 3 0 0
0 1 0 0 7 0 0 0 6
0 0 3 2 0 0 0 8 0
0 6 0 5 0 0 0 0 9
0 0 4 0 0 0 0 3 0
0 0 0 0 0 9 7 0 0
9階答案
傳說級因卡因之問1:
8 1 2 7 5 3 6 4 9
9 4 3 6 8 2 1 7 5
6 7 5 4 9 1 2 8 3
1 5 4 2 3 7 8 9 6
3 6 9 8 4 5 7 2 1
2 8 7 1 6 9 5 3 4
5 2 1 9 7 4 3 6 8
4 3 8 5 2 6 9 1 7
7 9 6 3 1 8 4 5 2
傳說級因卡因之問2:
1 4 5 3 2 7 6 9 8
8 3 9 6 5 4 1 2 7
6 7 2 9 1 8 5 4 3
4 9 6 1 8 5 3 7 2
2 1 8 4 7 3 9 5 6
7 5 3 2 9 6 4 8 1
3 6 7 5 4 2 8 1 9
9 8 4 7 6 1 2 3 5
5 2 1 8 3 9 7 6 4
二 數獨 博客標准99數獨測試
原題及答案見博客作業
三 數獨 骨灰級88數獨
0 8 0 7 5 2 0 0
0 0 4 0 0 0 0 0
0 3 0 0 0 6 0 0
5 2 0 0 0 0 0 1
0 0 0 0 0 0 6 0
0 0 0 0 0 0 5 2
0 4 6 0 0 8 0 0
0 0 3 0 0 1 8 0
0 4 0 0 0 3 0 0
0 3 0 8 6 0 0 0
0 0 0 0 0 0 0 2
0 0 1 0 7 0 0 5
0 0 0 0 0 1 0 0
1 0 5 2 0 0 7 3
0 6 0 3 0 0 0 8
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 2 0 0 0 0 8 0
0 0 6 3 0 8 0 0
4 0 0 5 1 0 0 0
0 6 0 0 0 0 0 3
3 0 0 0 2 5 0 0
0 0 0 7 6 0 0 5
0 0 0 1 0 0 7 0
8階答案
初級:
4 8 1 7 5 2 3 6
1 6 4 3 8 7 2 5
7 3 5 2 1 6 4 8
5 2 8 6 4 3 7 1
8 7 2 1 3 5 6 4
3 1 7 8 6 4 5 2
2 4 6 5 7 8 1 3
6 5 3 4 2 1 8 7
高級:
6 4 2 5 1 3 8 7
5 3 7 8 6 2 4 1
7 1 3 4 8 5 6 2
8 2 1 6 7 4 3 5
4 5 8 7 3 1 2 6
1 8 5 2 4 6 7 3
2 6 4 3 5 7 1 8
3 7 6 1 2 8 5 4
骨灰級:
5 3 1 8 7 2 6 4
6 2 7 4 5 3 8 1
7 1 6 3 4 8 5 2
4 8 2 5 1 6 3 7
1 6 5 2 8 7 4 3
3 7 4 6 2 5 1 8
8 4 3 7 6 1 2 5
2 5 8 1 3 4 7 6
四 數獨 標准級66數獨
0 0 0 2 3 0
0 0 0 0 0 0
2 0 0 0 0 4
0 0 1 0 0 0
0 5 0 0 1 3
0 0 3 0 6 0
0 0 4 0 0 0
0 0 2 0 6 0
5 0 0 3 0 0
0 0 0 0 0 4
0 3 0 0 0 0
0 6 0 0 0 1
5 0 0 0 2 1
1 0 0 0 0 0
0 0 0 0 0 0
0 0 3 0 0 4
0 0 6 4 0 0
0 1 0 0 0 2
6階答案:
1 6 4 2 3 5
3 2 5 6 4 1
2 3 6 1 5 4
5 4 1 3 2 6
6 5 2 4 1 3
4 1 3 5 6 2
6 5 4 1 2 3
3 1 2 4 6 5
5 4 6 3 2 1
1 2 3 6 5 4
2 3 1 5 4 6
4 6 5 2 3 1
5 6 4 3 2 1
1 3 2 5 6 4
6 4 1 2 5 3
2 5 3 1 6 4
3 2 6 4 1 5
4 1 5 6 3 2
五 數獨 入門級44數獨
0 0 0 0
0 2 0 3
0 0 0 0
1 0 4 0
4 0 0 0
0 0 2 0
0 0 0 1
0 1 0 0
1 0 0 0
4 0 1 0
0 0 0 0
0 0 0 2
4階答案:
3 1 2 4
4 2 1 3
2 4 3 1
1 3 4 2
4 2 1 3
1 3 2 4
2 4 3 1
3 1 4 2
1 3 2 4
4 2 1 2
2 4 3 1
3 1 4 2