軟件工程基礎-我的項目-數獨

任務:實現一個可以生成數獨局而且能求解數獨問題的控制檯程序。git

一、GitHub:https://github.com/MiaoZhou48/SoftwareEngineeringProjectgithub

二、時間耗費算法

 

PSD2.1數組

 

Personnal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning  計劃  40  40
.Estimate .估計這個任務須要多長時間    
Development 開發  150  120
.Analysis .需求分析(包括學習新技術)  180  200
.Design Spec .生成設計文檔  150  150
.Design Review .設計複審(和同事審覈設計文檔)  60  60
.Coding Standard .代碼規範(爲目前的開發制定合適的規範)  60  60
.Design 具體設計  80  80
.Coding .具體編碼  1600  1800
.Code Review .代碼複審  100  150
.Test .測試(自我測試,修改代碼,提交修改)  200  250
Reporting 報告  120  150
.Test Report .測試報告  80  100
.Size Measurement .計算工做量  40  30
.Postmortem & Process Improvement Plan .過後總結,並提出過程改進計劃  50  40
   合計  2910  3190

 

三、解題思路:ide

  此項目能夠細分爲兩個功能:生成數獨、解數獨。函數

  生成數獨我考慮採用的是回溯的方法,對殘缺數獨不斷求解,從而獲得相應數量的完善的數獨。性能

  肯定好方法以後即是漫長的學習之路,參考了相應的回溯算法的博客以及其餘算法的數獨生成博客。學習

  在學習過程當中也見識到了不少新的思路生成數獨,好比說對一個已經合理的數獨進行變換,而後判斷其在變換以後是否依然是符合數獨規範。經過這種方式生成數獨,首先會保證結果的互異性,其次相比於回溯不須要大量的計算,節省了大量的時間。測試

  可是因爲已經開始用回溯算法寫了,因此以後的改進階段會再嘗試一下敢變思路。優化

四、設計實現過程

  程序主體是3個函數

  judge():解數獨函數中用此函數判斷回溯條件。

  solve():具體的解函數本體。

  construct():構建函數。

  封裝成函數有利於代碼的複用,與此同時提高了程序的簡潔性和可讀性。

  函數邏輯說明:

  生成數獨函數construct():首先進入函數判斷目前生成的數獨是否數量達標,若符合標準則結束函數體,不然進入下一步。下一步判斷一下1-9個數字是否通過遍歷數字全排列完,如果則跳到行間的全排列判斷,若行全排完就結束函數體,不然改變行的排列順序再初始化數排列進行終局輸出。若判斷數字全排未完,就改變數字的順序再進行終局生成。也就是兩個嵌套的判斷,數字全排和行數組的全排。

  解數獨函數solve():解數獨首先加一個判斷條件,看是否已經生成了需求規定的9x9數獨,如果輸出數獨並結束;不然進行遞歸:利用judge函數判斷防止條件,能放就放並進入下一層遞歸,不能就回溯到上一層遍歷到下一個數再進行遞歸,最終遞歸結束返回最初的判斷,進行數獨輸出。

  其實思路很簡單可是實際運行的話,若給定的數獨數量是1000000或更大跑的時間差很少是在20s,因此仍是有待提高的。

五、性能改進及思路

  測試程序的時候,同窗建議我優化一下數字的輸出,construct函數實際上是最耗時的,因此優化也是從這裏着手。

  以前使用的printf輸出格式,但其實putchar()這種以字符形式輸出會更節省時間,因此我替換了以前的printf輸出方式,結果生成時間減小到了7s左右,相較以前的20多秒提高了不少。

  性能測試:

  這是輸入爲1000000時的性能狀況

  

  

六、代碼說明(思路解釋+註釋說明)

  旁邊的小銘同窗提供給我一種很是好的思路:就是對任意的一個終局數獨,任意交換1-三、4-六、7-9行的順序獲得的依然是知足需求的數獨,這也就彌補了8!=40320遠遠小於1000000這樣的大數字的尷尬境地。

  

  下面是具體的construct()函數代碼

  
void construct(int n)
{
    int count = 0;
    int trans[10] = { 0, 0, 6, 5, 4, 3, 2, 1, 7, 8 };
    int    Temp_column;
    char fn[10] = { '9','7','1','2','3','4','5','6','8','9' };
    if (count < n)
    {
        for (int a = 0; a < 6; a++)
        {
            if (count == n)
                break;
            if (a)
                next_permutation(trans + 4, trans + 6);
            for (int b = 0; b < 6; b++)
            {
                if (b)
                    next_permutation(trans + 7, trans + 9);
                int t = 0; 
                char kong = ' ';
                do{
                    if (t)
                        next_permutation(fn + 2, fn + 9);
                    for (int i = 1; i <= 9; i++)
                    {
                        for (int j = 1; j <= 9; j++)
                        {
                            if (j - trans[i] >= 0)
                                Temp_column = j - trans[i];
                            else 
                                Temp_column = j - trans[i] + 9;
                            putchar(fn[Temp_column % 9]);
                            if (j < 9)
                                printf(" ");
                        }
                        printf("\n");
                    }
                    count++; 
                    t++;
                    if (count == n)
                        break;
                    else
                        printf("\n");
                } while (t<40320);
                if (count == n)
                    break;
            }
        }
    }
}
點擊+號查看代碼

  

  下面是具體的solve()函數代碼

  
void solve()//解數獨
{
    judge(1, 1);
    int flag = 0,countcolumn[10] = { 0 };
    for (int i = 1; i <= 9; i++)
    {
        int counts[10] = { 0 };
        for (int j = 1; j <= 9; j++)
        {
            if (!Tdarray[i][j])
            {
                flag = 1;
                break;
            }
            else
            {
                if (j == 1) 
                    countcolumn[Tdarray[i][j]]++;
                counts[Tdarray[i][j]]++;
                if (counts[Tdarray[i][j]] > 1 || countcolumn[Tdarray[i][j]] > 1)
                {
                    flag = 1;
                    break;
                }
            }
        }
        if (flag)
        {
            cout << "解數獨失敗" << "";
            break;
        }
    }
    if (!flag)
    {
        for (int i = 1; i <= 9; i++)
        {
            for (int j = 1; j <= 9; j++)
            {
                putchar(Tdarray[i][j]);
                if (j < 9)
                    putchar(' ');
            }
            if (i < 9)
                putchar('\n');
        }
    }

}
點擊+號查看代碼

  解數獨就是要有一個判斷函數judge()用來在遍歷的階段看是否可以發放置這個數,思路就是回溯法~

七、實際耗時

  具體的時間消耗見最上方的表格。

相關文章
相關標籤/搜索