计算零到多个可能的数独结果,并打印到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.