逆夏de流年吧 关注:15贴子:347

新生研讨课之舞蹈链(Dancing links X)解数独(c语言版)!!!

只看楼主收藏回复

众所周知使用舞蹈链结构,将数独的暴力穷举问题转化为精确覆盖问题再回溯求解,是目前解数独相当快速的一种方法。其实我们早对此方法有所研究,但cggxgg过于羞涩一直没发(我也不敢发他的代码),我就只好把我做的工作发在这里了。我只是把他的c++代码翻译成了c语言的,后续还会用Java语言再写一遍以用于APP。
代码见2楼百度网盘链接


IP属地:湖北1楼2020-01-22 19:49回复
    操作方法大致是这样:
    1.创建一个名为“problem.txt”的文本文件,将待解数独(81个数字,空格用0代替)写到这个文本文件中;
    2.将这个文本文件和c文件放在一个文件夹下,然后就可以编译运行了。


    IP属地:湖北3楼2020-01-22 19:50
    回复
      解数独的速度是很快的,大家自己体验就好,下面我详细分析下这个舞蹈链的算法过程


      IP属地:湖北4楼2020-01-22 19:50
      回复
        大致思路可以分为以下几个步骤:
        1.利用数独的4个约束条件,将数独转化为一个01矩阵,每行有4个1,其余全为0,从而转化为一个精确覆盖问题;
        2.建立行、列对象链表,再根据01矩阵建立双向十字循环链表,其中将每行、列节点个数储存在行、列对象节点中;
        3.利用dancing links X求解这个精确覆盖问题,得到81行,将这些行的行数储存到result数组中;
        4.将得到的81行翻译成81个数,填入原数独的格子中,Q.E.D。


        IP属地:湖北5楼2020-01-22 19:50
        回复
          数独的4个约束条件是这样的:
          1、每个格子只能填一个数字
          2、每行1~9恰好填一遍
          3、每列1~9恰好填一遍
          4、每宫1~9恰好填一遍
          因此,如果我们定义81*4=324列,其中:
          1~81列:
          第1列定义成:(1,1)填了一个数字
          第2列定义成:(1,2)填了一个数字
          ……
          第9列定义成:(1,9)填了一个数字
          第10列定义成:(2,1)填了一个数字
          ……
          第18列定义成:(2,9)填了一个数字
          ……
          第81列定义成:(9,9)填了一个数字
          82~162列:
          第82列定义成:在第1行填了数字1
          第83列定义成:在第1行填了数字2
          ……
          第90列定义成:在第1行填了数字9
          第91列定义成:在第2行填了数字1
          ……
          第99列定义成:在第2行填了数字9
          ……
          第162列定义成:在第9行填了数字9
          163~243列:
          第163列定义成:在第1列填了数字1
          第164列定义成:在第1列填了数字2
          ……
          第171列定义成:在第1列填了数字9
          第172列定义成:在第2列填了数字1
          ……
          第180列定义成:在第2列填了数字9
          ……
          第243列定义成:在第9列填了数字9
          244~324列:
          第244列定义成:在第1宫填了数字1
          第245列定义成:在第1宫填了数字2
          ……
          第252列定义成:在第1宫填了数字9
          第253列定义成:在第2宫填了数字1
          ……
          第261列定义成:在第2宫填了数字9
          ……
          第324列定义成:在第9宫填了数字9
          至此,我们便将这4个约束条件转化成了一个1x324的行向量,如果对于原数独中81个格子,有数的格子转化为一个行向量,没有数的格子转化为9个行向量(分别假设填入1~9),于是我们得到一个有324列,最多81*9行的矩阵。至此,我们将问题转化为一个精确覆盖问题,即找到81行,使每行的1恰好不重复的出现在324列之中。下一步,我们把这个问题的求解交给dancing links X解决。


          IP属地:湖北6楼2020-01-22 19:50
          回复
            Dancing Links X(DLX)舞蹈链算法:
            链表建立的思路和详情我用一张图来解释:(可与代码对照理解)


            IP属地:湖北来自Android客户端7楼2020-01-22 19:53
            回复
              通过上一楼的图可以很容易理解dancing links X的具体过程,其余见代码,不再赘述


              IP属地:湖北8楼2020-01-22 19:53
              回复
                关于精确覆盖问题的定义和舞蹈链算法的思路,我提供一个很好的链接。博主讲的十分详尽透彻。
                https://www.cnblogs.com/grenet/p/3145800.html


                IP属地:湖北9楼2020-01-22 19:53
                回复
                  最后的将81个行向量翻译成数独很简洁:只需计算出这一行表示的行列坐标和该行根据假设填入的数即可(这只需分别找到1~81列和82~162列中的1即可)。


                  IP属地:湖北10楼2020-01-22 19:53
                  回复
                    总而言之,Dancing Links X是一个非常巧妙的算法,理解不难,但实现起来过程很容易出错。


                    IP属地:湖北11楼2020-01-22 19:53
                    回复
                      今天又增加了利用舞蹈链进行多解性判断的功能,大致思路是:找到一个解当做没找到这个解,继续往后找,直到找到另一个解,如果找到了第二个解就认为多解,否则认为唯一解。为此新增一个ansnum记录解的个数,新增一个flag记录是否找到了解,则只需将源代码的
                      if(Search()) return true;
                      改为
                      if(Search()){
                      ansnum++;
                      flag=true;
                      if(ansnum==1){
                      for(int i=0;i<81;i++)
                      result2[i]=result[i];
                      result[80]=0;
                      }
                      if(ansnum>=2) return true;
                      }
                      即可,至于输出部分根据ansnum加个条件判断即可。


                      IP属地:湖北12楼2020-01-23 23:35
                      回复
                        用Java实现DLX计划由我们组cgg同学完成,下一步我打算设计基于DLX解数独工具的数独生成算法和难度判定算法。


                        IP属地:湖北13楼2020-01-23 23:38
                        回复
                          弱弱地问一下,怎么没有2楼orz


                          IP属地:河南14楼2020-05-17 19:37
                          收起回复
                            弱弱地问一下,怎么没有二楼


                            IP属地:北京来自Android客户端15楼2020-05-21 16:45
                            回复
                              这两天看到数组模拟链表的文章深有感触,于是我用数组模拟重新把DLX写了一遍,用的是c++语言,由于类的声明和微秒级计时占用了大量篇幅,实际上执行DLX功能的代码加起来差不多90行。


                              IP属地:湖北17楼2020-06-03 08:45
                              回复