wiki链接:http://en.wikipedia.org/wiki/Knuth's_Algorithm_X
Knuth的X算法能够解决精确匹配问题,该算法是一个递归的,非确定性的,深度优先的带回溯的算法。该算法利用了0和1构成的矩阵来表示精确匹配。其目标是选择一些行使得由这些行构成的矩阵中每一列有且仅有1个1。
算法概括如下:
如果矩阵A为空,则问题成功解决
否则,选择一列c(确定的)
选择一行r,满足A[r][c]==1 (不确定的)
将r添加到所求解中
找到满足A[r][j]==1的每一列j
找到满足A[i][j]==1的每一行i
删除行i
删除列j
对于精简过的矩阵A重复上述步骤
不确定的选择r表明,算法本质上将自己拷贝成多个独立的子算法,每个子算法继承了当前的矩阵A,但删除了不同行r。如果列c不包含1,则该子算法失败结束。
显然,这些子算法形成了一棵搜索树,根节点为起始问题,树的第k层包含了所有已经选择了k个行的子算法。回溯其实就是前序深度优先便利这棵树。
为了减少迭代的次数,Knuth建议每次上述算法第二行选取包含1最少的那一列。
给一个示例:
全集U为{1,2,3,4,5,6,7},集合集S={A,B,C,D,E,F},其中
A={1,4,7}
B={1,4}
C={4,5,7}
D={3,5,6}
E={2,3,6,7}
F={2,7}
问题表示为矩阵如下:
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|
A | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
B | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
C | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
D | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
E | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
F | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
第0层:
第1步: 矩阵非空,算法继续
第2步:有两列包含最少的1,选择其中第一个列,列1(确定的)
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|
A | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
B | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
C | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
D | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
E | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
F | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
第3步:行A和行B都在列1中有1,所以都被选择(非确定的)
算法分支,开始第1层
第1层:选择行A
第4步:将A添加到解集中
第5步:将和行A中为1的列重复的行及列删除。行A在列1,4,7为1。列1为1的有A和B;列4为1的有A,B,C;列7为1的有A,C,E,F。所以删除行A,B,C,E,F和列1。
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|
A | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
B | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
C | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
D | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
E | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
F | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
剩余行D,和列2,3,5,6:
2 | 3 | 5 | 6 | |
---|---|---|---|---|
D | 0 | 1 | 1 | 1 |
第1步:矩阵非空,子算法继续
第2步:包含1最少的列为列2,但其包含0个1,所以子算法失败结束,算法开始第1层的另一个子算法,删除解集中的A
第1层:选择行B
第4步:将B加入解集
第5步:将和行B中为1的列重复的行及列删除。删除行A,B,C和列1,4。
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|
A | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
B | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
C | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
D | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
E | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
F | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
剩余行D,E,F,和列2,3,5,6,7:
2 | 3 | 5 | 6 | 7 | |
---|---|---|---|---|---|
D | 0 | 1 | 1 | 1 | 0 |
E | 1 | 1 | 0 | 1 | 1 |
F | 1 | 0 | 0 | 0 | 1 |
第1步:矩阵非空,子算法继续
第2步:包含1最少的列为列5
2 | 3 | 5 | 6 | 7 | |
---|---|---|---|---|---|
D | 0 | 1 | 1 | 1 | 0 |
E | 1 | 1 | 0 | 1 | 1 |
F | 1 | 0 | 0 | 0 | 1 |
第3步:选择行D
算法开始第2层的第一个分支
第2层:选择行D
第4步:将行D添加到解集中
第5步:删除和行D重复的列及行:行D,E和列3,5,6
2 | 3 | 5 | 6 | 7 | |
---|---|---|---|---|---|
D | 0 | 1 | 1 | 1 | 0 |
E | 1 | 1 | 0 | 1 | 1 |
F | 1 | 0 | 0 | 0 | 1 |
剩余行F和列2,5:
2 | 7 | |
---|---|---|
F | 1 | 1 |
第1步:矩阵非空,子算法继续
第2步:包含1最少的为列2,7,选择位于前面的列2
第3步:选择行F,算法进入第3层
第3层:选择行F
第4步:将F加入解集
第5步:删除和行F重复的列及行:行F和列2,7
2 | 7 | |
---|---|---|
F | 1 | 1 |
第1步:矩阵为空,子算法成功结束,找到解!
第3层没有其余可选择的子算法,移到第2层的下一个子算法
第2层没有其余可选择的子算法,移到第1层的下一个子算法。。。。。。
第1层和第0层都没有其余子算法,算法结束。
得到解集为{B,D,F}
1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|
B | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
D | 0 | 0 | 1 | 0 | 1 | 1 | 0 |
F | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
X算法可用Dancing Link(也成为DLX)来实现。这个中文名真不知道怎么说。。。。。。