计算零到多个可能的数独结果,并打印到Console中。

 

调用方法

MainController mc = new MainController();

mc.Do();

  

 

输入

数独数据 类型为int[,],-1表示空。

通过Sudu.Setup()方法设置。

 

输出

同输入格式。

通过Sudu.PrintResult()方法打印。

 

概述
 该方法属于穷举的搜索算法。将完整过程分解为重复的往一个空里填一个数字的过程,每填一个空,尝试所有可用数字,找到与已填数字不冲突的数字。如果找不到合适的数字,修改已填数字,既回溯到上一步。

 

代码实现分两部分,一个是填充循环,一个是填充位置改变和填充操作。分别实现为两个类。

 

 

填充循环

 

思路

循环主体是执行操作(填写一个数字)。

如果操作失败(没有有效的数字能够填入),则移动到上一步。如果操作成功,则移动到下一步。

如果无法移动到下一步,则认为完成所有步骤,获得了一个有效的结果。

如果无法移动到上一步,则认为无法修改步骤,结果查找完毕,退出执行循环。

 

代码

    public class MainController
    {
        public Sudu sudu = new Sudu();

        /// <summary>
        /// 执行循环
        /// </summary>
        /// <param name="findmulti">是否寻找多个解</param>
        public void Do(bool findmulti = true)
        {
            sudu.PrintResult();
            DateTime dt = DateTime.Now;
            int resultcount = 0;
            int actioncount = 0;
            while (true)
            {
                // 执行
                bool actionresult = sudu.TakeAction();
                actioncount++;
                if (actionresult)
                {
                    // 执行成功则移动到下一步
                    if (sudu.MoveNext() == false)
                    {
                        // 无法移动到下一步,则已经完成
                        Console.WriteLine("尝试次数:" + actioncount);
                        Console.WriteLine("结果个数:" + resultcount);
                        Console.WriteLine("耗时(s):" + (DateTime.Now - dt).TotalSeconds);
                        sudu.PrintResult();
                        resultcount++;
                        if (findmulti)
                        {
                            continue;
                        }
                        else
                        {
                            return;
                        }
                    }
                }
                else
                {
                    // 执行失败则退回上一步
                    if (sudu.MovePrev() == false)
                    {
                        // 无法移动到上一步,则已经结束搜索
                        Console.WriteLine("结束");
                        Console.WriteLine("尝试次数:" + actioncount);
                        Console.WriteLine("结果个数:" + resultcount);
                        Console.WriteLine("耗时(s):" + (DateTime.Now - dt).TotalSeconds);
                        sudu.PrintResult();
                        return;
                    }
                }

            }
        }
    }

  

填充位置改变和填充操作

 

思路

数字填充:每次往数组中填充一个数字,如果能填充不冲突的数字则返回True,否则返回False。

填充位置改变:当前填充位置表示为行序号和列序号,初始状态都为0。移动到下一步,上一步时,修改填充位置,即修改此序号对。

 

代码

    public class Sudu
    {
        public Sudu()
        {
            int[,] data = new int[,] {
            { -1,-1,2,9,-1,-1,-1,-1,-1},
            { -1,-1,-1,-1,-1,-1,8,-1,5},
            { -1,5,8,-1,-1,-1,7,-1,-1},
            { 1,-1,9,-1,3,-1,-1,-1,-1},
            { -1,-1,-1,-1,7,8,-1,-1,-1},
            {-1,-1,6,-1,-1,-1,-1,3,-1 },
            {9,4,-1,-1,5,-1,-1,-1,1 },
            {-1,-1,-1,-1,-1,7,-1,-1,9 },
            {6,8,-1,-1,-1,3,5,-1,-1 },
        };
            Setup(data);
        }
        /// <summary>
        /// 初始化数独数据。data中-1表示可填入,其他值表示固定值
        /// </summary>
        /// <param name="data"></param>
        public void Setup(int[,] data)
        {
            int c = 0;
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    if (data[i, j] == -1)
                    {
                        filleddata[i, j] = 0;
                        canfill[i, j] = 1;
                    }
                    else
                    {
                        c = c + 1;
                        filleddata[i, j] = data[i, j];
                        canfill[i, j] = 0;
                    }
                }
            }
            col = 0;
            row = 0;
            Console.WriteLine("已填数字:" + c);
        }
        /// <summary>
        /// 初始值为0,表示未开始填充
        /// </summary>
        int[,] filleddata = new int[9, 9];
        /// <summary>
        /// 0代表不能填充,1代表能填充
        /// </summary>
        int[,] canfill = new int[9, 9];

        int col = 0;
        int row = 0;
        /// <summary>
        /// 移动到上一个可填空位置
        /// </summary>
        /// <returns>是否有上一个可填充位置</returns>
        public bool MoveNext()
        {
            while (true)
            {
                if (col == 8 && row == 8)
                    return false;
                if (col == 8)
                {
                    row++;
                    col = 0;
                }
                else
                {
                    col++;
                }
                if (canfill[row, col] == 0)
                    continue;
                return true;
            }
        }
        /// <summary>
        /// 移动到下一个可填空位置
        /// </summary>
        /// <returns>是否有下一个可填充位置</returns>
        public bool MovePrev()
        {
            while (true)
            {
                if (col == 0 && row == 0)
                    return false;
                if (col == 0)
                {
                    row--;
                    col = 8;
                }
                else
                {
                    col--;
                }
                if (canfill[row, col] == 0)
                    continue;
                return true;
            }
        }
        /// <summary>
        /// 填充当前位置
        /// </summary>
        /// <returns>如果正常填充则返回true,无法填充或填充无效则返回false</returns>
        public bool TakeAction()
        {
            if (canfill[row, col] == 0)
                return false;
            //尝试填充
            while (true)
            {
                // 填充下一个值
                if (filleddata[row, col] < 9)
                    filleddata[row, col] = filleddata[row, col] + 1;
                else
                {
                    // 重置该值
                    filleddata[row, col] = 0;
                    return false;
                }
                if (Check())
                    return true;
            }
        }
        /// <summary>
        /// 检查冲突
        /// </summary>
        /// <returns></returns>
        private bool Check()
        {
            for (int i = 0; i < 9; i++)
            {
                if (col != i
                    && filleddata[row, col] == filleddata[row, i])
                    return false;
                if (row != i
                    && filleddata[row, col] == filleddata[i, col])
                    return false;
                if (((row / 3 * 3 + i / 3) != row
                    && (col / 3 * 3 + i % 3) != col)
                    && filleddata[row / 3 * 3 + i / 3, col / 3 * 3 + i % 3] == filleddata[row, col])
                    return false;
            }
            return true;
        }
        /// <summary>
        /// Console方式输出数独结果
        /// </summary>
        public void PrintResult()
        {
            Console.WriteLine("data");
            Print(filleddata);
        }
        private void Print(int[,] data)
        {
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    Console.Write(data[i, j] + ", ");
                }
                Console.WriteLine();
            }
        }
        public void PrintCurrent()
        {
            Console.WriteLine("col: " + col + " row: " + row + " data: " + "data:" + filleddata[row, col]);
        }
    }

 

如需尝试新的数独,替换Sudu类构造函数中的数据即可。

 

have fun.

 

posted on 2018-12-26 21:42  EpicBlue  阅读(1628)  评论(0编辑  收藏  举报