可以解决9*9的数独,还可以解决16*16的数独,以及任何理论上可行的多维数独.目前通过了初步测试.
数独的算法属于探测类型算法.与迷宫算法归为一类.按照迷宫算法思路,在编写程序时,没遇到太大的问题.
优化代码时,发现一个奇怪的现象:例如推测一个单元格的数值时,常规思路是先检查这个单元格是否是已知单元格,如果是,直接使用已知数值.如果不是,程序推测数值.这种思路理论上减少了推测,但实际不是这样的.
在编写初期,忽略了利用以上常规思路.即每一个单元格都是通过推测数值.这种思路在解数独时,两到三秒既可得到答案. 后来在检查代码时,觉得如果利用常规思路,算法性能应该能够得到改善(因为减少了推测).但结果是:运行程序五分种,仍然无法得到答案.
猜测原因有两种:
1.如果利用常规思路优化性能是可行的,那么算法本身的设计存在问题,检查代码表达的逻辑于理论是否一致.
2.常规思路不能改善性能,因为常规思路干涉了正常推理,使原本有限,规律的探测变得无规律,复杂化.
2.常规思路不能改善性能,因为常规思路干涉了正常推理,使原本有限,规律的探测变得无规律,复杂化.
总之,这是很有意思的现象,对于推测算法,给程序更少的约束条件,有时会有意想不到的收获.
- // 核心代码
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace shudu
- {
- /// <summary>
- /// 数独计算
- /// </summary>
- class shudu
- {
- int length = 9;
- int[,] Data;
- public shudu(int[,] data , int length) {
- this.Data = data;
- this.length = length;
- }
- /// <summary>
- /// 描述数独一个结点
- /// </summary>
- public class NodeShuDu
- {
- //数独中单元格标识
- public int key ;
- //单元格值
- public int value = 1;
- //结束集合
- public Dictionary<int, bool> dicConstraint;
- }
- public delegate Stack<NodeShuDu> DelNext(Stack<NodeShuDu> next, NodeShuDu node);
- /// <summary>
- /// 计算数独
- /// </summary>
- /// <param name="allAnswer">true, 计算出所有答案 , false 只返回一个答案</param>
- /// <returns></returns>
- public List<int[,]> Computing(bool allAnswer)
- {
- //初使化失败
- if (Data == null)
- return null;
- //生成固定约束
- //固定约束集合(根据已知条件生成约束)
- Dictionary<int, Dictionary<int, bool>> dicConstraint = new Dictionary<int, Dictionary<int, bool>>();
- int row = Data.GetLength(0);
- int col = Data.GetLength(1);
- int _k = 0;
- for (int r = 0; r < row; r++)
- {
- for (int c = 0; c < col; c++)
- {
- if (Data[r, c] != 0)
- {
- //检查已知条件是否合法
- _k = GetKey(r, c);
- if (dicConstraint.ContainsKey(_k) && dicConstraint[_k].ContainsKey(Data[r, c]))
- {
- throw new Exception("已知条件冲突!");
- }
- CreateConstraint(r, c, Data[r, c], dicConstraint);
- }
- }
- }
- DelNext GetNext = (strackNext, node) =>
- {
- int count = length * length;
- int v = 0;
- Dictionary<int, Dictionary<int, bool>> _dicC;
- if (strackNext == null)
- {
- strackNext = new Stack<NodeShuDu>();
- }
- if (node == null)
- {
- node = new NodeShuDu();
- node.key = 0;
- }
- while (strackNext.Count < count)
- {
- int r = 0, c = 0;
- GetRowCol(node.key, ref r, ref c);
- _dicC = null;
- //if (Data[r,c] > 0)
- //{
- // //已知值
- // v = Data[r, c] ;
- //}
- //else
- //{
- // //推测值
- // v = GetNextValue(strackNext, dicConstraint, node.key, node.value);
- //}
- //推测值
- v = GetNextValue(strackNext, dicConstraint, node.key, node.value);
- if (v > 0)
- {
- node.value = v;
- //将推测单元格节点压入堆栈中
- strackNext.Push(node);
- //添加约束
- _dicC = new Dictionary<int, Dictionary<int, bool>>();
- CreateConstraint(r, c, node.value, _dicC);
- node.dicConstraint = new Dictionary<int, bool>();
- foreach (KeyValuePair<int, Dictionary<int, bool>> p in _dicC)
- {
- if (node.dicConstraint.ContainsKey(p.Key))
- continue;
- node.dicConstraint.Add(p.Key, true);
- }
- int lstKey = node.key;
- //初使化下一下单元格
- node = new NodeShuDu();
- node.key = ++lstKey;
- }
- else
- {
- // if (strackNext != null && strackNext.Count > 0 && strackNext.Peek() != null)
- if (strackNext.Count > 0 )
- {
- node = strackNext.Pop();
- node.value++;
- }
- else
- {
- return null;
- }
- }
- }
- return strackNext;
- };
- List<int[,]> lst = new List<int[,]>();
- Stack<NodeShuDu> strackresult = null;
- NodeShuDu startNode = null;
- while ((strackresult = GetNext(strackresult, startNode)) != null)
- {
- NodeShuDu[] result = strackresult.ToArray();
- Array.Reverse(result);
- int[,] arrResult = new int[length, length];
- row = 0;
- col = 0;
- for (int i = 0; i < result.Length; i++)
- {
- GetRowCol(i, ref row, ref col);
- arrResult[row, col] = result[i].value;
- }
- lst.Add(arrResult);
- if (allAnswer)
- {
- startNode = null;
- row = 0;
- col = 0;
- //回朔到第一个程序推测的结点,这个结点值必须 小于 length(保证结点还能进一步推测)
- NodeShuDu next;
- Stack<NodeShuDu> history = new Stack<NodeShuDu> () ;
- while (strackresult.Count > 0)
- {
- startNode = strackresult.Pop();
- history.Push(startNode);
- ;
- }
- if (startNode == null) {
- break;
- }
- GetRowCol(startNode.key, ref row, ref col) ;
- history.Pop();
- while (Data[row, col] > 0)
- {
- strackresult.Push(startNode);
- if (history.Count == 0)
- {
- startNode = null;
- break;
- }
- startNode = history.Pop();
- GetRowCol(startNode.key, ref row, ref col);
- }
- if (startNode == null)
- break;
- startNode.value++;
- }
- else
- {
- break;
- }
- }
- return lst;
- }
- /// <summary>
- /// 单元格推测值
- /// </summary>
- /// <param name="strack">推测值集合</param>
- /// <param name="dicConstraint">约束条件信息</param>
- /// <param name="key">推测单元格</param>
- /// <param name="startValue">推测值</param>
- /// <returns></returns>
- public int GetNextValue(Stack<NodeShuDu> strack ,Dictionary<int, Dictionary<int,bool>> dicConstraint , int key , int startValue ){
- bool r = false;
- while (startValue <= length)
- {
- Dictionary<int, bool> o;
- if (dicConstraint.TryGetValue(key, out o))
- {
- if (o.ContainsKey(startValue))
- {
- startValue++;
- continue;
- }
- }
- //需要遍历完所有的结点
- r = false;
- foreach (NodeShuDu node in strack) {
- if (startValue == node.value && node.dicConstraint.ContainsKey(key))
- {
- startValue++;
- r = true;
- continue;
- }
- }
- if (r)
- continue;
- break;
- }
- if (startValue <= length)
- {
- return startValue;
- }
- else {
- return 0;
- }
- }
- /// <summary>
- /// 将二维下标转为一维下标
- /// </summary>
- /// <param name="row"></param>
- /// <param name="col"></param>
- public int GetKey(int row, int col) {
- return row * length + col;
- }
- /// <summary>
- /// 将一维下标转为二维下标
- /// </summary>
- /// <param name="key"></param>
- /// <param name="row"></param>
- /// <param name="col"></param>
- public void GetRowCol(int key , ref int row, ref int col) {
- row = key / length;
- col = key % length;
- }
- public delegate void AddConstraint(int key );
- /// <summary>
- /// 根据行列值生成约束
- /// </summary>
- public void CreateConstraint(int row, int col, int value, Dictionary<int, Dictionary<int,bool>> constraint)
- {
- if (constraint == null)
- constraint = new Dictionary<int, Dictionary<int,bool>>();
- int ck = GetKey(row, col);
- AddConstraint addConstraint = (k) =>
- {
- if (ck == k)
- return;
- Dictionary<int,bool> result;
- if (!constraint.TryGetValue(k, out result))
- {
- result = new Dictionary<int,bool>();
- constraint.Add(k, result);
- }
- if (!result.ContainsKey(value))
- result.Add(value,true);
- };
- //整行整列约束
- for (int i = 0; i < length; i++)
- {
- addConstraint(GetKey(row, i));
- addConstraint(GetKey(i, col));
- }
- //九宫格约束
- int t = Convert.ToInt16(Math.Sqrt((double)length)) ;
- int startR = row - row % t;
- int startC = col - col % t;
- for (int r = startR; r < startR + t; r++) {
- for (int c = startC; c < startC + t ; c++) {
- addConstraint(GetKey(r, c));
- }
- }
- }
- }
- }
- /调用代码
- int length = 9;
- int[,] data = new int[length, length]; //第一维行索引,第二维,列索引
- // 将已知条件写入data 中
- ......
- shudu shudu = new shudu(data, length);
- //返回结果
- List<int[,]> lst = shudu.Computing(false);