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