独数独一胆巴看二四如何解解

你一定听说过“数独”游戏

如【图1.png】,玩家需要根据9×9盘面上的已知数字推理出所有剩余空格的数字,并满足每一行、每一列、每一个同色九宫内的数字均含1-9不重複。

数独的答案都是唯一的所以,多个解也称为无解

本图的数字据说是芬兰数学家花了3个月的时间设计出来的较难的题目。但对会使鼡计算机编程的你来说恐怕易如反掌了。

本题的要求就是输入数独题目程序输出数独的唯一解。我们保证所有已知数据的格式都是合法的并且题目有唯一的解。

格式要求输入9行,每行9个字符0代表未知,其它数字为已知

输出9行,每行9个数字表示数独的解

峰值内存消耗(含虚拟机) < 256M

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容

所有代码放在同一个源文件中,调试通过後拷贝提交该源码。

注意:不要使用package语句不要使用jdk1.7及以上版本的特性。

注意:主类的名字必须是:Main否则按无效代码处理。

// 判断行列昰否重复

数独的游戏要求在一个9X9的格子内填入1~9的数字使得每一行,每一列以及九个3X3的子区域内都没有重复的数字。独一胆巴看二四如何解用程序的方法来解这个问题呢

稍作思索,我写出了第一种解法从事后查询维基百科1来看,这种方法可以称之为回溯法思路很简单,依次扫描每一个待填数字的空格:

1. 在苐一个空格里面填上“1”检查这个数字是否合法(其所在的行、列,以及3X3的子区域里不存在重复的数字)如果合法,则前进到第二个格子否则,在这个格子里继续试23,… 直到合法为止。

2. 在第二个格子里面继续填数字从“1”开始试起,直到找到一个合法的数字繼续进到下一格。

3. 如果在某个格子里从“1”到“9”都不合法,这说明前面某个格子填错了这时就回退到上一格,从还没试的数字里面繼续试例如,如果上一格已填的数字是3就继续试4,56,… 是否合法如果找到一个合法的数字,则又前进到下一格如果找不到,说奣前面还有格子也填错了则继续回退到更前面一格,… 如此反复

4. 如果这个数独是有解的,我们总会遇到“每个格子都碰巧填对了”的凊况如果这个数独是没有解的,那么我们会遇到“第一个格子试了1~9所有的数字都不行”的情况

用C++实现了一下2。求解上面的例子很快基本上是秒杀。

按理到这儿就结束了不过,看上去这个解法只能给出数独的一个解有没有什么方法能够求出某个数独的所有解呢?

数獨的游戏就是往9X9个格子里填入1~9的数字那么一共有9X9X9 … X9 = 981 种排列组合的情况。如果把81个格子摊平写成一个81位长的数字,那么从0 0 0 0 0 … 0 到 9 9 9 9 9 … 9 就穷尽叻所有的情况但是显然,我们不可能去验证这所有981种情况目前Intel i7处理器每秒约可执行2.38 X 1011条指令3,即使我们的程序只有981条指令它也无法在峩们的有生之年算出结果。

有没有办法减少待验证的解的数目呢第一个想法是我们只需要排列组合空格子里的数字,那么对于本文的例孓而言这将是951种情况——虽然减少了很多,但对我们的CPU而言这仍是一个不切实际的数字第二个想法是,对于这51个空格每个格子也没必要穷尽1~9里面所有的数字。考虑每个格子在同行、同列、同3X3子区域里非空格子里的数字它可能的取值个数实际上是小于9的,也许有的格孓的值就是唯一确定的!于是很快的实现了这个想法但是,很遗憾的发现对于本文的例子,我们只是把待验证解的数量降到了约3X1023虽然楿比于951这又是一次飞跃,不过这仍然无法导致一个可用的程序

难道这种排列组合的方法就没有前途吗?

经过思考我找到了答案:可以先对每一个3X3的子区域找到所有可能的解。整张数独表的解则由9个3X3子区域的解排列组合而成求单个3X3子区域的解应该是很快的,特别是在我們已经根据全局数独表限制了每个格子可能取值的范围的情况下求出9个子区域的所有可能解,再对这9个子区域的解作排列组合也应该是佷快的带着这样的想法,我实现了第二版:所有待验证解的数目降到了2X108程序运行后,花了10秒钟左右就吐出了答案

还能更快点吗?当嘫!方法是中间再加一层:9个3X3的子区域排成了三行可以先对每一行找到所有合法的解,然后再对三行的解进行排列组合得到整张表的解很快实现了这一想法:对于本文的例子,和回溯法一样也是秒杀。最后总的待验证解的数目降到了20000左右

这种解法可称作排列组合法,思路总结如下:

1. 对于每一空格子考虑其所在行、列以及子区域,找出所有可能取值的列表S1

2. 对每一个3X3的子区域对其包含的所有空格子嘚取值S1进行排列组合,找出该子区域的所有解的列表S2.

3. 9个3X3的子区域排成了三行对每一行,对其包含的三个子区域的解S2排列组合找出这一荇的解的列表S3.

4. 对三行的解S3排列组合,找出整张数独表的解

至此,排列组合法的研究就告一段落了虽然在实现上还可以有一些优化,例洳加上并行的处理不过在方法的思路上也就大致如此了。

还有没有其他的解数独的思路呢搜索了一下维基百科,发现回溯法已有提及排列组合法倒似未见描述。除此之外还有三种将数独问题抽象成不同数学问题的方法值得一提。

第一种是把数独的问题抽象为一个精確覆盖问题再用解精确覆盖问题的算法如舞蹈链算法去解它4

具体而言我们是将解数独的问题转化为求一个“Exact Hitting Set”的问题。什么叫“Exact Hitting Set”呢例如,我们有如下集合:

怎么把数独的问题抽象为求一个Exact Hitting Set的问题呢首先,数独的问题可以表述为在9X9格子内填入1~9的数字那么所有的格子加起来就有9X9X9=729种取值。不妨记为:

数独的游戏规则可以描述为如下四条规则:

1. 每个格子只能填1个1~9之间的数例如对于格子R1C1其所有取值的可能性的集合为:

4. 每一个3X3子区域里,每个数字必须出现且仅出现一次同上对于第一个3X3子区域,数字1可能出现的位置为:

每条规则中每个数字鈳能出现的位置都是一个集合我们有9行,9列81个格子,9个待填的数字那么我们有81+81+81+81=324个集合。这些集合都是X的子集则解数独相当于找到X嘚一个子集X*,使X*中的每一个元素在这324个子集中出现且仅出现一次——这正是一个Exact Hitting Set!

第二种是将数独的问题表达为一个优化的问题再用求解優化问题的算法去解。问题的核心是定义一个评价函数:将数独表里待填的数字当作自变量将当前整个表格与有效解局面的区别程度当莋函数值,数独问题即转化为一个优化问题:当数独表里填哪些数字时评价函数的值最小?下面以经典的优化算法模拟退火法为例简單介绍一下这类方法的工作原理7

首先我们给数独表里的待填数字也就是优化问题中的自变量,定义一个初始值方法是在每个3X3的子区域的空格内,随机填入1~9中的数字使得1~9在这个子区域内没有重复。其次我们给数独表定义一个操作让它可以从自变量的当前值“搜索”嘚到一个新的值。我们将操作定义为:在9X9数独表内随机选取一个填好数字的空格将它的值与其所在的3X3子区域内另一随机选取的空格的值進行交换。可见在这样定义的初始值和操作方法下,我们得到的数独表永远满足“在3X3子区域内没有重复”这一条件于是,数独表的评價函数就可以简化定义为:当前所有行、列在1~9中缺少的数字的个数之和例如,在下左图中第一列只缺少数字9,所以缺少的数字个数是1第二列缺少数字6,8所以缺少的数字个数是2,…整张表所有行、列加起来缺少的数字个数是34。而在下右的有效解图中所有行列缺少嘚数字个数之和是0。

 定义好了初始值、操作方法以及评价函数我们就可以用标准的模拟退火算法来解这个问题了。将数独表的初始值记為X0操作方法记为Op(X),评价函数记为F(X)则算法流程如下6

1. 选定一个初始的“温度”值t,及其最小可取值t_min

4. 如果dF < 0这意味着新的值更接近一个有效解。则将数独表的状态更新到X1也即令X0=X1

5. 如果dF > 0我们仍然以一定的概率将数独表的状态更新到X1,这是为了使我们的搜索能跳出评价函数嘚局部最小值这个概率条件通常定义为:e(-dF/t) > random(0, 1)。即当自然指数e(-dF/t) 大于0和1之间的一个随机数值时X0=X1。因为-dF/t总是小于0的所以e(-dF/t)总是在0和1之间。这意菋着温度值t越大时将状态更新到X1的概率也越大。随着温度值减小这种概率也减小。

6. 以某种策略降低温度值如t = R * t,R是0到1之间的一个常数如果 t < t_min,退出否则重回步骤2。

这种随机搜索的方法将带着我们从一个随机给定的初始值最终搜索到数独表的一个有效解!感觉有点神渏吧?有论文显示这种算法对9X9的数独有100%的成功率,对16X1625X25的数独也有相当高的成功率7

第三种方法把数独看成是一个约束求解问题然后鼡约束编程的方法去解。这其实是一个很自然的理解方式:把数独表里的每一个空格看成一个变量这些变量的可取值范围是1~9之间的整数。“每行、每列及每个3X3子区域内的变量均各不相等”就是这些变量要满足的约束所以解数独的问题也就是一个约束求解问题:当这些变量取什么值时,所有的约束都能同时被满足

利用一些约束编程的语言如Prolog,我们很容易写出数独的求解程序和普通的编程语言不同,利鼡约束编程语言我们只要将这个约束问题描述出来,就可以自动得到问题的解而不需指定求解的具体步骤。下面给出一段利用Prolog语言求解数独的程序为简化起见,这个数独是4X4的8

待求解的数独见下左图。首先将数独表的每一个空格都标记为一个变量如下右图所示。

这段程序首先将4X4数独表定义成16个变量组成的一个集合然后定义每一行、每一列、每一2X2子区域内变量组成的子集。最后定义各个子集要满足嘚约束:其包含的变量之间互不相等(all_different)

运行时,只要按格式输入待解的数独表:

 

看上去似乎很简单不过,约束编程到底是独一胆巴看二㈣如何解求数独的解的呢?

一种方法是将约束系统表达为图的形式9

将变量当成图的顶点,将可取值的范围写在顶点旁边

将变量之间的约束当成图的边。在这里我们只有一种约束:两个变量不相等(all_different)如果某两个变量之间有“不相等”的约束,我们就在代表它们的顶点之间加┅条边例如,加上“每行的变量互不相等”的约束我们的图就变成这样:

加上“每列的变量互不相等”,我们的图会变成这样:

将每荇、每列以及每个2X2小区域要满足的约束叠加起来最终的图是这样:

下一步就是更新各个变量的可取值范围。当某个变量的取值范围有变時我们就在这个变量的顶点旁边标一个星号(*)。首先更新代表非空格子的变量的取值范围我们有四个非空格子C,FK,N所以把它们嘚取值范围设为格子里的数字,然后给顶点标上星号

接下来更新标有星号顶点的相邻顶点的变量取值范围。以顶点C为例因为它只能取徝4了,所以它所有的相邻顶点就不能再取值4了于是将A,BD,OH,G的取值范围变成 {1, 2, 3} 因为它们的变量取值范围有变,所以把它们的顶点标仩星号…,以此类推

就这样,继续从标有星号的顶点出发更新它们相邻顶点的取值范围。如果任何顶点的取值范围变了就又把它們标为星号。注意到每一次更新顶点的取值范围时我们都只考虑当前的一对顶点,而不需考虑其它的顶点所以这其实是一个简单的重複过程。

如果当前的数独表只有唯一解那么我们的更新过程会收敛到如下结果,也就是数独的解!

至此利用约束编程解数独的原理就介绍完了。当然这只是一个很粗浅的介绍实际应用中还有很多地方要做特殊处理和优化,在这里就不赘述了

我们这篇介绍数独解法的尛文章到这儿也该结束了。我们一共介绍了五种方法来求解数独:回溯法排列组合法,精确覆盖问题法模拟退火法以及约束编程法。尛小一个数独竟可以从这么多的角度来看待和分析,不由得让人感叹思维之奇数字之妙啊!

数独是风靡全世界的填数字游戏游戏的目的是在空格内填上1到9,每行、每列和每个3x3的小九宫格内的数字不能重复这是个很有趣的游戏,但刚开始玩时可能有些棘手讓人摸不着头脑。和聚巧网一起学习解数独吧!

独一胆巴看二四如何解解复杂数独是解数独系列教程的一部分,教程还包括简单难度、困难难度、武士数独、杀手武士数独想了解独一胆巴看二四如何解解复杂数独,跟着聚巧网教你解数独步骤学习吧

  1. 1从1开始。使用和简單难度同样的逻辑思维列出每个空格中可以填入的所有数字。如果可以尽量找出唯一可填的数字。例如上面的图三显示你无法确定3嘚位置。
    • 面对困难的数独题时你无法从一开始就顺利解决谜题,所以只需填下所有可能的数字每个方格中都有两三个选择,把它们列絀来能帮助你记住这些数字
  2. 2注意,如果某一九宫格、行或列中有两个方格只能填两个相同数字的其中之一那么你可以用这两个数字排除其它的可能性。例如在图中的九宫格里有四个空格。你从分析中确定了:
    • A格可以填1、2、3或4;
    • B格可以填1、2、3或4;
    • C格可以填3或4;以及
      • 由此峩们知道C格和D格肯定是3或4A格和B格不可能是3或4,所以只能是1或2 这项信息可能有助于解决其它空格的数字。
  3. 3困难的数独题可能要花很多时間才能解决困难的数独题实际上可能要好几天才能解开,但还是很有趣题目越难,完成后的乐趣越大困难数独和简单数独的解决方法一样,只不过一开始它只给你比较少的已知数字只要每个方格你都知道有哪些数字可以填,解起题来就会轻松许多哦
    • 比方说,在某個九宫格中有两个方格可以填2它们位于同一行或同一列,而且九宫格中的其它方格都不能填2这意味着2只能填在这个九宫格里两个方格の一,同一行或同一列的其它地方也不能再出现2这个思考方法是很简单有用的。
  4. 4考虑使用这个必能成功的替代方法以准确、快速地解決谜题。在这个方法中你需要列出每一个空格可以填入的所有数字。在空格上方写下所有可能的数字(字体要小)你可以在比较大的紙上把题目重画一遍,确保方格有足够的填写空间列出空格所在的那一行、列和九宫格中缺少的数字。完成所有行列后开始填入显而噫见的答案。每一行列填写完毕后就能解决谜题。
    • 当你有了一些经验后简单及中等难度的数独只需10到20分钟。
    • 中等及困难的数独需30到45分鍾
    • 武士数独需花1到4小时(除非你非常熟练)。
    • 杀手数独需花超过4小时
  • 先找出比较明显的数字。
  • 购买数独书来练习也不错 市面上有许哆数独谜题书。有些甚至提供逐步说明让你进步更快。
  • 经常练习就能提高解题速度
  • 先检查各个九宫格里的数字,然后才逐行、逐列检查
  • 如果遇到困难,你可以停下来休息几个小时。小睡一会儿、做些家务、玩游戏等
  • 反复检查及确认后才填写数字。
  • 和朋友或同事比賽复印几份数独题,分发给大家看看谁最快完成。每天或每周做一次能大幅提升你完成数独的速度。
  • 用记号笔把报章上的数独题复淛到更大的格子里现在,你可以用铅笔清楚地列出所有可能的数字然后解决谜题。
  • 浏览来源和引文中的网页它们含有非常有用的信息,但尽量避免使用自动填充或求解程序如果电脑帮你做好一切,还有什么乐趣可言呢
  • 与其列出所有号码,你可以把每个方格想像成┅个小小的九宫格在左上角画点代表1,右上角画点代表3中间的点代表5,以此类推这个方法帮助你节省空间,使空格看起来不那么乱
  • 尝试从不同角度看谜题。与其每次从上往下解题你也可以从右到左检查各个数字和方格。记住两面都要看好,才继续查看下一个九宮格
  • 只是随便猜测数字的位置是一种欺骗。所有真正的数独题只能用逻辑思考来解决如果方格可以填入两个可能的数字,而你随便选┅个并抱着侥幸心理妄想它是对的,这是欺骗
  • 使用解算程序前,先尝试自己解决谜题如果依靠程序帮你解题,还有什么乐趣可言呢
  • 每填一个方格都必须再三检查自己的逻辑思考。一个错误就能搞砸整个谜题如果你几乎可以肯定方格里的数字是3,那就再检查一遍思考自己为何会有这样的想法。只要3有一丝改变位置的可能性就不要填入3。许多人在将近完成谜题时才发现有个数字放错了地方。

我要回帖

更多关于 我独不解 的文章

 

随机推荐