【www.shanpow.com--数字歇后语】
数独的解法一:数独解法
数独解法
2009-04-06 12:27:34| 分类: 默认分类 |字号 订阅
唯一解法
前言
数独这个数字解谜游戏,完全不必要用到算术!会用到的只是推理与逻辑。刚开始接触数独时,即使是只 须用到"唯一解"技巧的简易级谜题,就已可让我们焦头烂额了,但是随着我们深陷数独的迷人世界之后,这 类简易级的数独谜题必定在短时间内难再使我们获得征服的满足。于是,当我们逐步深入、进阶到更难的游 戏后,我们将会需要发展龈?多的解谜技巧。虽然最好的技巧便是我们自己发现的窍门,这样我们很容易??能记住它们,运用自如,不需要别人来耳提面命。但是如果完全不去观摩学习他人发展出来的技巧,而全靠 自己摸索,那将是一个非常坚苦的挑战,也不是正确的学习之道!所以让我们一齐来探讨数独的解谜方法吧!
数独的解谜技巧,刚开始发展时,以直观式的唯一解及摒除法为主,对于初入门的玩家来说,这也是一般人 较容易理解、接受的方法,对于一般简易级或中级的数独谜题,如果能灵活运用此二法则,通常已游刃有余。
唯一解详说
当数独谜题中的某一个宫格因为所处的列、行或九宫格已出现过的数字已达 8 个,那么这个宫格所能填入 的数字就剩下这个还没出现过的数字了。
<图 1> (9, 8)出现唯一解了
<图 1>是最明显的唯一解出现时机,请看第 8 行,由 (1,8) ~(8,8) 都已填入数字了,只剩(9,8)还是 空白,此时(9,8)中应填入的数字,当然就是第 8 行中还没出现过的数字了!请一个个数字核对一下, 哦!是数字 8 还没出现过,所以(9,8) 中该填入的数字就是数字 8 了。
<图 2> (8, 9)出现唯一解了
<图 2>是另一个明显出现唯一解的情形,请看第 8 列,由 (8,1) ~(8,8) 都已填入数字了,只剩(8,9)还是 空白,此时(8, 9)中应填入的数字,当然就是第 8 列中还没出现过的数字了!请一个个数字核对一下, 哦!是数字 9 还没出现过,所以(8, 9) 中该填入的数字就是数字 9 了。
<图 3> (7, 5)出现唯一解了
<图 3>是另一种明显出现唯一解的情形,请看下中九宫格,在这个九宫格中除了(7, 5)还是空白外,其他宫格 都已填有数字了,所以(7, 5)中应填入的数字,当然就是下中九宫格中还没出现过的数字了!请一个个数字核对一下, 哦!是数字 1 还没出现过,所以(7, 5) 中该填入的数字就是数字 1 了。
<图 4> 一般情形下的唯一解
类似 <图 1>~<图 3>这种明显出现唯一解的情形,在一般情形之下及解题初期是不太可能出现的! <图 4>是一个最典型的简易级数独谜题,如果单纯观察某一个行、列或九宫格,没有一处是已出现 8 个数字的, 难道如此就无解了吗?非也!非也!在此图中,出现唯一解的宫格其实有 3 处之多!你能找出来吗?
没错,在一般情形之下及解题初期,唯一解的寻找必须综合所处的行、列及九宫格三者,同时过滤筛选出已出现 的数字才行!如果漏掉其一,可能就无法找出唯一解的出现位置了。现在且不忙着填入数字,先来找找看<图 4>中 目前已出现的唯一解在哪儿吧:
第一个唯一解位置在(2, 3):(2, 3) 所处的第 2 列中已出现的数字是:9、3、5、7。所处的第 3 行中 已出现的数字是:4、2、6、8。至于所处的上左九宫格中,已出现的数字是:2、9、4。所以综合而言, 受其所处位置的行、列及九宫格影响,不得再使用并填入(2, 3) 的数字计有:2、3、4、5、6、7、8、9。 能用来填入的数字确实只剩数字 1 这个唯一的解了。
第二个唯一解位置在(8, 7):(8, 7) 所处的第 8 列中已出现的数字是:1、2、8、6。所处的第 7 行中 已出现的数字是:3、9、5、4。至于所处的下右九宫格中,已出现的数字是:4、6、5。所以综合而言, 受其所处位置的行、列及九宫格影响,不得再使用并填入(8, 7) 的数字计有:1、2、3、4、5、6、8、9。 能用来填入的数字确实只剩数字 7 这个唯一的解了。
第三个唯一解位置在(5, 5):(5, 5) 所处的第 5 列中已出现的数字是:1、7。所处的第 5 行中 已出现的数字是:2、5。至于所处的中央九宫格中,已出现的数字是:3、6、8、9。所以综合而言, 受其所处位置的行、列及九宫格影响,不得再使用并填入(5, 5) 的数字计有:1、2、3、5、6、7、8、9。 能用来填入的数字确实只剩数字 4 这个唯一的解了。
以上所谓的三个唯一解位置,是以<图 4>现况未填入任何数字之前而言,如果开始填入数字,出现唯一解的位置 可能将随之增加。例:当(8, 7) 填入数字 7 之后,(7, 7)将出现唯一解 1;如果再将数字 1 填入(7, 7), 在(7, 8)又将出现唯一解 3;......如此不断循环下去,就可以将整个谜题解出了。
唯一候选数法
概说
依照候选数法概说一文中,候选数表的制作规则,我们可以知道:可以填入某一 个宫格的数字,一定会列于该宫格的候选数中;不在候选数中的数字,就不能填入该宫格中。
所以如果在候选数表中发现某一个宫格的候选数仅有 1 个数字,那就是表示:不必再考虑了!这个宫格就是 只能填入这个数字啦!如果填入别的数字,就会违反数独的填制规则的。
利用“找出候选数表中,候选数仅有 1 个数字的宫格来,并填入该候选数”的方法就叫做唯一候选数法(Singles Candidature, sole Candidate)。
唯一候选数法示例
<图 1>数独谜题的候选数表
<图 1> 是我们在候选数法概说一文中完成的候选数表,其中有好几个宫格的候选数 都只有 1 个,所以可以利用唯一候选数法来进行填制。先还不要填入数字,我们先来找找看,有哪些宫格有 唯一候选数?
在 (2, 7) 有唯一候选数 7。
在 (5, 5) 有唯一候选数 5。
在 (8, 3) 有唯一候选数 3。
哇!同时出现了 3 个唯一候选数啊!那么,先填入哪一个会不会影响填制结果呢?当然不会了, 只要你高兴,喜欢先填哪一个都没问题的。
好,就在这 3 个宫格中填入他们的唯一候选数吧,填制结果如<图 2>:
<图 2>
哇!又有唯一候选数出现了呢!没错,一般简易级的数独谜题,如果使用直观式的 唯一解法及摒除法来解题,即使是数独老手,也要花费相当的工夫才能完成; 但是如果采用唯一候选数法,从候选数表制作完成开始,唯一候选数将一个一个接连不断的出现,轻轻松松的 就可以完成解题啦!<图 3> 是 <图 1> 的完成解。
<图 3>完成解
隐性三链数删减法
概说
遇到了高级、困难级的数独谜题,使得唯一候选数法和隐性唯一候选数法黔驴技穷的时候,就是各种删减法上场的时机了。在各种的删减法中,哪一个要先用 是随个人之喜好的,并无限制。本页介绍的例子当然可用其他删减法完成解题,但还是要以隐性三链数删减法优先??!
<图 1>
请看<图 1>的第 2 列,数字 1、7、8 只出现在(2, 1)、(2, 7)和(2, 8)这三个宫格的候选数中;这时 隐性三链数删减法的条件已成立了!这表示第 2 列的数字 1、7 和 8 将只能填到这三个宫格中,因为: 如果让别的数字填入这三个宫格之中后,这三个相异的数字能填入的可能宫格就只剩下两个,而那是 不可能的事!所以若这三个宫格的候选数中还有其他数字,全部是多余无用的,它们已不可能再用来 填入这些宫格中了,所以可以毫不考虑的把它们删减掉。于是(2, 7)和(2, 8)这两个宫格候选数中的 6 都可被安全的删减掉;其中(2, 7)的候选数少了数字 6,将使得(8, 7)出现行隐性唯一候选数 6 ,于是 可用隐性唯一候选数法来填入下一个解了。
整理一下:
当某 3 个数字仅出现在某列的某三个宫格候选数中时,就可以把这三个宫格的候选数删减成该 3 个数字。
同理,当某 3 个数字仅出现在某行的某三个宫格候选数中时,就可以把这三个宫格的候选数删减成该 3 个数字。
当然,当某 3 个数字仅出现在某个九宫格的某三个宫格候选数中时,就可以把这三个宫格的候选数删减成该 3 个数字。
利用“找出某 3 个数字仅出现在某行、某列或某一个九宫格的某三个宫格候选数中的情形,进而将这三个 宫格的候选数删减成该 3 个数字”的方法就叫做隐性三链数删减法(Hidden Triples)。
本法其实为隐性数对删除法的推广,而且还可以继续加以推广:
隐性四链数删减法就是:“找出某 4 个数字仅出现在某行、某列或某一个九宫格的某四个宫格候选数中 的情形,进而将这四个宫格的候选数删减成该 4 个数字”的方法。
隐性五链数删减法就是:“找出某 5 个数字仅出现在某行、某列或某一个九宫格的某五个宫格候选数中 的情形,进而将这五个宫格的候选数删减成该 5 个数字”的方法。
......
如果愿意的话,你确实是可以这样推广的,只是,实用上是否有其应用的价值或空间呢?
隐性三链数删减法示例
隐性三链数删减法一共有 3 种状况:第一种发生在行、第二种是发生在列、第三种则发生在九宫格。<图 1> 就是 发生在列的例子了,其他的情况举例如下:
<图 2>
<图 2> 是隐性三链数删减发生在行的例子:图中第 4 行的数字 2、4、9 只出现在 (4, 4)、(5, 4)及(6, 4) 这三个宫格的候选数中,所以可以将三个宫格候选数中 2、4、9 以外的数字安全的删减掉,(4, 4)的候选数删减成2、4; (5, 4)的候选数删减成2、4、9;(6, 4)的候选数删减成 9;出现了唯一候选数啦!
<图 3>
<图 3> 是隐性三链数删减发生在九宫格的例子:图中中央九宫格的数字 2、5、9 只出现在 (5, 4)、(5, 6)及(6, 4) 这三个宫格的候选数中,所以可以将三个宫格候选数中 2、5、9 以外的数字安全的删减掉, (5, 4)的候选数删减成2、5、9;(5, 6)的候选数删减成2、5;(6, 4)的候选数删减成 9;出现了唯一候选数啦!
<图 4>
像 <图 1>~<图 3> 这样只经一次删减就出现下一个解的情况当然不错了,但有时可没法这样顺心, <图 4> 就是一个例子。下一个解将出现在(5, 6) 这个宫格,你能找出该填入什么数字吗?
以目前所学到的方法,要解出下一个解,需要二个步骤:
先看中左九宫格吧!由于只剩(5, 1)~(5, 3)这个区块尚未填入数字,所以可用区块删减法将 第 5 列其他区块候选数中的 1、3、4 全部删减掉,但实际上仅能删到(5, 4)及(5, 6)候选数的数字 4 而已。
接下来请观察第 6 行! 由于数字 1、4、9 只出现在 (2, 6)、(8, 6)及(9, 6) 这三个宫格的候选数中 [因为(5, 6)的候选数在上一步骤中已被删减为5、8 了 ], 所以可用隐性三链数删减将三个宫格候选数中 1、4、9 以外的数字安全的删减掉, (2, 6)的候选数删减成1、4、9;(9, 6)的候选数没变;(8, 6)的候选数则由 2、4、5、8、9 删减成 4、9; 由于 5 被删减掉了,使得(5, 6) 出现了行隐性唯一候选数5啦!
隐性数对删减法
概说
遇到了高级、困难级的数独谜题,使得唯一候选数法和 隐性唯一候选数法黔驴技穷的时候,就是各种删减法上场的时机了。在各种的删减法中,哪一个要先用 是随个人之喜好的,并无限制。本页介绍的当然就要以隐性数对删减法优先??!
<图 1>
请看<图 1>的上右九宫格,数字 8、9 都只出现在(2, 8)和(2, 9)这两个宫格的候选数中;这时隐性数对删减法 的条件已成立了!这表示上右九宫格的数字 8 和 9 将只能填到这两个宫格中,而且:如果数字 8 将填入(2, 8), 那么(2, 9)就一定要填入数字 9;反之,如果数字 9 将填入(2, 8),那么(2, 9)就一定要填入数字 8; 不论哪一个状况出现,(2, 8)和(2, 9)这两个宫格的候选数中若还有其他数字,全部是多余无用的,因为这 两个宫格若填入数字 8、9 以外的数字,那么上右九宫格的数字 8 或 9 就将无处可填了。候选数的意义是 可能填入该宫格的数字,而这两个数字以外的数字已不可能再用来填入本宫格中了,所以可以毫不考虑的把 它们删减掉。当(2, 8)和(2, 9)这两个宫格的候选数都安全的删减成数字 8、9 之后,(2, 5)出现了列隐性 唯一候选数 2 ,于是可用隐性唯一候选数法来填入下一个解了。
整理一下:
当某个数对仅出现在某个九宫格的某两个宫格候选数中时,就可以把这两个宫格的候选数删减成该数对。
同理,当某个数对仅出现在某列的某两个宫格候选数中时,就可以把这两个宫格的候选数删减成该数对。
当然,当某个数对仅出现在某行的某两个宫格候选数中时,就可以把这两个宫格的候选数删减成该数对。
利用“找出某个数对仅出现在某行、某列或某一个九宫格的某两个宫格候选数中的情形,进而将这两个 宫格的候选数删减成该数对”的方法就叫做隐性数对删减法(Hidden Pairs)。
当隐性数对删减法完成后,通常还可引发数对删减法;以<图 1>为例,当(2, 8)和(2, 9)这两个宫格的候选数 都安全的删减成数字 8、9 之后,还可利用数对删减法把 (2, 1)、(2, 2)、(2, 3) 这三个c格候选数中的数字 8 删减掉。
隐性数对删减法示例
隐性数对删减法一共有 3 种状况:第一种发生在行、第二种是发生在列、第三种则发生在九宫格。<图 1> 就是 发生在九宫格的例子了,其他的情况举例如下:
<图 2>
<图 2> 是隐性数对删减发生在行的例子:图中第 2 行的数对 4、6 只出现在 (3, 2)及(9, 2) 这两个宫格 的候选数中,所以可以将(3, 2)及(9, 2)的候选数安全的删减成数对 4、6;而经此一删,(3, 3) 宫格出现 了列隐性唯一候选数 1 啦!
<图 3>
<图 3> 是隐性数对删减发生在列的例子:图中第 7 列的数对 4、7 只出现在 (7, 1)及(7, 8) 这两个宫格 的候选数中,所以可以将(7, 1)及(7, 8)的候选数安全的删减成数对 4、7;而经此一删,(8, 1) 宫格出现 了行隐性唯一候选数 2 啦!
三链列删减法
概说
遇到了高级、困难级的数独谜题,使得唯一候选数法和 隐性唯一候选数法黔驴技穷的时候,就是各种删减法上场的时机了。在各种的删减法中,哪一个要先用 是随个人之喜好的,并无限制。本页介绍的例子当然可用其他删减法完成解题,且本删减法成立的条件 和其他方法相比稍嫌繁杂,但为了介绍,在进行解题时还是要以三链列删减法优先??!
<图 1>
请看<图 1>第 1、4、6 列的数字 5 ,都只出现在第 1、5、8 行的宫格候选数中;这时 三链列删减法的条件已成立了!这表示第 1 行、第 5 行及第 8 行的数字 5 将只能被填到第 1 、4、6 列了,因为:第 1 列的数字 5 只出现在(1, 1)及(1, 8),所以数字 5 只能填到这两个宫格;
先假设第 1 列的数字 5 将被填到(1, 1),第 1 行就不能再填数字 5 了,所以第 4 列 的数字 5 只好填到(4, 5),第 6 列的数字 5 只好填到(6, 8);
另外,假设第 1 列的数字 5 将被填到(1, 8),第 8 行就不能再填数字 5 了,所以第 6 列的数字 5 只好填到(6, 1)或(6, 5);
如果第 6 列的数字 5 填到(6, 1),第 4 列的数字 5 就要填到(4, 5);
如果第 6 列的数字 5 填到(6, 5),第 4 列的数字 5 就要填到(4, 1);
不论哪一种情况发生,第 1、5、8 行的数字 5 一定要填在第 1、4、6 列的交点,别的宫格已不能再使用 数字 5 来填入了,所以若其他宫格的候选数中还有数字 5,全部是多余无用的, 可以毫不考虑的把它们删减掉。于是(5, 1)、(5, 5)、(9, 5)和(1, 8)、(2, 8)这五个宫格候选数中的 5 都可被安全的删减掉;其中(9, 5)的候选数少了数字 5,将使得(9, 4)出现列隐性唯一候选数 5 ,于是 可用隐性唯一候选数法来填入下一个解了。
整理一下:
当某个数字在某三列仅出现在相同的三行时,就可以把这三行其他宫格候选数中的该数字删减掉。
同理,当某个数字在某三行仅出现在相同的三列时,就可以把这三列其他宫格候选数中的该数字删减掉。
利用“找出某个数字在某三列仅出现在相同三行的情形,进而将该数字自这三行其他宫格候选数中删减掉”; 或“找出某个数字在某三行仅出现在相同三列的情形,进而将该数字自这三列其他宫格候选数中删减掉”的方法 就叫做三链列删减法(Swordfish)。
本删减法其实是矩形顶点删减法的推广,如果你愿意的话,还可以继续推广:
四链列删减法:利用“找出某个数字在某四列仅出现在相同四行的情形,进而将该数字自这四行其他宫格 候选数中删减掉”;或“找出某个数字在某四行仅出现在相同四列的情形,进而将该数字自这四列其他 宫格候选数中删减掉”的方法
五链列删减法:利用“找出某个数字在某五列仅出现在相同五行的情形,进而将该数字自这五行其他宫格 候选数中删减掉”;或“找出某个数字在某五行仅出现在相同五列的情形,进而将该数字自这五列其他 宫格候选数中删减掉”的方法
六链列删减法:...... 不过如果真的这样做,实际应用时,能够用上的机率大概不多就是了。
遇到了高级、困难级的数独谜题,使得唯一候选数法和 隐性唯一候选数法黔驴技穷的时候,虽然你可以优先使用三链列删减法来寻找下一个解;但大部分的人在 使用删减法的优先顺序上,通常都会将三链列删减法排在稍后一点,为什么要如此安排,在实际使用一段时间之后, 相信你自能体会了,但这个方法又是不可或缺的,如果不会运用本删减法,有很多高级的数独谜题就将无解了。
三链列删减法示例
三链列删减法只有 2 种状况:第一种的删减发生在行、第二种的删减发生在列。<图 1> 就是 删减发生在行的例子了,第二种的情况举例如下:
<图 2>
<图 2> 是三链列删减发生在列的例子:图中第 3、5、8 行的数字 2 只出现在第 3、4、5 列, 所以可以将数字 2 自(4, 6)、(5, 6)的候选数中安全的删减掉,其中(5, 6) 的候选数由 2、5 删减成 5 时,出现唯一候选数啦!
区块删减法
概说
遇到了高级、困难级的数独谜题时,唯一候选数法和 隐性唯一候选数法仍有其黔驴技穷的时候;这时就是区块删减法上场的时机了,往后将要介绍的 数对删减法(Naked Pairs)、隐性数对删减法(Hidden Pairs)、三链数删减法(Naked Triples)、 隐性三链数删减法(Hidden Triples) 、矩形顶点删减法(X-Wing)、三链列删减法(Swordfish)都具有类似的特性:使用这 些技巧的目的仅在删减候选数的数目,删减之后,还是得使用唯一候选数法和隐性唯一候选数法来 找出下一个解并填入数字的。
当使用唯一候选数法或隐性唯一候选数法找不出下一个解时,到底该先使用哪一个删减法呢?随您高兴的用吧! 如果你比较擅长使用数对删减法,那就先用数对删减法吧!如果你认为区块删减法比较好用,那就先用数对删减法吧! ......;介绍时总有先后的次序,但并不表示先介绍的就较好用或必须先用哦!只要能达到:“安全删减掉候选数, 并找出下一个解”的目的,使用哪一种删减法都是可以的。
<图 1>
请看<图 1>,这时若使用唯一候选数法或隐性唯一候选数法是找不出下一个解来的!就先来试试区块删减法吧。 请观察第 9 行:数字 1 在本行各宫格的候选数中,是不是仅出现在(1,9)~(3,9)的这一个区块中?太好了,区块删减 的条件已有了;因为这表示第 9 行的数字 1 只能填在(1,9)~(3,9)的这一个区块中,而不论填在本区块 的哪一个宫格中,上右九宫格的其他宫格将因本九宫格已出现数字 1,而不得再填入 1,否则就违反数独填制的规则 啦!所以(1, 7)~(3, 7)及(1, 8)~(3, 8)这两个区块的宫格,如果其候选数中包含有数字 1,就可以毫不考虑的 把它删除掉,因为候选数的意义是可能填入该宫格的数字,而这个数字已不可能再用来填入该宫格中了。啊!太好啦! (1, 7)的候选数中包含有数字 1,所以可以把 (1, 7) 的候选数由 1、6 删减成 6,于是可用唯一候选数法来填入 下一个解了。
当区块删减法的条件成立时,可别高兴得太早,因为很有可能找不到可删减的数字,例如:在<图 1>的第 1 行中, 数字 2 在本行的各宫格候选数中,仅出现在(4, 1)~(6, 1)这一个区块中,而不论数字 2 将来会被填到本区块 的哪一个宫格中,将使得数字 2 不得再填入(4, 2)~(6, 2)及(4, 3)~(6, 3)这两个区块中;但请找找看! 这两个区块各宫格的候选数中全部没有数字 2,所以是白忙了一场,条件是成立了,但候选数并未因此而得到删减。
整理一下,并为了简化叙述起见,下面所述的“区块候选数”表示:该区块的各个宫格候选数的总和。例如(1, 3)~(3, 3) 的区块候选数就是(1, 3)的候选数 4、6、7 及(2, 3)的候选数 3、4、6 及(3, 3)的候选数 3、7 的总和: 3、4、6、7 啦!:
当某一个数字只出现在某行的某一个区块候选数中时,就可以把该数字自包含该区块的九宫格之其他 区块候选数中删减掉。
同理,当某一个数字只出现在某列的某一个区块候选数中时,就可以把该数字自包含该区块的九宫格之其他 区块候选数中删减掉。
同理,当某一个数字只出现在某个九宫格的某一个区块候选数中时,就可以把该数字自包含该区块的行或列之其他 区块候选数中删减掉。
利用“找出某一行、某一列或某一个九宫格各个区块候选数中只出现一次的数字来,并将该数字自包含该区块的另一个 行、列或九宫格的其他区块候选数中删减掉”的方法就叫做区块删减法 (Locked Candidates, Single Sector Candidates)。
区块删减法示例
区块删减法一共有 4 种状况:第一种是发生在行而去删减九宫格、第二种是发生在列而去删减九宫格、 第三种是发生在九宫格而去删减行、第四种是发生在九宫格而去删减列。
<图 1> 就是发生在行而去删减九宫格的例子了,其他的情况举例如下:
<图 2>
<图 2> 是发生在列而去删减九宫格的例子:因为第 3 列的数字 6 只出现在 (3, 1)~(3, 3) 这一个区块, 所以可以将上左九宫格的另两个区块 (1, 1)~(1, 3)、(2, 1)~(2, 3) 候选数中的数字 6 安全的删减掉; 于是(1, 1)的候选数 2、6 将被删减成 2,出现了唯一候选数啦!
<图 3>
<图 3> 是发生在九宫格而去删减列的例子:因为上右九宫格的数字 5 只出现在 (3, 7)~(3, 9) 这一个区块, 所以可以将第 3 列的另两个区块 (3, 1)~(3, 3)、(3, 4)~(3, 6) 候选数中的数字 5 安全的删减掉; 于是(3, 3)的候选数 5、9 将被删减成 9,出现了唯一候选数啦!
<图 4>
<图 4> 是发生在九宫格而去删减行的例子:因为中央九宫格的数字 1 只出现在 (4, 5)~(6, 5) 这一个区块, 所以可以将第 5 行的另两个区块 (1, 5)~(3, 5)、(7, 5)~(9, 5) 候选数中的数字 1 安全的删减掉; 于是(8, 5)的候选数 1、3、7、8 将被删减成 3、7、8;同理,中央九宫格的数字 7、8 都只出现在 (4, 5)~(6, 6) 这一个区块,所以可以将第 5 行的另两个区块 (1, 5)~(3, 5)、(7, 5)~(9, 5) 候选数中 的数字 7、8 都安全的删减掉;于是(8, 5)的候选数 3、7、8 将再度被删减成 3;出现了唯一候选数啦!
像<图 1>~<图 3>这样,只做一次区块删减就找到下一个解的情况固然是不错,但有时并没有那么顺心, 像<图 4>就需要删减三次才得到下一个解,不过那还算好的了,因为三次的删减都恰好发生在同一个区块中, 请看下面发生在不同区块的情形吧!
<图 5>
<图 5> 中的(4, 3)将可利用区块删减法得出下一个解,你能够不看下面的解答,自己找出来吗?试试!
也许你已经找出答案了,恭喜!也许你还找不出答案,那也没关系,人有失手,马有失蹄,总有脑袋被浆糊 糊住而一时失误的时候,请看答案吧:因为第 8 列的数字 2 只出现在 (8, 1)~(8, 3) 这一个区块, 所以可以将下左九宫格的另两个区块 (7, 1)~(7, 3)、(9, 1)~(9, 3) 候选数中的数字 2 安全的删减掉; 删减之后的结果如<图 6>。
<图 6>
接下来,因为第 3 行的数字 2 只出现在 (4, 3)~(6, 3) 这一个区块,所以可以将中左九宫格的另两个区块 (4, 1)~(6, 1)、(4, 2)~(6, 2) 候选数中的数字 2 安全的删减掉;删减之后的结果如<图 7>。
<图 7>
哈!哈!看出来了吗?(4, 3)已出现了列隐性唯一候选数2啦!
关键数删减法
概说
遇到了高级、困难级的数独谜题,使得唯一候选数法和隐性唯一候选数法黔驴技穷的时候,就是各种删减法上场的时机了。在各种的删减法中,哪一个要先用是随个人之喜好的,并无限制。本页介绍的例子虽然可能可以使用其他删减法完成解题,但在大部份的情况下是无可取代的,不过本删减法成立的条件和其他方法相比稍嫌繁杂,所以一般在使用时,均将其优先顺序 放在后面,只在不得已时才用之!
<图 1>
请看<图 1>,此时使用以往所提及的:数对删减法、区块删减法、隐性数对删减法、三链数删减法、 隐性三链数删减法、矩形顶点删减法、三链列删减法...等各式删减法都已找不到下一个解了,这才是 关键数删减法(Colors, Colouring)最好的上场时机。
某一个数字在某一行、某一列或者某一个九宫格的各宫格候选数中恰出现两次时,我们说在 这一行、这一列或者这一个九宫格中有了一个关键数。由于使用本删减法的时机是在数独填制的中后期, 所以拥有同一个关键数的行列或九宫格通常不止一处,而且环环相扣,使得候选数中包含该关键数的宫格 形成泾渭分明的两大阵营;<图 2> 和 <图 1>是完全相同的数独残局,但只显示候选数 4 的情形:
<图 2>
在 <图 2> 中,第一列的数字 4 仅出现在 (1, 1) 及 (1, 5),是本列的关键数,此时,若数字 4 应填入 (1, 1),则 (1, 5) 就不能再填入数字 4;反之,若数字 4 应填入 (1, 5),则 (1, 1) 就不能再填入数字 4 了; 虽然我们还不知道哪一个宫格应填入数字 4,但却可以利用关键数的这一个特性,将待填的部分宫格区分成两组, 只要其中的一组宫格应填入数字 4 ,另一组宫格就不可能再填入数字 4 。<图 2> 中底色为粉红及浅蓝的两组宫格, 就具有这样的性质。
接下来,我们就可以根据这两组宫格的分布情形,做一些确切的判定:
当在底色为浅蓝的宫格中填入数字 4 时,并无任何不妥!
若在底色为粉红的宫格中填入数字 4 时,则第 7 列或第 7 行都将出现两个数字 4,这是违反填制规则的。
所以所有底色为粉红的宫格都不可能填入数字 4,这些宫格候选数中的数字 4,全部都可以删减掉!回到 <图 1>,我们可发现,进行删减之后,下一个解的寻找根本就不成任何问题了。
大部分情况下,利用行列及九宫格的关键数将相关宫格区分为两组后,并不一定可找出上述的矛盾状况, 而确切的据以判定某一组宫格可进行候选数的删减,例如<图 3>就是一个例子:由第 9 列的关键数 6 所引发区分的两组宫格,不论将数字 6 填到粉红或浅蓝为底色的宫格中,都是不会产生矛盾的。
<图 3>
不过<图 3>却展示了关键数删减法的另一种删减状况;请看第 1 列中的 (1, 5) 及 (1, 8),它们有什么 特殊之处呢?尤怪居然要用浅绿的底色来标示!
哈!哈!相信你已看出来了,在这两个宫格的同一行上,都有两个不同底色的宫格存在,这代表:不论最后 数字 6 应填到哪一组底色的宫格中,因为本行的数字 6 已被填入了,所以这两个宫格都不可能再填入数字 6 了,因此这两个宫格的候选数 6 都可被安全的删减掉!
为了更清楚的说明这类的删减,假设有某个数独残局的数候选数 1 分布如<图 4> :
<图 4>
利用<图 4>第 1 列中的关键数 1,可将部分宫格区分为两组独立的宫格,分别以粉红及浅蓝为底色来标示; 只要其中的一组宫格被填入数字 1,另一组宫格就不可能再填入数字 1。虽然在本图中的任一组宫格中填入 数字 1 都不会产生矛盾,但是仍可以利用这些宫格的分布,对其他宫格进行删减。
先看 (3, 7)、(3, 8)、(3, 9),因为上右九宫格中己拥有粉红及浅蓝为底色的宫格各一个,表示不论 数字 1 应填到哪一组底色的宫格中,因为本九宫格中的数字 1 已被填入了,所以其他宫格都不能再 使用数字 1 了,因此这三个宫格的候选数 1 都可被安全的删减掉!
再看 (4, 9),因为同行的(2, 9)有一个粉红底色的宫格,同列的(4, 4)又有一个浅蓝底色的宫格,所以 不论数字 1 应填到哪一组底色的宫格中,因为同一个行、列中的数字 1 已被填入了,所以本宫格就不能 再使用数字 1 了;这个宫格的候选数 1 可安全的删减掉!
最后来看看 (4, 1)、(5, 1),因为同行中己拥有粉红及浅蓝为底色的宫格各一个,所以这两个宫格的 候选数 1 都可安全的删减掉!
利用“以关键数的关系找出矛盾的组合,或者找出确切可进行删减的宫格,进而将该数字自宫格候选数中删减掉” 的方法就叫做关键数删减法(Colors, Colouring)。由于在说明本法的分组状况时, 以颜色来区分是最清楚明了的,所以外国人就以 “colors 颜色”为名,也是十分传神的。
矩形顶点删减法
概说
遇到了高级、困难级的数独谜题,使得唯一候选数法和 隐性唯一候选数法黔驴技穷的时候,就是各种删减法上场的时机了。在各种的删减法中,哪一个要先用 是随个人之喜好的,并无限制。本页介绍的例子当然可用其他删减法完成解题,且本删减法成立的条件 和其他方法相比稍嫌繁杂,但为了介绍,在进行解题时还是要以矩形顶点删减法优先??!
<图 1>
请看<图 1>的第 1 列及第 9 列,数字 8 都只出现在第 5、8 行的宫格候选数中;这时 矩形顶点删减法的条件已成立了!这表示第 5 行及第 8 行的数字 8 将只能被填到第 1 列及 第 9 列了,因为:第 1 列的数字 8 只出现在(1, 5)及(1, 8),所以数字 8 只能填到这两个宫格; 同样的,第 9 列的数字 8 只出现在(9, 5)及(9, 8),所以数字 8 也只能填到这两个宫格; 先假设第 1 列的数字 8 将被填到(1, 5),第 5 行就不能再填数字 8 了,所以第 9 列的数字 8 只好 填到(9, 8);另外,假设第 1 列的数字 8 将被填到(1, 8),第 8 行就不能再填数字 8 了,所以第 9 列的数字 8 只好填到(9, 5);不论哪一种情况发生,第 5 行及第 8 行的数字 8 都已被填入,别的 宫格已不能再使用数字 8 来填入了,所以若其他宫格的候选数中还有数字 8,全部是多余无用的, 可以毫不考虑的把它们删减掉。于是(3, 5)、(6, 5)和(3, 8)、(7, 8)这四个宫格候选数中的 8 都可被安全的删减掉;其中(6, 5)的候选数少了数字 8,将使得(6, 6)出现列隐性唯一候选数 8 ,于是 可用隐性唯一候选数法来填入下一个解了。
整理一下:
当某个数字在某两列仅出现在相同的两行时,就可以把这两行其他宫格候选数中的该数字删减掉。
同理,当某个数字在某两行仅出现在相同的两列时,就可以把这两列其他宫格候选数中的该数字删减掉。
利用“找出某个数字在某两列仅出现在相同两行的情形,进而将该数字自这两行其他宫格候选数中删减掉”; 或“找出某个数字在某两行仅出现在相同两列的情形,进而将该数字自这两列其他宫格候选数中删减掉”的方法 就叫做矩形顶点删减法(X-Wing)。因为本删减法的条件成立时,关键的数字 8 所处的宫格在数独方阵上看来,刚好就在一个矩形的顶点。
遇到了高级、困难级的数独谜题,使得唯一候选数法和 隐性唯一候选数法黔驴技穷的时候,虽然你可以优先使用矩形顶点删减法来寻找下一个解;但大部分的人在 使用删减法的优先顺序上,通常都会将矩形顶点删减法排在稍后一点,为什么要如此安排,在实际使用一段时间之后, 相信你自能体会了,但这个方法又是不可或缺的,如果不会运用本删减法,有很多高级的数独谜题就将无解了。
矩形顶点删减法示例
矩形顶点删减法只有 2 种状况:第一种的删减发生在行、第二种的删减发生在列。<图 1> 就是 删减发生在行的例子了,第二种的情况举例如下:
<图 2>
<图 2> 是矩形顶点删减发生在列的例子:图中第 2 行、第 8 行的数字 3 只出现在第 1 列及第 2 列, 所以可以将数字 3 自(1, 3)、(1, 5)及(2, 1)、(2, 4)、(2, 5)的候选数中安全的删减掉,其中(2, 4) 的候选数由 2、3、4、6 删减成 2、4、6 时;(3, 4)将出现隐性唯一候选数 3 啦!
<图 3>
<图 3> 也是一个删减法综合运用的例子。在(1, 8)中将可找到下一个解,你能找出来吗?
因为上中九宫格的数字 1 只发生在(2, 4)~(2, 6) 这一个区块,所以可以利用区块删减法 把(2, 7)~(2, 9)候选数中的数字 1 安全的删减掉。
因为第 1 行及第 7 行的数字 1 只出现在第 4 列及第 9 列,所以可以利用矩形顶点删减法 把(4, 3)及(9, 6)、(9, 8)、(9, 9)候选数中的数字 1 安全的删减掉。
经过以上删减之后,(1, 8)出现行隐性唯一候选数 1 啦!
转发至微博
数独的解法二:算法实践——数独的基本解法
算法实践——数独的基本解法
数独(Sudoku)是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫内的数字均含1-9,不重复。 每一道合格的数独谜题都有且仅有唯一答案,推理方法也以此为基础,任何无解或多解的题目都是不合格的。
如下图所示,就是一个数独的题目
关于数独的详细介绍,参看“百度百科——数独”
数独的基本解法就是利用规则的摒弃法
一些定义
每一行称为数独的行,每一列称为数独的列,每一个小九宫格称为数独的宫。数独的基本规则就是每一行、每一列、每一宫中,1-9这9个数字都只出现一次。
用(行,列)表示上图的单元格,例如(1,1)表示第一行第一列的单元格,(2,4)表示第二行第四列的单元格
如上图,每个空白单元格中能填的数字都是有限制的。
例如:(1,1)就只能填7和8;而(6,4),只能填8;
那些只能填一个数字的空白单元格,我们称之为唯一数单元格,上图中(6,4)就是唯一数单元格
解题的顺序,就是从唯一数单元格开始,由于唯一数单元格只能填一个数,故先在这个单元格里填数。在这个单元格里填数,由于规则的定义,那么这个单元格所在的行、所在的列、所在的宫的其他单元格就不能再填这个数了。这些单元格能填的数的可能性就少了。有可能会产生新的唯一数单元格。
在相当的一些的数独题目中,从唯一数单元格开始填数,不停的在唯一数单元格填数就可以把数独解出来。
如果在解题的过程中,发现某些空白单元格没有数字能填这样的单元格称之为无解单元格,那就说明:要么这个数独没有解;要么之前的解题过程有问题,需要返回检查之前的解题过程查看。
但是还有不少的数独的题目,在解题的过程中,在还有空白单元格的情况下,却找不到唯一数单元格,也就是意味着每个空白单元格中能填的数字至少有2个。我们称之为无唯一数单元格的状况
这个时候怎么办?我们找到其中一个可能数最少的空白单元格(这个没有定论,可以是可能数最少的空白单元格;也可以是第一个空白单元格;也可以是可能数最多的空白单元格,选哪个空白单元格对后面的解题是否有影响,没有证明过,不好妄下定论。凭感觉选可能数最少的空白单元格是最好的选择),由于能填的数字不止一个,先把当前的状态保存起来,再在能选的数字中选择一个数字填写(从小到大选择),然后继续求解下去。如果能解出最后的结果,说明当前的选择是正确的;如果后面的求解过程有问题,说明当前的数字的选择有问题,那么再挑选另一个数填写,继续求解。如果,所有的选择都求不出最后的结果,还是说明:要么这个数独没有解;要么之前的解题过程有问题,需要返回检查之前的解题过程查看。如此反复,直到求出最终的答案。
会有种极端的情况(可能性不大)。那就是在当前的空白单元格的所有可能的数字都选择了一遍,都没有解。而之前又没有出现无唯一数单元格的状况。那就说明这个数独根本就没有解
下图是数独求解的流程图
下面谈谈该算法的具体实现
1、数独状态的表示
用计算机来求解数独。基本的一点就是如何表示数独的状态。
用整形一维数组来表示数独的状态
用Num(80)表示数独的状态(数组的下标从0开始),数独是一个二维表格,而数组是一维数组。那么就存在一维和二维之间的转换
一维数组的下标Index(小标从0开始)和二维下标X、Y(下标从0开始)之间的转换公式
一维到二维的转换
X=Int(Index/9)
Y=Index mod 9
二维到一维的转换
Index=X*9+Y
数组中的每个整数表示数独对应的单元格的状态
正数表示空白单元格能填的数的组合,用二进制表示。用位来表示该单元格是否能填相应的数字,1表示能填,0表示不能填。
如文章开始的数独的单元格(1,1)可能填7和8,则第7位和第8位上是1(位数是从后往前数),其余位都是0,用整数表示就是Num(0)=0110000002=192
每在单元格中填一个数字,则把相应的行、列、宫中其余的单元格把该数字去掉。
我们可以充分利用位运算来简化去数字的过程。如:要把单元格去掉7这个数字的可能。首先7对应的二进制位0010000002,取其反数得到1101111112,再和目标单元格的数值进行AND的位运算,来实现去除该单元格7这个数字的可能性(由于位运算的便捷,不需要考虑该单元格是否原本包含7这个数字的可能性)。
如:(1,1)=0110000002 AND 1101111112=0100000002,去除7这个可能性,只剩8这个可能性了,也就是成为唯一数单元格
再比如:(1,9)=0100000102 AND 1101111112=0100000102,原本单元格就没有7这个可能性,执行位运算后,还是原来的可能性,没有发生变化。
负数表示该单元格已经确定的数,例如:(1,2)=-6,表示该单元格已近填了数字6
0表示该单元格既没有填确定的数字,也没有可填数的可能性。也就是上文说的无解单元格
为了算法中计算的方便,事先把这些二进制数都缓存起来,用一个一维的数组表示
用数组V来表示各个位对应的数字
V(0)=0000000012=1
V(1)=0000000102=2
V(2)=0000001002=4
V(3)=0000010002=8
V(4)=0000100002=16
V(5)=0001000002=32
V(6)=0010000002=64
V(7)=0100000002=128
V(8)=1000000002=256
V(9)=1111111112=511
数字7对应的二进制数为V(6)=0010000002=64,7的反数为V(9)-V(6)=1101111112=447
每个单元格初始的值都是V(9)=1111111112=511
2、如何获得一个单元格的可填数的个数
由于是用二进制来表示单元格的状态,那么可填数的个数就是该数字中1的个数。我们之前有一个很方便的方法快速计算一个数中1的个数,参看算法的强大——快速计算一个正二进制整数中包含多少个1。
3、状态的缓存
依据之前的说法,在碰到无唯一数单元格的情况时,要把当前的状态缓存起来。考虑到实际情况,从算法的角度上来说,用栈(先进后出)这个数据结构来实现比较合适。可以自己写一个栈的实现。但是,目前很多的编程语言都实现了基本的数据结构,提供了基本的数据结构的类和方法供我们调用。
以Visual Studio为例,它有Stack这个类,实现了栈的基本操作。有两个栈的方法:Push(压栈)——把数据写到栈里面;Pop(出栈)——把数据从栈里提出来,并删除栈中的数据。
4、代码说明
基本的变量
Private _Num(80) As Integer Private _V(9) As Integer Private _S As System.Text.StringBuilder Private _HasString As Boolean _Num数组表示数独的状态;_V数组是辅助数组,缓存常用的二进制数
_S是一个文本对象,保存数独求解的过程;_HasString是个开关变量,表示是否记录求解过程;这两个变量是辅助变量,仅仅起到记录的作用。
类的初始化
Public Sub New(Optional ByVal HasString As Boolean = True) Dim I As Integer _V(0) = 1 For I = 1 To 8 _V(I) = _V(I - 1) * 2 Next _V(9) = 511 For I = 0 To 80 _Num(I) = _V(9) Next _S = New System.Text.StringBuilder _HasString = HasString End Sub
代码的前半段生成V这个数组,_V(9)=511。后半段,初始化数独数组。由于是空白数独数组,故每个单元格的值都是_V(9)
在给定的单元格里移除某个数字的可能性代码
Private Function RemoveNum(ByVal Row As Integer, ByVal Col As Integer, ByVal Num2 As Integer) As Integer Dim Index As Integer = Row * 9 + Col If _Num(Index) > 0 Then _Num(Index) = _Num(Index) And Num2 Return _Num(Index) End Function
3个参数,Row表示行,Col表示列(都是下标从0开始),Num2表示要去除的数的反码,以二进制表示。
例如:在(1,1)这个单元格去除7这个可能性,则调用RemoveNum(0,0,1101111112)
返回值是该单元格的状态值,如果返回0,表示该单元就成了无解单元格,要后面的代码做适当的处理
在给定的单元格填某个数的代码
Private Function SetNumPri(ByVal Row As Integer, ByVal Col As Integer, ByVal Num As Integer) As Boolean If (_V(Num) And _Num(Row * 9 + Col)) = 0 Then Return False _Num(Row * 9 + Col) = -(Num + 1) Num = _V(9) - _V(Num) Dim I As Integer, J As Integer For I = 0 To 8 If RemoveNum(I, Col, Num) = 0 Then Return False If RemoveNum(Row, I, Num) = 0 Then Return False Next Dim R1 As Integer = Int(Row / 3) * 3 Dim C1 As Integer = Int(Col / 3) * 3 For I = R1 To R1 + 2 For J = C1 To C1 + 2 If RemoveNum(I, J, Num) = 0 Then Return False Next Next Return True End Function
3个参数,Row表示行,Col表示列,Num表示要填充的数字(下标从0开始),这个方法是供类内部调用,从程序的角度来说,程序处理下标,从0开始比从1开始要来得简单。
例如:在(1,1)中填入数字7,则调用SetNumPri(0,0,6)
代码的第1行,先利用位运算判断当前单元格能否填制定的数字,不能填返回False
代码的第2行,设置当前单元格为指定数字,之前说了,用负数表示已填好的数字
代码的第3行,获得当前数字的反码,为后面去除该单元格所在的行、列、宫的其他单元格的该数字做准备
后面有两个循环,第一个循环去除行、列的其他单元格的该数字;第二个双循环去除宫的其他单元格的该数字。在调用RomoveNum方法时,若返回的是0,说明产生了无解单元格,那说明在这个单元格填该数字是不合理的,故返回False
当全部的代码都能顺利完成了,说明这个单元格填该数字是合理的,返回True
该方法的另一个重载形式
Private Function SetNumPri(ByVal Index As Integer, ByVal Num2 As Integer) As Boolean Dim Row As Integer = Int(Index / 9) Dim Col As Integer = Index Mod 9 Dim I As Integer For I = 0 To 8 If _V(I) = Num2 Then Exit For Next Return SetNumPri(Row, Col, I) End Function
这也是一个供内部调用的方法,两个参数,Index是一维数组的下标;Num2是数字的二进制的形式。整个方法就是参数的转换,然后调用之前的方法
下面是两个供外面调用的方法
Public Function SetNum(ByVal Row As Integer, ByVal Col As Integer, ByVal Num As Integer) As Boolean Return SetNumPri(Row - 1, Col - 1, Num - 1) End Function Public Function SetLine(ByVal Row As Integer, ByVal ParamArray Num() As Integer) As Boolean If Num.Length = 0 Then Return True Dim I As Integer For I = 0 To IIf(Num.Length - 1 > 8, 8, Num.Length - 1) If Num(I) > 0 AndAlso SetNumPri(Row - 1, I, Num(I) - 1) = False Then Return False Next Return True End Function
第一个方法是公开给外部调用的填数的方法。对外来说,从直观性上来说,下标是从1开始比较合适,但是内部的方法从0开始比较好。
如在(1,1)填7,调用SetNum(1,1,7),这个方法转而调用SetNumPri(0,0,6)
这个方法一般用在初始化数独时候调用
第二个方法也是公开给外部的方法,一次填写一行数的方法,如果是空白单元格,则用0替代
如本文开始的数独,填写第一行代码就是SetLine(1,0,6,0,5,9,3,0,0,0)
几个辅助方法
Private Sub RestoreNum(ByVal L As List(Of Integer)) Dim I As Integer For I = 0 To 80 _Num(I) = L.Item(I) Next AppendString("Restore Matrix") End Sub
恢复L中的数据到数独数组中,L是之前缓存的数据。AppendString这个方法是将数据记录到文本对象
Private Function Get1Count(ByVal Value As Integer) As Integer Dim C As Integer = 0 Do While Value > 0 Value = Value And (Value - 1) C += 1 Loop Return C End Function
获得一个数中1的个数,也就是获得一个空白单元格的可填数的数目
例如:(1,1)=0110000002,Get1Count(0110000002)=2,说明(1,1)这个单元格能填2个数
Private Function GetIndexOfNum(ByVal Num As Integer, ByVal Index As Integer) As Integer Dim I As Integer, K As Integer = 0 For I = 0 To 8 If (_V(I) And Num) <> 0 Then K += 1 If K = Index Then Return I + 1 End If Next Return -1 End Function
获得指定数Num(二进制形式)的第Index个的可填数
还是以上面的为例,(1,1)=0110000002,
GetIndexOfNum(0110000002,1)=7,表示第1个可填数是7
GetIndexOfNum(0110000002,2)=8,表示第2个可填数是8
GetIndexOfNum(0110000002,3)=-1,表示没有第3个可填数
辅助记录函数
这些函数对求解算法没啥太大的帮助,仅仅是将求解的过程记录到文本中,以供日后研究参考
Private Function ReturnNumString(ByVal Num As Integer) As String If Num < 0 Then Return "#" & (-Num) & " " Dim I As Integer, S As String = "" For I = 0 To 8 If (_V(I) And Num) <> 0 Then S &= (I + 1) Next Return S.PadRight(10) End Function
返回一个数字的文本格式,如果是空白单元格,返回该单元格的所有可填数;如果是已填单元格,返回#+数字的字符串。返回的字符串经过对齐处理。
Private Function ReturnMatrix() As String Dim I As Integer, J As Integer, S As String = "" For I = 0 To 8 For J = 0 To 8 S &= ReturnNumString(_Num(I * 9 + J)) Next S &= vbNewLine Next Return S End Function
返回整个数独的状态文本
Private Sub AppendString(ByVal Text As String, Optional ByVal AppendMatrix As Boolean = True) If _HasString = False Then Exit Sub _S.AppendLine(Text) _S.AppendLine() If AppendMatrix = True Then _S.AppendLine(ReturnMatrix) _S.AppendLine() End If End Sub
将文本添加到文本对象,并根据AppendMatrix参数来决定是否将整个数独的状态添加到文本对象
Private Function IndexToXY(ByVal Index As Integer) As String Return (Int(Index / 9) + 1) & "-" & (Index Mod 9 + 1) & " Num:" & -_Num(Index) End Function
返回指定Index的坐标和已填的数,用于在文本对象中
Public Function CalculationString() As String Return _S.ToString End Function
对外公开的方法,返回文本对象,也就是之前记录的求解过程,共日后研究参考
主求解函数——算法的核心
下面的3个函数是算法的核心
Private Function FindMinCell() As Integer Dim I As Integer, C As Integer Dim tP As Integer = -1, tMin As Integer = 20 I = 0 Do If _Num(I) > 0 Then C = Get1Count(_Num(I)) If C = 1 Then If SetNumPri(I, _Num(I)) = False Then Return -2 AppendString("SetNum " & IndexToXY(I)) If I = tP Then tP = -1 tMin = 20 End If I = -1 Else If C < tMin Then tP = I tMin = C End If End If End If I += 1 Loop Until I > 80 Return tP End Function
该函数是获得最少可能数的单元格(可填数大于2的空白单元格)
该函数返回值有3个可能性
返回值:-1,没有找到这样的单元格,函数从某个唯一数单元格开始填数,依次填下去,并且把所有的空白单元格都填满。这说明,求解结束。
返回值:-2,没有找到这样的单元格,函数从某个唯一数单元格开始填数,依次填下去,产生了无解单元格。说明之前的求解过程有错误或者说该数独无解
返回值:0-80,找到这样的单元格,并且当前的数独数组中不再存在唯一数单元格(函数直接会在唯一数单元格上填数)
Public Function Calculate() As Integer() Dim I As Integer Dim K As Integer Dim Q As New Stack(Of List(Of Integer)) Dim L As List(Of Integer) _S = New System.Text.StringBuilder AppendString("Init Matrix") K = FindMinCell() Do While K <> -1 If K = -2 Then If Q.Count = 0 Then AppendString("Error!!!!!", False) Return Nothing End If L = Q.Pop K = L(82) L.RemoveAt(82) I = L(81) + 1 L.RemoveAt(81) AppendString("Stack Pop " & Q.Count + 1, False) RestoreNum(L) K = FindNextK(Q, L, K, I) Else L = New List(Of Integer) L.AddRange(_Num) K = FindNextK(Q, L, K, 1) End If Loop AppendString("Calculating Complete!!!!") Dim V(80) As Integer For I = 0 To 80 V(I) = -_Num(I) Next Return V End Function
对外公开的主求解函数,返回最终结果的整形数组
首先解释一下栈对象Q,由于栈Q每次压栈的时候只能压一个对象,而当出现无唯一数单元格的情况的时候,需要将当前的数据缓存起来。需要缓存的内容有三个部分,分别是数独数组、找到的最少可能数的单元格的下标、最少可能数的单元格的选择填的第几个数。故用一个List(of Integer)对象将之前的三个内容缓存起来。L(0)—L(80)表示是数独数组,L(81)是最少可能数的单元格的下标,L(82)是最少可能数的单元格的选择填的第几个数。
该函数的主要是判断K的值,如上个函数所述,K的值主要有3种
K=-1,说明没有空白单元格,数独已经完美的求解完成,直接返回结果
K=-2,说明有无解单元格,那么判断栈Q中的数据,如果栈Q中没有数据,说明该数独无解;如果栈Q中有数据,那么把数据提出来,把数独的状态恢复到之前的情况。并从上次缓存的最少可能数单元格中,提取下一个可填数去继续进行尝试。
举例说明,缓存了0,1。说明上次尝试的是第1个单元格(下标从0开始)的第1个可填数。由于出现了无解单元格,说明第1个可填数是不正确的,那么继续尝试第2个可填数。调用的方法:FindNextK(Q, L, K, I),之前I已经加过1了。
K=0-80,得到最少可能数的单元格的下标。从该单元格的第1个可填数开始尝试。调用的方法:FindNextK(Q, L, K, 1)
尝试可能数的函数是FindNextK,返回值也是分为3种,-1、-2、0-80。意义和上面一样
Private Function FindNextK(ByVal Q As Stack(Of List(Of Integer)), ByVal L As List(Of Integer), ByVal K As Integer, ByVal Index As Integer) As Integer Dim J As Integer = GetIndexOfNum(_Num(K), Index) Do While J <> -1 If SetNumPri(K, _V(J - 1)) = True Then AppendString("Stack Push " & Q.Count + 1, False) AppendString("SetNum MayBe " & IndexToXY(K)) L.Add(Index) L.Add(K) Q.Push(L) K = FindMinCell() Exit Do End If RestoreNum(L) Index += 1 J = GetIndexOfNum(_Num(K), Index) Loop If J = -1 Then K = -2 Return K End Function
辅助函数,获得尝试可能数的结果
首先,通过GetIndexOfNum获得当前可填数。如果返回值-1的话,说明当前已经没有可填数,出现无解单元格,直接返回值为-2
然后尝试在当前单元格填数,调用SetNumPri(K, _V(J - 1)),返回True表示该数能填,那么把当前的状态缓存到栈Q中,并通过FindMinCell函数获得下一个可能的K值,并返回;返回False表示该数不能填,恢复数据到数独数组,继续尝试下一个数。
至此该算法类的代码都说明完整了
在该算法中仅仅用了最基本的解法——摒除法。遇见唯一数单元格,就直接填数,如果遇见无唯一数单元格,则缓存数据,并对该单元格的所有可填数做尝试,直到求解出该数独为止。
会有人疑问,利用栈Q缓存数据,会不会极大的占用系统资源,导致无法解题的情况。以目前的情况来看,我用该算法求解了“程序员们都是不被世人所理解的真正天才吗?-请大家看这个数独的解法”中的号称最难的数独,并把求解的结果保存到文件后打开分析了一下,发现栈Q的缓存不超过20步,以20步为例,每步83*4字节,则一共20*83*4=6640字节<7K字节。远小于系统的承受能力。因此,不必担心系统的承受能力
如果,谁有好的数独的算法,欢迎交流,不吝赐教。
让我们实战看看成果,用该算法求解本文开头的数独,代码如下:
Dim tS As New clsSudoku
tS.SetLine(1, 0, 6, 0, 5, 9, 3, 0, 0, 0) tS.SetLine(2, 9, 0, 1, 0, 0, 0, 5, 0, 0) tS.SetLine(3, 0, 3, 0, 4, 0, 0, 0, 9, 0) tS.SetLine(4, 1, 0, 8, 0, 2, 0, 0, 0, 4) tS.SetLine(5, 4, 0, 0, 3, 0, 9, 0, 0, 1) tS.SetLine(6, 2, 0, 0, 0, 1, 0, 6, 0, 9) tS.SetLine(7, 0, 8, 0, 0, 0, 6, 0, 2, 0) tS.SetLine(8, 0, 0, 4, 0, 0, 0, 8, 0, 7) tS.SetLine(9, 0, 0, 0, 7, 8, 5, 0, 1, 0)
tS.Calculate()
My.Computer.FileSystem.WriteAllText("1.txt", tS.CalculationString, False)
该数独还是比较简单的,一路唯一数单元格到底
结果如下:
Calculating Complete!!!!
#7 #6 #2 #5 #9 #3 #1 #4 #8 #9 #4 #1 #2 #7 #8 #5 #3 #6 #8 #3 #5 #4 #6 #1 #7 #9 #2 #1 #9 #8 #6 #2 #7 #3 #5 #4 #4 #7 #6 #3 #5 #9 #2 #8 #1 #2 #5 #3 #8 #1 #4 #6 #7 #9 #3 #8 #7 #1 #4 #6 #9 #2 #5 #5 #1 #4 #9 #3 #2 #8 #6 #7 #6 #2 #9 #7 #8 #5 #4 #1 #3
下面是该算法类的完整代码
Public Class clsSudoku Private _Num(80) As Integer Private _V(9) As Integer Private _S As System.Text.StringBuilder Private _HasString As Boolean Public Sub New(Optional ByVal HasString As Boolean = True) Dim I As Integer _V(0) = 1 For I = 1 To 8 _V(I) = _V(I - 1) * 2 Next _V(9) = 511 For I = 0 To 80 _Num(I) = _V(9) Next _S = New System.Text.StringBuilder _HasString = HasString End Sub Private Function Get1Count(ByVal Value As Integer) As Integer Dim C As Integer = 0 Do While Value > 0 Value = Value And (Value - 1) C += 1 Loop Return C End Function Private Function RemoveNum(ByVal Row As Integer, ByVal Col As Integer, ByVal Num2 As Integer) As Integer Dim Index As Integer = Row * 9 + Col If _Num(Index) > 0 Then _Num(Index) = _Num(Index) And Num2 Return _Num(Index) End Function Public Function SetNum(ByVal Row As Integer, ByVal Col As Integer, ByVal Num As Integer) As Boolean Return SetNumPri(Row - 1, Col - 1, Num - 1) End Function Public Function SetLine(ByVal Row As Integer, ByVal ParamArray Num() As Integer) As Boolean If Num.Length = 0 Then Return True Dim I As Integer For I = 0 To IIf(Num.Length - 1 > 8, 8, Num.Length - 1) If Num(I) > 0 AndAlso SetNumPri(Row - 1, I, Num(I) - 1) = False Then Return False Next Return True End Function Private Function SetNumPri(ByVal Row As Integer, ByVal Col As Integer, ByVal Num As Integer) As Boolean If (_V(Num) And _Num(Row * 9 + Col)) = 0 Then Return False _Num(Row * 9 + Col) = -(Num + 1) Num = _V(9) - _V(Num) Dim I As Integer, J As Integer For I = 0 To 8 If RemoveNum(I, Col, Num) = 0 Then Return False If RemoveNum(Row, I, Num) = 0 Then Return False Next Dim R1 As Integer = Int(Row / 3) * 3 Dim C1 As Integer = Int(Col / 3) * 3 For I = R1 To R1 + 2 For J = C1 To C1 + 2 If RemoveNum(I, J, Num) = 0 Then Return False Next Next Return True End Function Private Function SetNumPri(ByVal Index As Integer, ByVal Num2 As Integer) As Boolean Dim Row As Integer = Int(Index / 9) Dim Col As Integer = Index Mod 9 Dim I As Integer For I = 0 To 8 If _V(I) = Num2 Then Exit For Next Return SetNumPri(Row, Col, I) End Function Private Function FindMinCell() As Integer Dim I As Integer, C As Integer Dim tP As Integer = -1, tMin As Integer = 20 I = 0 Do If _Num(I) > 0 Then C = Get1Count(_Num(I)) If C = 1 Then If SetNumPri(I, _Num(I)) = False Then Return -2 AppendString("SetNum " & IndexToXY(I)) If I = tP Then tP = -1 tMin = 20 End If I = -1 Else If C < tMin Then tP = I tMin = C End If End If End If I += 1 Loop Until I > 80 Return tP End Function Public Function Calculate() As Integer() Dim I As Integer Dim K As Integer Dim Q As New Stack(Of List(Of Integer)) Dim L As List(Of Integer) _S = New System.Text.StringBuilder AppendString("Init Matrix") K = FindMinCell() Do While K <> -1 If K = -2 Then If Q.Count = 0 Then AppendString("Error!!!!!", False) Return Nothing End If L = Q.Pop K = L(82) L.RemoveAt(82) I = L(81) + 1 L.RemoveAt(81) AppendString("Stack Pop " & Q.Count + 1, False) RestoreNum(L) K = FindNextK(Q, L, K, I) Else L = New List(Of Integer) L.AddRange(_Num) K = FindNextK(Q, L, K, 1) End If Loop AppendString("Calculating Complete!!!!") Dim V(80) As Integer For I = 0 To 80 V(I) = -_Num(I) Next Return V End Function Private Sub RestoreNum(ByVal L As List(Of Integer)) Dim I As Integer For I = 0 To 80 _Num(I) = L.Item(I) Next AppendString("Restore Matrix") End Sub Private Function GetIndexOfNum(ByVal Num As Integer, ByVal Index As Integer) As Integer Dim I As Integer, K As Integer = 0 For I = 0 To 8 If (_V(I) And Num) <> 0 Then K += 1 If K = Index Then Return I + 1 End If Next Return -1 End Function Private Function FindNextK(ByVal Q As Stack(Of List(Of Integer)), ByVal L As List(Of Integer), ByVal K As Integer, ByVal Index As Integer) As Integer Dim J As Integer = GetIndexOfNum(_Num(K), Index) Do While J <> -1 If SetNumPri(K, _V(J - 1)) = True Then AppendString("Stack Push " & Q.Count + 1, False) AppendString("SetNum MayBe " & IndexToXY(K)) L.Add(Index) L.Add(K) Q.Push(L) K = FindMinCell() Exit Do End If RestoreNum(L) Index += 1 J = GetIndexOfNum(_Num(K), Index) Loop If J = -1 Then K = -2 Return K End Function Private Function ReturnNumString(ByVal Num As Integer) As String If Num < 0 Then Return "#" & (-Num) & " " Dim I As Integer, S As String = "" For I = 0 To 8 If (_V(I) And Num) <> 0 Then S &= (I + 1) Next Return S.PadRight(10) End Function Private Function ReturnMatrix() As String Dim I As Integer, J As Integer, S As String = "" For I = 0 To 8 For J = 0 To 8 S &= ReturnNumString(_Num(I * 9 + J)) Next S &= vbNewLine Next Return S End Function Private Sub AppendString(ByVal Text As String, Optional ByVal AppendMatrix As Boolean = True) If _HasString = False Then Exit Sub _S.AppendLine(Text) _S.AppendLine() If AppendMatrix = True Then _S.AppendLine(ReturnMatrix) _S.AppendLine() End If End Sub Private Function IndexToXY(ByVal Index As Integer) As String Return (Int(Index / 9) + 1) & "-" & (Index Mod 9 + 1) & " Num:" & -_Num(Index) End Function Public Function CalculationString() As String Return _S.ToString End Function End Class
数独的解法三:数独解题方法大全
数独解题方法大全
作者:扬子活力论坛 泥瓦匠 整理:隱讀書生
数独这个数字解谜游戏,完全不必要用到算术!会用到的只是推理与逻辑。解题方法分两大类:直观法和候选数法。
直观法就是不需要任何辅助工具,从接到数独谜题的那一刻起就可以立即开始解题。绝不猜测。数独直观法解题技巧主要有:唯一解法、基础摒除法、区块摒除法、唯余解法、矩形摒除法、单元摒除法,余数测试法。
候选数法就是解数独题目需先建立候选数列表,根据各种条件,逐步安全的清除每个宫格候选数的不可能取值的候选数,从而达到解题的目的。
使用候选数法一般能解比较复杂的数独题目,但是候选数法的使用没用直观法那么直接,需要先建立一个候选数列表的准备过程。所以实际使用时可以先利用直观法进行解题,到无法用直观法解题时再使用候选数方法解题。
候选数法解题的过程就是逐渐排除不合适的候选数的过程,所以在进行候选数删除的时候一定要小心,确定安全的删除不合适的候选数,否则,很多时候只有重新做题了。有了计算机软件的帮助,使得候选数表的维护变得轻松起来。
数独候选数法解题技巧主要有:唯一候选数法、隐性唯一候选数法、区块删减法、数对删减法、隐性数对删减法、三链数删减法、隐性三链数删减法、矩形顶点删减法、三链列删减法、关键数删减法、关连数删减法。
一、直观法:
1、唯一解法:
当某行已填数字的宫格达到8个,那么该行剩余宫格能填的数字就只剩下那个还没出现过的数字了。成为行唯一解。
当某列已填数字的宫格达到8个,那么该列剩余宫格能填的数字就只剩下那个还没出现过的数字了。成为列唯一解。
当某九宫格已填数字的宫格达到8个,那么该九宫格剩余宫格能填的数字就只剩下那个还没出现过的数字了。成为九宫格唯一解。
下面是例题:
A行已经添入8个数字,A行只有数字3没有出现过,所以A9=3,这是行唯一解。
第1列已经添入8个数字,第1列只有数字5没有出现过,所以E1=5,这是列唯一解。
在A8所在九宫格区域已经添入8个数字,只有数字9没有出现过,所以A8=9,这是九宫格唯一解。
2、基础摒除法
基础摒除法就是利用1 ~ 9 的数字在每一行、每一列、每一个九宫格都只能出现一次的规则进行解题的方法。基础摒除法可以分为行摒除、列摒除、九宫格摒除。
实际寻找解的过程为:
寻找九宫格摒除解:找到了某数在某一个九宫格可填入的位置只余一个的情形;意即找到了 该数在该九宫格中的填入位置。
寻找列摒除解:找到了某数在某列可填入的位置只余一个的情形;意即找到了该数在该列中的填入位置。
寻找行摒除解:找到了某数在某行可填入的位置只余一个的情形;意即找到了该数在该行中的填入位置。
利用基础摒除法解题的过程就是依次从数字1 ~ 9 在行、列、九宫格寻找能放入该数唯一的一个位置。需要综合用到行摒除、列摒除、九宫格摒除的方法。
看能用基础摒除法确定B2、C8、E7、F6、I5的数字吗?
题目如下:
A4=9,则A行其它格排除9;G1=9,第1列排除数字9;D3=9,第3列排除数字9。
见下图
由基础摒除法,第A1所在的九宫格内9只有一个唯一的位置,即确定B2=9。
见下图
A4=9,则4列其它格排除9;G1=9,第G行排除数字9;H9=9,第H行排除数字9。
见下图
由基础摒除法,第G4所在的九宫格内9只有一个唯一的位置,即确定I5=9。
见下图
A4=9,则4列其它格排除9;D3=9,第D行排除数字9;I5=9,第5列排除数字9。
见下图
由基础摒除法,第D4所在的九宫格内9只有一个唯一的位置,即确定F6=9。
见下图
A4=9,则A行其它格排除9;B2=9,第B行排除数字9;H9=9,第9列排除数字9。
见下图
由基础摒除法,第A7所在的九宫格内9只有一个唯一的位置,即确定C8=9。
见下图
C8=9,则8列其它格排除9;D3=9,第D行排除数字9;F6=9,第F行排除数字9;H9=9,第9列排除数字9。
见下图
由基础摒除法,第D7所在的九宫格内9只有一个唯一的位置,即确定E7=9。
3、区块摒除法
区块摒除法是基础摒除法的提升方法,是直观法中使用频率最高的方法之一。
所谓区块,就是将行分成3个三个相连的小方块构成,列也是分成3个三个相连的小方块构成。九宫格同样被看成由3个三个相连的小方块构成,如下面示意图:
区块摒除法的核心思想如下面解释(以行为例),对于在列也是相同的道理
假如(G1~G3)黄色区域区块其中之一是数字9。
则,(H4~H6)蓝色区域可能含有数字9。
否则(I4~I6)绿色区域含有数字9。
假定我们已确定(G1~G3)黄色区域区块其中之一是数字9。
(H4~H6)蓝色区域含有数字9。
则:在(I7~I9)绿色区域一定含有数字9。如果再通过其它方法确定(I7~I9)绿色区域中某两个宫格不能为数字9,则就能确定数字9在(I7~I9)区块的具体位置。
下面举一些例子
能使用区块摒除法确定F6的数字吗?
D2=2,则E1~E3蓝色区块,或F1~F2绿色区块必包含数字2。
又有B1=2,利用列摒除法,E1、F1不能为数字1,有F2,F3已填有数字,所以,E2~E3蓝色区块必有数字2
由上面得出黄色区块,蓝色区块包含数字2,这是典型的区块摒除法,得到绿色区块必包含数字2
又G4=2,F5已添入数字,所以F6=2
4、唯余解法
唯余解法就是某宫格可以添入的数已经排除了8个,那么这个宫格的数字就只能添入那个没有出现的数字。
唯余解法道理非常简单,但在实际使用是比较困难,要注意识别。A5=?
其实这就是唯余解法的原理,很简单吧。但是实际使用时就不会容易发现了。
能使用唯余解法确定B7的值吗?
呵呵,等于8。
能确定E9、A9、B9、C9的值吗?
由区块摒除法可以得出E9=9。在区块摒除法没有举这个例子,这里补充。
由唯余解法,C9=2。
同样,可得出B9=4,A9=8。
5、矩形摒除法
矩形摒除法是比较高级的排除方法,虽然矩形摒除法的原理非常简单,在实际使用时比较难于观察出来。
矩形摒除法的原理如下:
如上图,如果在第3列,我们确定数字9只能在B3或H3出现。在第7列,数字9只能在B7或H7出现。则B3,H3,B7,H7构成矩形,符合矩形摒除法的条件。
由上,可以得出数字"9"仅可能出现在 (B3,H7)上,或者出现在 (B7,H3)上
无论出现上面的那一种情况,我们都可以推断出B行,H行的红色区域都不能再为数字 9了。
下面举一个使用矩形摒除法的例子
由C7=3,我们可以判断在第3列,数字3只能出现在A3和H3。
又第6列,数字3只能出现在A6和H6
由A3,H3,A6,H6形成矩形符合矩形摒除法的条件
由矩形摒除法得到H8不可能是3,又根据C7=3,所以G9=3
6、单元摒除法
单元摒除法是比较基本的排除方法,下面举例解释
能确定A8的数字吗?
由D5=7,得出D8不等于7
H9=7,得出G8、H8、I8均不等于7
显然A8=7
7、余数测试法
所谓余数测试法就是在某行或列,九宫格所填数字比较多,剩余2个或3个时,在剩余宫格添入值进行测试的解题方法。
我们看B行,B3可能添入的数为5或者6,我们从5开始测试。
我们在B3添入5进行测试,得到左图,没有得出出错的推断,所以B3=5可能是正确的判断,如果能判断出B3<>6,则才能肯定B3=5。
所以下面我们还需要用B3=6进行测试
在B3添入6,推出B8=5。
观察C行,C7,C8,C9必含有数字5。
证明B3=6是错误的。从而得出B3=5
二、候选数法:
1、唯一候选数法
候选数法解题的过程就是逐渐排除不合适的候选数的过程,当某个宫格的候选数排除到只有一个数的时候,那么这个数就是该宫格的唯一的一个候选数,这个候选数就是解了。
我们可以排除D3为12356789的可能,经过候选数的安全删除后,D3的候选数变为"4"这个唯一候选数了。
2、隐性唯一候选数法
当某个数字在某一列各宫格的候选数中只出现一次时,那么这个数字就是这一列的唯一候选数了。这个宫格的值就可以确定为该数字。
这时因为,按照数独游戏的规则要求每一列都应该包含数字1~9,而其它宫格的候选数都不含有该数,则该数不可能出现在其它的宫格,那么就只能出现在这个宫格了。
对于唯一候选数出现行,九宫格的情况,处理方法完全相同
这是制作好的一张候选数表,注意观察B5,B9,D1
可以看出在第1列,数字9只在D1出现。
在第5列,数字3只在B2出现。
在B9所处的九宫格里,数字9只有在B9出现。
所以“9”是第1列的隐形唯一候选数。
“3”是第5列的隐形唯一候选数。
“9”是A7九宫格的隐形唯一候选数。
所以确定D1=3,B5=3,B9=9
3、三链数删减法
找出某一列、某一行或某一个九宫格中的某三个宫格候选数中,相异的数字不超过3个的情形,进而将这3个数字自其它宫格的候选数中删减掉的方法就叫做三链数删减法。
三链数删减法的原理如下面图示
在H行,H2,H5,H7的候选数(12),(23),(13),构成三链数,那么123这三个数在H行将只能出现在H2,H5,H7,那么本行其它宫格就可以删除这3个候选数了。这是三链数发生在行的情况。
在G7所在九宫格,G7,H8,I9的候选数(12),(23),(13),构成三链数,那么123这三个数在这个九宫格将只能出现在G7,H8,I9,那么本九宫格其它宫格就可以删除这3个候选数了。这是三链数发生在九宫格的情况。
三链数是数对的扩展,我们在对上面的三链数进行扩展,得到右边的特殊的三链数,只要保证在3个宫格内,其包含的候选数也为3个,就都符合我们的要求,比如(123,123,123),(12,12,123)都符合要求。
我们进一步再扩充,发现只要在N个宫格内,其包含的候选数也恰为N个,那么处理和三链数是相同的道理,这样就形成了四链数,比如(12,23,34,14),(123,123,14,1234)等。
甚至可以扩充到五链数,七链数(虽然在实际解题中作用不大了)。
平时我们用到最多的就是三链数,四链数了。
在A4所在九宫格,我们看到B4~B6,形成三链数,则本九宫格其它宫格就可以去除候选数"2","7","9",这样就得到C6=4。
同上面完全相同的一副图,在A行,A7~A9形成由179构成的三链数,排除本行其它宫格的候选数179后得到A3=3。
4、隐性三链数删减法
隐性三链数是从隐性数对发展而来的。
在某行,存在三个数字出现在相同的宫格内,在本行的其它宫格均不包含这三个数字,我们称这个数对是隐形三链数。那么这三个宫格的候选数中的其它数字都可以排除。
当隐形三链数出现在列,九宫格,处理方法是完全相同的。
我们进一步扩充,在某行(列,九宫格),存在N个数字出现在相同的宫格内,在本行的其它宫格均不包含这N个数字,我们称这个数对是隐形N链数。那么这N个宫格的候选数中的其它数字都可以排除
在中间九宫格,候选数“2”,“5”,“9”仅出现在E4,E6,F4,形成隐形三链数,所以在E4,E6,F4,可以排除其它候选数,得到F4=9。
5、矩形顶点删减法
矩形顶点删减法和直观法讲到的矩形摒除法分析方法是一样的。矩形顶点删减法在识别时比较不容易找到,所以最好先使用其它的方法。
如上图,如果在第3列,候选数“9”只能在B3或H3出现。在第7列,候选数“9”只能在B7或H7出现。
则B3,H3,B7,H7构成矩形,符合矩形顶点删减法的条件。
由上,可以得出数字“9”仅可能出现在(B3,H7)上,或者出现在(B7,H3)上
无论出现上面的那一种情况,我们都可以推断出B行,H行的红色区域都不能再为数字9了。可以将红色的宫格的候选数中去除数字“9”。
举例说明如下:
在第3列,数字“3”仅在A3、H3出现和第6列,数字“3”仅在A6、H6出现,A3、H3,A6、H6构成矩形,符合矩形顶点删减法要求,
则红色宫格应排除候选数“3”
6、三链列删减法
三链列删减法是矩形顶点删减法的扩展,如果不清除矩形顶点删减法,可以参考矩形顶点删减法,以便于更容易理解本节内容。
利用“找出某个数字在某三列仅出现在相同三行的情形,进而将该数字自这三行其他宫格候选数中删减掉”;或“找出某个数字在某三行仅出现在相同三列的情形,进而将该数字自这三列其他宫格候选数中删减掉”的方法 就叫做三链列删减法。
如果数字“1”可能出现在B行、E行、G行的黄色宫格,则符合“某个数字在某三列仅出现在相同三行的情形”,符合三链列删减法的要求。
则红色宫格均不包含候选数“1”。
这是前图的一个变形。其中一行的“1”只能放在这一行的两个位置。 处理和上图一样,红色宫格均可以排除候选数“1”。
举例说明:
数字"6"在第2列,第6列,第8列。均出现在A,B,I行。其中在第6列仅出现B,I行,仍然符合三链列删减法的要求。
则红色宫格均可以排除候选数"6"
7、关键数删减法
在进入到解题后期,利用前面讲到的唯一候选数法、隐性唯一候选数法、 区块删减法、数对删减法、隐性数对删减法、 三链数删减法、隐性三链数删减法、矩形顶点删减法、三链列删减法都无法有进展的时候,可以考虑使用关键数删减法。关键数删减法就是在后期找到一个数,这个数在行(或列,九宫格)仅出现两次的数字。我们假定这个数在其中一个宫格类,继续求解,如果发生错误,则确定我们的假设错误。如果继续求解仍然出现困难,不妨假设这个数在另外一个宫格,看能不能得到错误。这就是关键数删减法。
关键数删减法的本质是让我们一个个去测试,逐渐排除不可能的候选数,从而求解的过程。
这种解法就暂时不举例子了







