• 用程序来解数独


    数独(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:貌似这道题的答案不止一种,之前做程序的时候至少得到两种答案,不过结果没保存下来。谁知道呢,就让其他人去研究吧 ...

  • 相关阅读:
    C# 访问AD查询用户信息
    js UTC时间转本地时间
    Silverlight中的序列化和反序列化
    ASP.NET FORM认证配置排错记录
    opencv中cvSetData用法
    WS2812B-64位 8*8位 RGB LED点阵
    1602 LCDKeypad Shield
    Wemos D1 使用ESP8266 板载存储
    Wemos D1 ESP8266的网络工作模式
    Wemos D1 1602 液晶屏幕
  • 原文地址:https://www.cnblogs.com/SugarLSG/p/3149113.html
Copyright © 2020-2023  润新知