分享一款C#解数独的算法

可以解决9*9的数独,还可以解决16*16的数独,以及任何理论上可行的多维数独.目前通过了初步测试.

 
         数独的算法属于探测类型算法.与迷宫算法归为一类.按照迷宫算法思路,在编写程序时,没遇到太大的问题.
        优化代码时,发现一个奇怪的现象:例如推测一个单元格的数值时,常规思路是先检查这个单元格是否是已知单元格,如果是,直接使用已知数值.如果不是,程序推测数值.这种思路理论上减少了推测,但实际不是这样的.
 
        在编写初期,忽略了利用以上常规思路.即每一个单元格都是通过推测数值.这种思路在解数独时,两到三秒既可得到答案. 后来在检查代码时,觉得如果利用常规思路,算法性能应该能够得到改善(因为减少了推测).但结果是:运行程序五分种,仍然无法得到答案.

       猜测原因有两种:
       1.如果利用常规思路优化性能是可行的,那么算法本身的设计存在问题,检查代码表达的逻辑于理论是否一致.
       2.常规思路不能改善性能,因为常规思路干涉了正常推理,使原本有限,规律的探测变得无规律,复杂化.
 
       总之,这是很有意思的现象,对于推测算法,给程序更少的约束条件,有时会有意想不到的收获.
  

 

 

 
 
  1. //  核心代码  
  2. using System;  
  3. using System.Collections.Generic;  
  4. using System.Text;  
  5. namespace shudu  
  6. {  
  7.     /// <summary>  
  8.     /// 数独计算  
  9.     /// </summary>  
  10.     class shudu  
  11.     {  
  12.         int length = 9;  
  13.         int[,] Data;  
  14.         public shudu(int[,] data , int length) {  
  15.             this.Data = data;  
  16.             this.length = length;  
  17.         }  
  18.         /// <summary>  
  19.         /// 描述数独一个结点  
  20.         /// </summary>  
  21.         public class NodeShuDu  
  22.         {  
  23.             //数独中单元格标识  
  24.             public int key  ;  
  25.             //单元格值  
  26.             public int value = 1;  
  27.             //结束集合  
  28.             public Dictionary<intbool> dicConstraint;  
  29.         }  
  30.         public delegate Stack<NodeShuDu> DelNext(Stack<NodeShuDu> next, NodeShuDu node);  
  31.        /// <summary>  
  32.        ///  计算数独  
  33.        /// </summary>  
  34.        /// <param name="allAnswer">true, 计算出所有答案 , false 只返回一个答案</param>  
  35.        /// <returns></returns>  
  36.         public List<int[,]> Computing(bool allAnswer)  
  37.         {  
  38.             //初使化失败  
  39.             if (Data == null)  
  40.                 return null;  
  41.             //生成固定约束  
  42.             //固定约束集合(根据已知条件生成约束)  
  43.             Dictionary<int, Dictionary<intbool>> dicConstraint = new Dictionary<int, Dictionary<intbool>>();  
  44.    
  45.             int row = Data.GetLength(0);  
  46.             int col = Data.GetLength(1);  
  47.             int _k = 0;  
  48.             for (int r = 0; r < row; r++)  
  49.             {  
  50.                 for (int c = 0; c < col; c++)  
  51.                 {  
  52.                     if (Data[r, c] != 0)  
  53.                     {  
  54.                         //检查已知条件是否合法  
  55.                         _k = GetKey(r, c);  
  56.                         if (dicConstraint.ContainsKey(_k) && dicConstraint[_k].ContainsKey(Data[r, c]))  
  57.                         {  
  58.                             throw new Exception("已知条件冲突!");  
  59.                         }  
  60.                         CreateConstraint(r, c, Data[r, c], dicConstraint);  
  61.                     }  
  62.                 }  
  63.             }  
  64.  
  65.             DelNext GetNext = (strackNext, node) =>  
  66.             {  
  67.                 int count = length * length;  
  68.                 int v = 0;  
  69.                 Dictionary<int, Dictionary<intbool>> _dicC;  
  70.                 if (strackNext == null)  
  71.                 {  
  72.                     strackNext = new Stack<NodeShuDu>();  
  73.                 }  
  74.                 if (node == null)  
  75.                 {  
  76.                     node = new NodeShuDu();  
  77.                     node.key = 0;  
  78.                 }  
  79.  
  80.                 while (strackNext.Count < count)  
  81.                 {  
  82.                     int r = 0, c = 0;  
  83.                     GetRowCol(node.key, ref r, ref c);  
  84.                     _dicC = null;  
  85.                     //if (Data[r,c] > 0)  
  86.                     //{  
  87.                     //    //已知值  
  88.                     //    v = Data[r, c] ;  
  89.                     //}  
  90.                     //else  
  91.                     //{  
  92.                     //    //推测值  
  93.                     //    v = GetNextValue(strackNext, dicConstraint, node.key, node.value);  
  94.                     //}  
  95.                     //推测值  
  96.                     v = GetNextValue(strackNext, dicConstraint, node.key, node.value);  
  97.  
  98.                     if (v > 0)  
  99.                     {  
  100.                         node.value = v;  
  101.                         //将推测单元格节点压入堆栈中  
  102.                         strackNext.Push(node);  
  103.                         //添加约束  
  104.                         _dicC = new Dictionary<int, Dictionary<intbool>>();  
  105.                         CreateConstraint(r, c, node.value, _dicC);  
  106.                         node.dicConstraint = new Dictionary<intbool>();  
  107.                         foreach (KeyValuePair<int, Dictionary<intbool>> p in _dicC)  
  108.                         {  
  109.                             if (node.dicConstraint.ContainsKey(p.Key))  
  110.                                 continue;  
  111.                             node.dicConstraint.Add(p.Key, true);  
  112.                         }  
  113.                         int lstKey = node.key;  
  114.                         //初使化下一下单元格  
  115.                         node = new NodeShuDu();  
  116.                         node.key = ++lstKey;  
  117.                     }  
  118.                     else 
  119.                     {  
  120.                        // if (strackNext != null && strackNext.Count > 0 && strackNext.Peek() != null)  
  121.                         if (strackNext.Count > 0 )  
  122.                         {  
  123.                             node = strackNext.Pop();  
  124.                             node.value++;  
  125.                         }  
  126.                         else 
  127.                         {  
  128.                             return null;  
  129.                         }  
  130.                     }  
  131.                 }  
  132.                 return strackNext;  
  133.             };  
  134.  
  135.             List<int[,]> lst = new List<int[,]>();  
  136.             Stack<NodeShuDu> strackresult = null;  
  137.             NodeShuDu startNode = null;  
  138.             while ((strackresult = GetNext(strackresult, startNode)) != null)  
  139.             {  
  140.                 NodeShuDu[] result = strackresult.ToArray();  
  141.                 Array.Reverse(result);  
  142.                 int[,] arrResult = new int[length, length];  
  143.                 row = 0;  
  144.                 col = 0;  
  145.                 for (int i = 0; i < result.Length; i++)  
  146.                 {  
  147.                     GetRowCol(i, ref row, ref col);  
  148.                     arrResult[row, col] = result[i].value;  
  149.                 }  
  150.                 lst.Add(arrResult);  
  151.                 if (allAnswer)  
  152.                 {  
  153.                     startNode = null;  
  154.                     row = 0;  
  155.                     col = 0;  
  156.                      
  157.                     //回朔到第一个程序推测的结点,这个结点值必须 小于 length(保证结点还能进一步推测)  
  158.                     NodeShuDu next;  
  159.                     Stack<NodeShuDu> history = new Stack<NodeShuDu> () ;  
  160.                     while (strackresult.Count > 0)  
  161.                     {  
  162.                        startNode = strackresult.Pop();  
  163.                        history.Push(startNode);  
  164.                    ;  
  165.                     }  
  166.                     if (startNode == null) {  
  167.                         break;  
  168.                     }  
  169.                     GetRowCol(startNode.key, ref row, ref col) ;  
  170.                     history.Pop();  
  171.                     while (Data[row, col] > 0)  
  172.                     {  
  173.                         strackresult.Push(startNode);  
  174.                         if (history.Count == 0)  
  175.                         {  
  176.                             startNode = null;  
  177.                             break;  
  178.                         }  
  179.                         startNode = history.Pop();  
  180.                         GetRowCol(startNode.key, ref row, ref col);  
  181.                     }  
  182.                     if (startNode == null)  
  183.                         break;  
  184.                     startNode.value++;  
  185.                 }  
  186.                 else 
  187.                 {  
  188.                     break;  
  189.                 }  
  190.             }  
  191.             return lst;  
  192.         }  
  193.         /// <summary>  
  194.         /// 单元格推测值  
  195.         /// </summary>  
  196.         /// <param name="strack">推测值集合</param>  
  197.         /// <param name="dicConstraint">约束条件信息</param>  
  198.         /// <param name="key">推测单元格</param>  
  199.         /// <param name="startValue">推测值</param>  
  200.         /// <returns></returns>  
  201.         public int GetNextValue(Stack<NodeShuDu>  strack ,Dictionary<int, Dictionary<int,bool>> dicConstraint , int key , int startValue ){  
  202.             bool r = false;  
  203.             while (startValue <= length)  
  204.             {  
  205.                 Dictionary<intbool> o;  
  206.                 if (dicConstraint.TryGetValue(key, out o))  
  207.                 {  
  208.                     if (o.ContainsKey(startValue))  
  209.                     {  
  210.                         startValue++;  
  211.                         continue;  
  212.                     }  
  213.                 }  
  214.                 
  215.                 //需要遍历完所有的结点  
  216.                 r = false;  
  217.                 foreach (NodeShuDu node in strack) {  
  218.                     if (startValue == node.value && node.dicConstraint.ContainsKey(key))  
  219.                     {  
  220.                         startValue++;  
  221.                         r = true;  
  222.                         continue;  
  223.                     }  
  224.                 }  
  225.                 if (r)  
  226.                     continue;  
  227.                 break;  
  228.             }  
  229.             if (startValue <= length)  
  230.             {  
  231.                 return startValue;  
  232.             }  
  233.             else {  
  234.                 return 0;  
  235.             }  
  236.         }  
  237.         /// <summary>  
  238.         /// 将二维下标转为一维下标  
  239.         /// </summary>  
  240.         /// <param name="row"></param>  
  241.         /// <param name="col"></param>  
  242.         public int GetKey(int row, int col) {  
  243.             return row * length + col;  
  244.         }  
  245.  
  246.         /// <summary>  
  247.         /// 将一维下标转为二维下标  
  248.         /// </summary>  
  249.         /// <param name="key"></param>  
  250.         /// <param name="row"></param>  
  251.         /// <param name="col"></param>  
  252.         public void GetRowCol(int key , ref int row, ref int col) {  
  253.             row = key / length;  
  254.             col = key % length;  
  255.         }  
  256.  
  257.         public delegate void AddConstraint(int key );  
  258.         /// <summary>  
  259.         /// 根据行列值生成约束  
  260.         /// </summary>  
  261.         public void CreateConstraint(int row, int col, int value, Dictionary<int, Dictionary<int,bool>> constraint)  
  262.         {  
  263.             if (constraint == null)  
  264.                 constraint = new Dictionary<int, Dictionary<int,bool>>();  
  265.             int ck = GetKey(row, col);  
  266.             AddConstraint addConstraint = (k) =>  
  267.             {  
  268.                 if (ck  == k)  
  269.                     return;  
  270.                 Dictionary<int,bool> result;  
  271.                 if (!constraint.TryGetValue(k, out result))  
  272.                 {  
  273.                     result = new Dictionary<int,bool>();  
  274.                     constraint.Add(k, result);  
  275.                 }  
  276.                 if (!result.ContainsKey(value))  
  277.                     result.Add(value,true);  
  278.                  
  279.             };  
  280.             //整行整列约束  
  281.             for (int i = 0; i < length; i++)  
  282.             {  
  283.                 addConstraint(GetKey(row, i));  
  284.                 addConstraint(GetKey(i, col));  
  285.             }  
  286.             //九宫格约束  
  287.             int t = Convert.ToInt16(Math.Sqrt((double)length)) ;  
  288.             int startR = row - row % t;  
  289.             int startC = col - col % t;  
  290.             for (int r = startR; r < startR + t; r++) {  
  291.                 for (int c = startC; c < startC + t ; c++) {  
  292.                     addConstraint(GetKey(r, c));  
  293.                 }  
  294.             }  
  295.         }  
  296.     }  
  297. }  
  298.    
  299.    
  300.    
  301. /调用代码  
  302.  int length = 9;  
  303.  int[,] data = new int[length, length]; //第一维行索引,第二维,列索引  
  304. // 将已知条件写入data 中  
  305. ......  
  306.    
  307.  shudu shudu = new shudu(data, length);  
  308.    
  309. //返回结果  
  310.  List<int[,]> lst = shudu.Computing(false); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值