数独顾名思义——每个数字只能出现一次。数独是一种源自18世纪末的瑞士,后在美国发展、并在日本得以发扬光大的数字谜题。数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次。 这种游戏全面考验做题者观察能力和推理能力,虽然玩法简单,但数字排列方式却千变万化,所以不少教育者认为数独是训练头脑的绝佳方式。
关于数独的算法有很多种,最基本的有基拙挖掘法,唯一解法,侯选数置换法.查了很多资料但都没有找到完整的算法实现.花了30块银子到当当网上买了<<编程之美>>找到其中的章节,看完之后大失所望.其中的算法实现藏头露尾,寥寥几行.让人看的云里雾里.连类定义都没有给全.
这里只好自己用基拙挖掘法实现了一个完整的算法,现共享之.其核心思想是循环在每个单元格尝试如果能找到合适的数则前进.如果找不到则进行回朔.下面是代码实现.
如有bug 请一定告之,呵呵.mail:bingbing200x@163.com
* 算法思想:
* 1.[数独的规则]是在每一行,每一列,以及每一个3*3的九宫格内都不能出现重复的数字,备选数字从1-9.
* 关于数独的具体规则请看这一篇文章:http://blog.csdn.net/kabini/archive/2008/06/30/2598524.aspx
* 2.为了数独的规则,则设置三个二维数组元素来记录行,列以及当前索引(x,y)所在的3*3九宫格内出现过的数字
* 例如:rowExistArr[0,2]==1则表示在第0行,2这个数字已经出现,所以第0行不能再出现2这个数字
* colExistArr[0,2]==1 则表示在第0列,2这个数字已经出现,所以第0列不能再出现2这个数字
* mtxExistArr[0,2]==1,则表示在索引为0的3*3九宫格内,2这个数字已经出现,3*3九宫格图解请
* 看GetMtxIndex方法上的注释.
* 3.进行数独运算前会有一些预先初始的数字, 这些数字是不能改变的, IsInitValue==true.每一个单元格对象
* SingleNumberCell ,都会有CellNumChoices对象这里记录这个当元格可供选择的数字列表。如果一个备选数字
* 违反了数独的规则,则在备选列表中选择下一个数字。如果全都违反数独规则,则把当前对象销毁掉回朔到上一个
* 单元格把上一个单元格的数字抹去重新从备选对象中选择下一个数字。IsProcessed==true的cell对象都是回退时
* 经过的对象。
public class SingleNumberMatrix { #region 内部类 private class CellNumChoices { private Queue<int> _choices; public CellNumChoices(Queue<int> choices) { _choices = choices; } public int GetOneNum() { if (_choices.Count > 0) return _choices.Dequeue(); return SingleNumberMatrix.EMPTY; } } /// <summary> /// 数独矩阵中的单元格 /// </summary> private class SingleNumberCell { private int _xPos; private int _yPos; private int _value; private bool _bIsProcessed; private bool _bIsInitValue; private CellNumChoices _cellChoices; public SingleNumberCell(int x, int y, Queue<int> choices) { _xPos = x; _yPos = y; _bIsProcessed = false; _bIsInitValue = false; _cellChoices = new CellNumChoices(choices); } /// <summary> /// 从后选数组中找到一个可用的数,如果返回0,则需要进行回朔 /// </summary> /// <returns></returns> public bool GetOneChoice() { _value = _cellChoices.GetOneNum(); return _value > SingleNumberMatrix.EMPTY; } public int X { get { return _xPos; } set { _xPos = value; } } /// <summary> /// 当前元素是否已处理过 /// </summary> public bool IsProcessed { get { return _bIsProcessed; } set { _bIsProcessed = value; } } /// <summary> /// 是否是初始化时已存在的 /// </summary> public bool IsInitValue { get { return _bIsInitValue; } set { _bIsInitValue = value; } } public int V { get { return _value; } set { _value = value; } } public int Y { get { return _yPos; } set { _yPos = value; } } } #endregion private const int xCount = 9, yCount = 9; public const int EMPTY = 0; /// <summary> /// 备选数字 /// </summary> private int[] numChoices = new int[9] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; /// <summary> /// x轴行出现的数字 /// </summary> private int[,] rowExistArr = new int[xCount, yCount + 1]; /// <summary> /// y轴列出现的数字 /// </summary> private int[,] colExistArr = new int[xCount, yCount + 1]; /// <summary> /// 3*3一组的九个单元格内出现的数字 /// </summary> private int[,] mtxExistArr = new int[xCount, yCount + 1]; /// <summary> /// 数据矩阵 /// </summary> private int[,] _matrix; SingleNumberCell[,] _cellsMatix; /// <summary> /// 索引项 /// </summary> private int xIndex, yIndex; public SingleNumberMatrix(int[,] matrix) { _matrix = matrix; _cellsMatix = new SingleNumberCell[xCount, yCount]; } // 1 2 3 | 4 5 6 | 7 8 9 ->y // 4 5 6 | 7 8 9 | 1 2 3 // 7 8 9 | 1 2 3 | 4 5 6 // ——————– // 2 3 4 | 5 6 7 | 8 9 1 // 5 6 7 | 8 9 1 | 2 3 4 // 8 9 1 | 2 3 4 | 5 6 7 // ——————– // 3 4 5 | 6 7 8 | 9 1 2 // 6 7 8 | 9 1 2 | 3 4 5 // 9 1 2 | 3 4 5 | 6 7 8 public bool Run() { if (ValidMatrix(_matrix)) { for (xIndex = 0; xIndex < xCount; xIndex++) { for (yIndex = 0; yIndex < yCount; yIndex++) { if (null == this._cellsMatix[xIndex, yIndex]) { SingleNumberCell cell = new SingleNumberCell(xIndex, yIndex, GetCurChoices()); if (cell.GetOneChoice()) { this.SaveMtxState(cell.V); _cellsMatix[xIndex, yIndex] = cell; cell.IsProcessed = true; } else BackIndex(); } else { SingleNumberCell cell = _cellsMatix[xIndex, yIndex]; if (cell.IsInitValue) { if (cell.IsProcessed) BackIndex(); else cell.IsProcessed = true; continue; } this.ClearMtxState(cell.V); if (cell.GetOneChoice()) { this.SaveMtxState(cell.V); } else { _cellsMatix[xIndex, yIndex] = null; BackIndex(); } } }//end yIndex }//end xIndex Console.Write(“Finished! congratulations to you ~!”); Console.WriteLine(); return true; } else { Console.Write(“error:{rownum:” + xIndex.ToString() + “|colnum:” + yIndex.ToString() + “}”); return false; } } /// <summary> /// 回朔 /// </summary> private void BackIndex() { if (yIndex == 0) { if (xIndex > 0) { xIndex–; yIndex = yCount – 2; } } else yIndex -= 2; } public void Print() { Console.Write(” 0 1 2 3 4 5 6 7 8″); Console.WriteLine(); Console.Write(“______________________________”); Console.WriteLine(); for (xIndex = 0; xIndex < xCount; xIndex++) { string str = string.Empty; for (yIndex = 0; yIndex < yCount; yIndex++) { if (yIndex == 0) str += xIndex.ToString() + ” | “; str += this._cellsMatix[xIndex, yIndex].V.ToString(); str += ” “; } Console.WriteLine(str); } } //[0,0] [0,1][0,2][0,3] [0,4][0,5][0,6] [0,7][0,8] ->y //[1,0] [1,1][1,2][1,3] [1,4][1,5][1,6] [1,7][1,8] //[2,0] [2,1][2,2][2,3] [2,4][2,5][2,6] [2,7][2,8] //[3,0] [3,1][3,2][3,3] [3,4][3,5][3,6] [3,7][3,8] //[4,0] [4,1][4,2][4,3] [4,4][4,5][4,6] [4,7][4,8] //[5,0] [5,1][5,2][5,3] [5,4][5,5][5,6] [5,7][5,8] //[6,0] [6,1][6,2][6,3] [6,4][6,5][6,6] [6,7][6,8] //[7,0] [7,1][7,2][7,3] [7,4][7,5][7,6] [7,7][7,8] //x //[8,0] [8,1][8,2][8,3] [8,4][8,5][8,6] [8,7][8,8] public static void Test() { int xCount = 9, yCount = 9; int[,] matrix = new int[xCount, yCount]; matrix[0, 0] = 8; matrix[2, 8] = 8; matrix[3, 3] = 8; matrix[4, 2] = 8; matrix[8, 4] = 8; SingleNumberMatrix si = new SingleNumberMatrix(matrix); try { if (si.Run()) si.Print(); } catch (Exception e) { Console.Write(e.Message); } } /// <summary> /// 根据当前索引,从备选数组中选一所有合法的数 /// </summary> /// <returns></returns> private Queue<int> GetCurChoices() { Queue<int> que = new Queue<int>(); foreach (int temp in numChoices) { if (ValidNum(temp)) { que.Enqueue(temp); } } return que; } /// <summary> /// 检测当前数,在行,列以及九宫格为单位的区域的合法性 /// </summary> /// <param name=”temp”></param> /// <returns>true:代表当前数可用,false:代表当前数不可用</returns> private bool ValidNum(int temp) { if (rowExistArr[xIndex, temp] != EMPTY) return false; if (colExistArr[yIndex, temp] != EMPTY) return false; if (mtxExistArr[GetMtxIndex(), temp] != EMPTY) return false; return true; } /// <summary> /// 根据行,列获取当前x,y索引元素 /// 所在 3*3九宫格的索引 //0 0 0 | 0 0 0 | 0 0 0 //0 0 0 | 0 1 0 | 0 2 0 //0 0 0 | 0 0 0 | 0 0 0 // ——————– //0 0 0 | 0 0 0 | 0 0 0 //0 3 0 | 0 4 0 | 0 5 0 //0 0 0 | 0 0 0 | 0 0 0 // ——————– //0 0 0 | 0 0 0 | 0 0 0 //0 6 0 | 0 7 0 | 0 8 0 //0 0 0 | 0 0 0 | 0 0 0 /// </summary> /// <returns></returns> private int GetMtxIndex() { int xMatIndex = xIndex / 3; int yMatIndex = yIndex / 3; return xMatIndex * 3 + yMatIndex; } /// <summary> /// 记录矩阵的状态,行,列,以及九宫格的占用 /// </summary> private void SaveMtxState(int temp) { rowExistArr[xIndex, temp] = 1; colExistArr[yIndex, temp] = 1; mtxExistArr[GetMtxIndex(), temp] = 1; } private void ClearMtxState(int temp) { rowExistArr[xIndex, temp] = SingleNumberMatrix.EMPTY; colExistArr[yIndex, temp] = SingleNumberMatrix.EMPTY; mtxExistArr[GetMtxIndex(), temp] = SingleNumberMatrix.EMPTY; } /// <summary> /// 验证矩阵的合法性,并对行,列,以及九宫格出现的数字进行标记 /// </summary> /// <param name=”matrix”></param> /// <returns></returns> private bool ValidMatrix(int[,] matrix) { for (xIndex = 0; xIndex < xCount; xIndex++) { for (yIndex = 0; yIndex < yCount; yIndex++) { int temp = matrix[xIndex, yIndex]; if (temp == EMPTY) continue; if (!ValidNum(temp)) return false; else { SingleNumberCell cell = new SingleNumberCell(xIndex, yIndex, null); cell.V = temp; cell.IsInitValue = true; this._cellsMatix[xIndex, yIndex] = cell; SaveMtxState(temp); } } } return true; } }