dancing links-数独的程序解决方案

        看了高手的实现,我很惭愧。。。

        话说前一阵子玩手机上的白金数独,随着玩的关数越往后,越来越难,一个数独得花费甚至好几个小时,那个时候就让我有了一股写个程序解决的冲动,但是冲动没管用,还是放下了。

        又话说前两天看到百度新闻上说有个60多岁的老头,做出来了传说世界上最难的数独,这个我不关心,我关心的是,新闻里说,有网友用程序实现,在24小时内给解决了。我不是夸他猛,而是觉得24小时解决个破数独,有点给程序员界的人丢脸啊,竟然还上了新闻。

        一怒之下,我也写个试试。

实现一:

        一开始,我用以下思路实现了一个,对于我手机上的我个人觉得挺难的数独,此程序能秒速出结果,但是我将世界上最难的那个数独输入进去后,花了十几秒,让我很震惊(也可能是用perl写的吧,性能差点,但不至于这么慢):

        首先我用生成排列组合的递归过程,来生成每一行的各种可能性,由于已经填入的数据,有一大部分可以剪枝除去,虽然是9的阶乘,但是很快就能求出这些可能性。

        然后,用回溯法在所有的可能性中进行搜索,这也由于数独的规则苛刻,有一大部分能够剪枝除去,因此此处速度也还行。

        但是,上面的算法有个问题是,如果一开始填入的数据比较少,那么时间就成倍的增长了,让人难以接受。于是,我就在网上搜索看别人有没有别的好方法。

        百度了一下,发现很多人都是用类似的方法实现,性能也差不多。但是我注意到有那么几个人提及了一个算法:dancing links。但都是一提而过,没有下文了。

        于是我还是google了一下,发现果然还是外国的文献比较多,竟然有好多人已经用dancing links算法实现了数独的解决:

         http://cgi.cse.unsw.edu.au/~xche635/dlx_sodoku/

         我把上面这个网页提供的代码下下来,编译执行了一下,发现速度惊人,世界上最难的数独,秒速。让我不得不学习一下啊:

关于dancing links:

        一开始讲此算法,首先提及的是说很多人用L(R(x))=L(x), R(L(x))=R(x)删除链表中的元素,但是没注意到可以用L(R(x))=x, R(L(x))=x来恢复链表。但是不知道这个恢复操作的价值到底是什么。上面的网页中给出了比较好的说明:

        When a node is removed in a link list, it is actually removed, in the sense of "the node is not in the linked list anymore". This means, Finding an unfilled column is simply an O(1) operation. Removing a column oce its been "filled" is also an O(1) operation, since you can just cover the header node. When looping through "For every row R that fills this column C", we don't need to loop through every single row of the array to find one that has not been marked "used", we simply traverse the linked list!

        这个恢复操作,能保证你从链表中删除的节点还能恢复回来。而在恢复回来之前,对于整个链表看来,节点确实是已经删除了。如果是这样,在回溯当中,删除无用节点带来的是巨大的剪枝以及减少额外的搜索操作,因此整体上就有了巨大的提升。

        dancing links 算法对于解决精确覆盖问题有奇效(什么事精确覆盖?看算法给出的例子),而为了将某些问题(比如数独)转化成这种问题,还是需要你好好动动脑子的,对于问题的转化,上面的网页给出了很好的说明,大致如下:

         将要求作为列,将动作作为行,那么就能构造出同dancing links例子的矩阵,比如对于数独的要求有四个:

         1.每行都必须有数字1到9

         2.每列都必须有数字1到9

         3.每个单元都必须有数字1到9

         4.每个格子有且只能有一个数(为什么得有这个要求?一个格子不能放多个数)

         上面的要求总共有,9*9 + 9*9 + 9*9 + 9*9 = 324种

          动作只有一个:

          1.往某行某列中放置数字1-9

         动作共有9*9*9=729种可能,而每一个动作都能解决上面要求(324个)中的四个,于是就能得到一个324*729的0,1矩阵,也就得到了dancing links算法需要的矩阵。

剩下的,就是构造数据结构,代入dancing links算法来解决问题了。


在算法过程中要注意一下几个地方:

          1.链的删除是整块的删除,而不是节点的删除,对于列,只要跟其余的列脱离关系就行了;而对于行也是同样。没有必要把某一个节点上下左右都断开。那样恢复没法恢复,操作也复杂。

          2.选择某一列进行算法开始的选择,在有些时候是很有帮助的(因此,上面网页中程序的实现中,虽然很简单,但是还是将选择单独抽成了一个函数)

          3.如果是用C,手动开辟的空间,要想不内存泄露,你就得万分小心了。也多亏这个程序执行完也就退出了,所以没啥影响。


        还有我想感叹一下,国内讨论技术的感觉好少啊,不懂装懂的人倒是不少。平时工作中查找关于samba呀DNS,等等等等,百度N下都不管用,还是谷歌啊。百度一下,除了转载的就是复制的啊。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值