个人项目作业
1.github項目地址
https://github.com/easylliu/gitlearning/tree/master/Sudoku
2.開發時間
| Planning | 計劃 | 30 | 50 |
| Estimate | 估計這個任務需要多少時間 | 10 | 15 |
| Development | 開發 | ||
| Analysis | 需求分析 (包括學習新技術) | 120 | 180 |
| Design Spec | 生成設計文檔 | 無 | 無 |
| Design Review | 設計復審 (和同事審核設計文檔) | 無 | 無 |
| Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 無 | 30 |
| Design | 具體設計 | 30 | 60 |
| Coding | 具體編碼 | 120 | 240 |
| Code Review | 代碼復審 | 60 | 120 |
| Test | 測試(自我測試,修改代碼,提交修改) | 120 | 180 |
| Reporting | 報告 | ||
| Test Report | 測試報告 | 30 | 50 |
| Size Measurement | 計算工作量 | 10 | 20 |
| Postmortem & Process Improvement Plan | 事后總結, 并提出過程改進計劃 | 30 | 30 |
| 合計 | 560 | 975 |
3.解題思路描述
(1).數獨終局生成
開始在看到題目時想到的是通過初始點一直往后延伸推導,后來想了一會感覺有點復雜;在思考無果后通過上網查找資料了解到生成終局的兩種方法
隨機法,矩陣轉換法;但相比之下感覺矩陣轉換法更加直接,隨機法不確定性加大了難度;而由于第一個數已經確定為9,因此第一行第一列不能參與變換,這樣通過一個數獨變換能產生共計5184種;但我們的要求最多有1000000種,因此想產生200個種子數獨,從8個數中取出四位,共有70種取法,而每種取法又對應三種數字交換方式共計210種符合要求,這樣產生的數獨數就已經超過1000000種符合要求了。
(2).求解數獨
在求解數獨上,開始自己思考是想通過那些已確定的那些數字出發,沿著他們所在行列塊進行推導,但隨后感覺有點困難;通過網上查找感覺方法差不多,相對較為直接的就是回溯法求解了,從最開始點一直遞歸遍歷找到所有存在的可能性,在通過行列和3*3塊的驗證來確定是否符合條件,簡單粗暴,在實現上較為簡單,初始計劃打算就使用了這種方法。4.設計實現過程
(1).數獨終局生成
根據思路想到,首先產生的一個種子出發,首先通過其行列變換最多產生5184個數獨,如果還不足的話從那70種取法中隨機選出先交換數字產生新的種子數獨,然后再行列變換,如此種種,在一定程度上還可以保證隨機性,直到產生夠所要的數獨數即可結束。通過這種思路構建函數也就清晰起來,在數獨產生的函數中通過種子數獨不斷調用矩陣變換函數,然后可將矩陣變換函數拆解成行變換函數和列變換函數,如此這樣,函數之間的聯系清晰明朗,實現起來目標也很明確。總得來說在這一部分產生4個函數,其直接的關系也是層層調用。通過這樣的分析對函數的作用了解也清晰許多,通過他們直接的聯系和他們的用途設計單元測試就比較輕松了。
(2).求解數獨
通過暴力解法,對從0到81的所有空白進行1到9的選擇,然后判斷適合符合行列和塊的要求,然后遞歸往下延伸,當后面推不出來遞歸返回繼續延伸推導,直到求解出正確的結果結束。
相對來說這種接法也相對較為直接,從0到81,先填入數字,在判斷是否符合行列和塊的要求,然后遞歸向后延伸。如此這樣分析,相對復雜點的就是遞歸的使用,想都來說3個函數足以。主函數調用填空遞歸函數,遞歸函數在填入數字后調用檢查函數判斷是否符合行列塊的要求,如此實現。然后相對來說,對遞歸的單元測試也相對有些麻煩,通過從對函數功能的理解出發,結合和其他函數的聯系設計單元測試會更加全面。
5.性能改進
改進:由于產生數獨和求解數獨的最大值已到達1000000,在輸出到文件和讀取時的效率問題就顯得尤為重要了,因此在這方面花費時間能減就減,因此我就從每次寫入到文件的數量上做出了改進,再往文件里寫入數獨時,我改進到每次寫入兩百個數獨,這樣減少寫入的次數,提高效率。
由圖片中可以看到,調用次數最多的為檢查行列塊的合理性函數,這也是暴力求解產生的負面效果,其次在面對文件操作時話費的時間較多,仍待改善。
6.代碼說明
主要分做兩個部分,一個是產生終局數獨,一個是求解數獨。
這四個為生成終局數獨的四個關鍵函數:
主要就是sudo_create產生種數獨通過調用矩陣變換得到擴展的數獨,矩陣變換由行變換和列變換組成;
其中最為關鍵一個:產生種子,調用擴展
void sudo_create(int sudo_count, char sudo[9][10], char changenum_array[70][5]) {int index_changenum[70] = { 0 }; //是否隨機到標識符 char sudo_middle[9][10]; //中間種子數獨int randnum = 0, i, j;int count = 0; //已生成的數獨數char a, b, c, d;for (i = 0; i<9; i++) { //初始化strcpy_s(sudo_middle[i], sudo[i]);}count = matrix_change(count, sudo_count, sudo_middle, output); //根據一個種子轉換輸出while (count < sudo_count) {if (count == -1) break;randnum = rand() % 70 + 0;if (index_changenum[randnum] == 0) { //未隨機到a = changenum_array[randnum][0];b = changenum_array[randnum][1];c = changenum_array[randnum][2];d = changenum_array[randnum][3];for (i = 0; i < 9; i++) {for (j = 0; j < 9; j++) {if (sudo[i][j] == a) {sudo_middle[i][j] = b;}else if (sudo[i][j] == b) {sudo_middle[i][j] = a;}else if (sudo[i][j] == c) {sudo_middle[i][j] = d;}else if (sudo[i][j] == d) {sudo_middle[i][j] = c;}elsesudo_middle[i][j] = sudo[i][j];}}count = matrix_change(count, sudo_count, sudo_middle, output);if (count == -1) break;for (i = 0; i < 9; i++) {for (j = 0; j < 9; j++) {if (sudo[i][j] == a) {sudo_middle[i][j] = c;}else if (sudo[i][j] == b) {sudo_middle[i][j] = d;}else if (sudo[i][j] == c) {sudo_middle[i][j] = a;}else if (sudo[i][j] == d) {sudo_middle[i][j] = b;}elsesudo_middle[i][j] = sudo[i][j];}}count = matrix_change(count, sudo_count, sudo_middle, output);if (count == -1) break;for (i = 0; i < 9; i++) {for (j = 0; j < 9; j++) {if (sudo[i][j] == a) {sudo_middle[i][j] = d;}else if (sudo[i][j] == b) {sudo_middle[i][j] = c;}else if (sudo[i][j] == c) {sudo_middle[i][j] = b;}else if (sudo[i][j] == d) {sudo_middle[i][j] = a;}elsesudo_middle[i][j] = sudo[i][j];}}count = matrix_change(count, sudo_count, sudo_middle, output);if (count == -1) break;}}//cout << output; }這三個為求解數獨的主要函數:
主要就是solve_sudo函數調用填空進行遞歸,填入一個數后就判斷其合理性
關鍵一個函數:遞歸調用,逐步展開
//填數 bool solve_blank(int sudo_num) {int line, column, i;line = sudo_num / 9;column = sudo_num % 9;if (sudo_num >= 81)return true;if (sudo1[line][column] == 0) {for (i = 1; i <= 9; i++) {sudo1[line][column] = i;if (matrix_judge(line, column, i)) {if (solve_blank(sudo_num + 1)) return true;}sudo1[line][column] = 0;}}else {return solve_blank(sudo_num + 1);}return false; }轉載于:https://www.cnblogs.com/easyliu/p/7596316.html
總結
- 上一篇: 用递归方法计算斐波那契数列(Recurs
- 下一篇: opencv调试利器ImageWatch