數獨計算(C#)


計算零到多個可能的數獨結果,並打印到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.

 


免責聲明!

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



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