在“跳跃的舞者,舞蹈链(Dancing Links)算法——求解精确覆盖问题”一文中介绍了舞蹈链(Dancing Links)算法求解精确覆盖问题。
本文介绍该算法的实际运用,利用舞蹈链(Dancing Links)算法求解数独。
在前文中可知,舞蹈链(Dancing Links)算法在求解精确覆盖问题时效率惊人。
那利用舞蹈链(Dancing Links)算法求解数独问题,实际上就是下面一个流程:
1、把数独问题转换为精确覆盖问题
2、设计出数据矩阵
3、用舞蹈链(Dancing Links)算法求解该精确覆盖问题
4、把该精确覆盖问题的解转换为数独的解
首先看看数独问题(9*9的方格)的规则:
1、每个格子只能填一个数字
2、每行每个数字只能填一遍
3、每列每个数字只能填一遍
4、每宫每个数字只能填一遍(宫的概念,参看“算法实践——数独的基本解法”)
那现在就是利用这个规则把数独问题转换为精确覆盖问题,可是,直观上面的规则,发现比较难以转换为精确覆盖问题。因此,把上面的表述换个说法:
1、每个格子只能填一个数字
2、每行1-9的这9个数字都得填一遍(也就意味着每个数字只能填一遍)
3、每列1-9的这9个数字都得填一遍
4、每宫1-9的这9个数字都得填一遍
这样理解的话,数独问题转换为精确覆盖问题就相对简单多了。关键就是如何构造精确覆盖问题中的矩阵。
我们把矩阵的每个列都定义成一个约束条件:
第1列定义成:(1,1)填了一个数字
第2列定义成:(1,2)填了一个数字
……
第9列定义成:(1,9)填了一个数字
第10列定义成:(2,1)填了一个数字
……
第18列定义成:(2,9)填了一个数字
……
第81列定义成:(9,9)填了一个数字
至此,用第1-81列完成了约束条件1:每个格子只能填一个数字
第N列(1≤N≤81)定义成:(X,Y)填了一个数字。
N、X、Y之间的关系是:X=INT((N-1)/9)+1;Y=((N-1) Mod 9)+1;N=(X-1)×9+Y
第82列定义成:在第1行填了数字1
第83列定义成:在第1行填了数字2
……
第90列定义成:在第1行填了数字9
第91列定义成:在第2行填了数字1
……
第99列定义成:在第2行填了数字9
……
第162列定义成:在第9行填了数字9
至此,用第82-162列(共81列)完成了约束条件2:每行1-9的这9个数字都得填一遍
第N列(82≤N≤162)定义成:在第X行填了数字Y。
N、X、Y之间的关系是:X=INT((N-81-1)/9)+1;Y=((N-81-1) Mod 9)+1;N=(X-1)×9+Y+81
第163列定义成:在第1列填了数字1
第164列定义成:在第1列填了数字2
……
第171列定义成:在第1列填了数字9
第172列定义成:在第2列填了数字1
……
第180列定义成:在第2列填了数字9
……
第243列定义成:在第9列填了数字9
至此,用第163-243列(共81列)完成了约束条件3:每列1-9的这9个数字都得填一遍
第N列(163≤N≤243)定义成:在第X列填了数字Y。
N、X、Y之间的关系是:X=INT((N-162-1)/9)+1;Y=((N-162-1) Mod 9)+1;N=(X-1)×9+Y+162
第244列定义成:在第1宫填了数字1
第245列定义成:在第1宫填了数字2
……
第252列定义成:在第1宫填了数字9
第253列定义成:在第2宫填了数字1
……
第261列定义成:在第2宫填了数字9
……
第324列定义成:在第9宫填了数字9
至此,用第244-324列(共81列)完成了约束条件4:每宫1-9的这9个数字都得填一遍
第N列(244≤N≤324)定义成:在第X宫填了数字Y。
N、X、Y之间的关系是:X=INT((N-243-1)/9)+1;Y=((N-243-1) Mod 9)+1;N=(X-1)×9+Y+243
至此,用了324列完成了数独的四个约束条件,矩阵的列定义完成。
那接下来,就是把数独转换为矩阵。数独问题中,每个格子分两种情况。有数字的格子、没数字的格子。
有数字的格子:
以例子来说明,在(4,2)中填的是7
把(4,2)中填的是7,解释成4个约束条件
1、在(4,2)中填了一个数字。
2、在第4行填了数字7
3、在第2列填了数字7
4、在第4宫填了数字7(坐标(X,Y)到宫N的公式为:N=INT((X-1)/3)×3+INT((Y-1)/3)+1)
那么这4个条件,分别转换成矩阵对应的列为
1、在(4,2)中填了一个数字。对应的列N=(4-1)×9+2=29
2、在第4行填了数字7。对应的列N=(4-1)×9+7+81=115
3、在第2列填了数字7。对应的列N=(2-1)×9+7+162=178
4、在第4宫填了数字7。对应的列N=(4-1)×9+7+243=277
于是,(4,2)中填的是7,转成矩阵的一行就是,第29、115、178、277列是1,其余列是0。把这1行插入到矩阵中去。
没数字的格子:
还是举例说明,在(5,8)中没有数字
把(5,8)中没有数字转换成
(5,8)中填的是1,转成矩阵的一行就是,第44、118、226、289列是1,其余列是0。
(5,8)中填的是2,转成矩阵的一行就是,第44、119、227、290列是1,其余列是0。
(5,8)中填的是3,转成矩阵的一行就是,第44、120、228、291列是1,其余列是0。
(5,8)中填的是4,转成矩阵的一行就是,第44、121、229、292列是1,其余列是0。
(5,8)中填的是5,转成矩阵的一行就是,第44、122、230、293列是1,其余列是0。
(5,8)中填的是6,转成矩阵的一行就是,第44、123、231、294列是1,其余列是0。
(5,8)中填的是7,转成矩阵的一行就是,第44、124、232、295列是1,其余列是0。
(5,8)中填的是8,转成矩阵的一行就是,第44、125、233、296列是1,其余列是0。
(5,8)中填的是9,转成矩阵的一行就是,第44、126、234、297列是1,其余列是0。
把这9行插入到矩阵中。由于这9行的第44列都是1(不会有其他行的44列会是1),也就是说这9行中必只有1行(有且只有1行)选中(精确覆盖问题的定义,每列只能有1个1),是最后解的一部分。这就保证了最后解在(5,8)中只有1个数字。
这样,从数独的格子依次转换成行(1行或者9行)插入到矩阵中。完成了数独问题到精确覆盖问题的转换。