用程序來解數獨


數獨(Sudoku,記憶中這個游戲也叫九宮格,可能是我記錯了,應該叫數獨比較准確)

數獨百度百科:http://baike.baidu.cn/view/961.htm

九宮格百度百科:http://baike.baidu.cn/view/230179.htm#sub5038177

 

前兩天看到 @萬倉一黍 關於數獨解法算法實踐的一篇文章(http://www.cnblogs.com/grenet/archive/2013/06/19/3138654.html),突然想起去年的時候,因為微博上一篇關於某個外國學者花費3個月,研究出一道只有一種答案的數獨題,然后自己一時熱血涌上,花了3個鍾寫了個程序解了這道題。

原微博地址:http://weibo.com/2305049812/z0k9MciY7?type=repost

我的程序結果:http://weibo.com/1974539725/z0mBU0AtX?type=repost

這道數獨題是這樣的:

 

接下來說說我當時寫程序時的想法是怎樣的:

先拋開這道題,數獨的規則很簡單,就是要每一行、每一列、每一個九宮格中都含有1-9九個數字,且不重復。那就可以根據這一規則來制定解題的流程、邏輯。

用程序來解題的好處就是,我們有“循環”、“遞歸”這兩個概念,而且計算機的計算速度遠比人腦要快多了。

 

大概邏輯是:

1、先判斷每一格空格可能填寫的數字,取數量最少的那一格。

2、循環它可能的數字列,取第一個填入該格,再重復執行該邏輯(遞歸調用)。

3、若得到的數字列為空,則跳回上一格,取下一個數字填入,繼續執行查詢;若已跳回第一格,且已取完最后一個數字,則宣布該題無解;若已執行到最后一個,且得到數字列(理論上來將該數字列只有一個item),則宣布得到該題的解。

 

 

這里我使用Winform(C#)來做這個程序。

先設計好界面:

在后台用一個數組變量保存這(9x9)81個格子

private static int _x = 9, _y = 9;
private TextBox[,] _tbs = new TextBox[_x, _y];

 

當點擊“開始計算”按鈕后,后台啟動一個線程開始計算。

private bool Find()
{
    // 循環每一格,找出各自可能情況數
    var count = 10;
    int[] indexs = null;
    IList<int> numbers = null;
    for (var i = 0; i < _x; ++i)
        for (var j = 0; j < _y; ++j)
        {
            int val;
            if (string.IsNullOrEmpty(_tbs[i, j].Text.Trim()) || !int.TryParse(_tbs[i, j].Text.Trim(), out val))
            {
                var ns = GetNums(i, j);
                if (ns.Count == 0)
                    return false;
                else if (ns.Count < count)
                {
                    count = ns.Count;
                    indexs = new int[2] { i, j };
                    numbers = ns;
                }
            }
        }

    // 選擇最少可能情況的那一格
    if (numbers == null)
        return true;
    else
    {
        var tb = _tbs[indexs[0], indexs[1]];
        do
        {
            tb.Text = numbers[0].ToString();
            if (Find())
                return true;
            else
                numbers.RemoveAt(0);
        }
        while (numbers.Count > 0);

        tb.Text = "";
        return false;
    }
}

// 獲取這一格所有可能的情況
private IList<int> GetNums(int x, int y)
{
    // 獲取該格所在九宮格
    var cells = new List<TextBox>();
    int xstart = x / 3 * 3,
        ystart = y / 3 * 3;
    for (int i = xstart, iend = xstart + 3; i < iend; ++i)
        for (int j = ystart, yend = ystart + 3; j < yend; ++j)
            cells.Add(_tbs[i, j]);

    int val;
    var nums = new List<int>();
    for (var num = 1; num <= 9; ++num)
    {
        if (nums.Contains(num)) continue;

        var isIn = false;

        // 橫行
        for (var index = 0; index < _y; ++index)
        {
            if (index != y &&
                !string.IsNullOrEmpty(_tbs[x, index].Text.Trim()) &&
                int.TryParse(_tbs[x, index].Text.Trim(), out val) &&
                val == num)
            {
                isIn = true;
                break;
            }
        }
        if (isIn) continue;

        // 豎行
        for (var index = 0; index < _x; ++index)
        {
            if (index != x &&
                !string.IsNullOrEmpty(_tbs[index, y].Text.Trim()) &&
                int.TryParse(_tbs[index, y].Text.Trim(), out val) &&
                val == num)
            {
                isIn = true;
                break;
            }
        }
        if (isIn) continue;

        // 九宮格
        var tb = _tbs[x, y];
        foreach (var c in cells)
        {
            if (c != tb &&
                !string.IsNullOrEmpty(c.Text.Trim()) &&
                int.TryParse(c.Text.Trim(), out val) &&
                val == num)
            {
                isIn = true;
                break;
            }
        }
        if (isIn) continue;

        // 可選數值
        nums.Add(num);
    }

    return nums;
}

 

兩個大的方法已完成,接下來計算花費的時間。以這道題為例,最后得出結果為:

   

 

這個可能不是最優的邏輯算法,之前試過另一種方法,同樣可以得出這個結果,但花費了70多秒,這個方法只花費了19秒多。

貼出這篇文章只是為了學習,希望各位前輩多多指教。

 

PS:貌似這道題的答案不止一種,之前做程序的時候至少得到兩種答案,不過結果沒保存下來。誰知道呢,就讓其他人去研究吧 ...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM