📜  精确覆盖问题和算法X |套装1

📅  最后修改于: 2021-05-05 01:13:21             🧑  作者: Mango

如果您曾经尝试创建一个用于解决Sudoku的程序,则可能遇到过Exact Cover问题。在本文中,我们将讨论什么是精确覆盖问题以及Donald Knuth提出的算法“ Algorithm X”来解决此问题。

给定一组X的子集的集合S,一个确切的盖是所述子集S * S的,使得X的每个元素被包含为S中的恰好一个子集*。它应满足以下两个条件–

  • S *中任何两个子集的交集应为空。也就是说,X的每个元素最多应包含在S *的一个子集中
  • S *中所有子集的并集为X。这意味着并集应包含集合X中的所有元素。因此,可以说S *覆盖了X。

示例(标准表示)–
令S = {A,B,C,D,E,F},X = {1、2、3、4、5、6、7},使得–

  • A = {1,4,7}
  • B = {1,4}
  • C = {4,5,7}
  • D = {3,5,6}
  • E = {2,3,6 7}
  • F = {2,7}

那么S * = {B,D,F}是一个精确的覆盖,因为X中的每个元素在{B,D,F}子集中只包含一次。如果我们合并子集,那么我们将获得X的所有元素–
[Tex] B \ bigcup D \ bigcup F = \ {1,2,3,4,5,6,7 \} [\ Tex]

确切的承保范围问题是确定是否存在确切的承保范围的决策问题。它被认为是NP-完全问题。

问题可以以矩阵的形式表示,其中行表示S的子集,列表示X的元素。上述问题可以表示为:

问题矩阵

问题矩阵

在矩阵表示的上下文中,我们的确切覆盖范围是对行的选择,以使每一列在所选行中仅包含单个1。因此,我们可以在下面看到,在选定的行B,D,F中,每一列只有一个1。

确切的掩护

确切的掩护

算法X

唐纳德·克努斯(Donald Knuth)提出了一种算法X ,该算法可以找到精确覆盖问题的所有解决方案。 Donald Knuth提出的称为DLX的“跳舞链接”技术可以有效地实现算法X。

算法X是递归的,深度优先的回溯算法。它本质上是不确定的,这意味着对于相同的输入,它可以在不同的运行中表现出不同的行为。
以下是算法X的伪代码–

1. If the matrix A has no columns, the current partial solution
   is a valid solution; terminate successfully. 
2. Otherwise, choose a column c (deterministically). 
3. Choose a row r such that A[r] = 1 (nondeterministically). 
4. Include row r in the partial solution. 
5. For each column j such that A[r][j] = 1,
        for each row i such that A[i][j] = 1, 
            delete row i from matrix A. 
      delete column j from matrix A. 
6. Repeat this algorithm recursively on the reduced matrix A. 

r表示方法的非确定性选择,算法会将自身复制到子算法中。每个子算法都继承原始矩阵A,但相对于选择的r减少了矩阵(我们将在示例中很快看到)

子算法形成一个搜索树,其根源是原始问题,并且每个级别k都有子算法对应于上一级选择的行(就像n皇后搜索空间一样)。

如果选择的列c完全为零,则没有子算法,并且该过程未成功终止。 Knuth建议我们选择列中最少为1的列。如果没有剩余的列,那么我们知道我们已经找到了解决方案。

例子
考虑上面的示例,我们将对其应用算法X来找到确切的覆盖范围–

等级– 0
步骤1:我们的矩阵不为空,它具有列,然后继续
步骤2:第一列中的数字最少为1,因此为C-1,因此我们将其选中
步骤3:A和B行在C-1处有1,因此被选中。

因此,现在算法移至级别1的第一个分支

级别– 1(选择A行)
步骤4:选择A行并将其添加到部分解决方案中
步骤5:A行在第1、4、7列中有1

C-1在A和B行中有1个,C-4在A,B和C行中有1个,C-7在A,C,E和F行中有1个。

因此,应删除第1、4、7列以及A,B,C,E和F行。

步骤1 –矩阵不为空,请继续
步骤2 –最小数目为1的第一列为C-2
由于C-2列中没有1,因此我们的搜索将在此处成功终止。
现在我们的算法将在级别0回溯,并在级别1的第二个分支处继续执行B行

级别– 1(选择B行)
步骤4:选择B行并将其添加到部分解决方案中
步骤– 5:B行在C-1和C-4列中有1

C-1在A和B行有1。C-4在A,B和C行有1。

因此C-1,C-2和A,B,C行将从矩阵中删除。

现在我们重复算法–
步骤1:矩阵不为空,请继续
步骤2:C-5的最小数目为1,因此选择了它。

步骤3:D行在C-5处有1,因此被选中
现在,算法将移至第2级的第1个分支,矩阵的行为D,E和F

2级(选择D行)
步骤4:选择D行并将其添加到部分解决方案中。
步骤5:C-3,C-5,C-6在D行有1

在C-3行D和E具有1,在C-5行D具有1,在C-6行D具有1。

因此,应删除这些行和列,然后剩下一个仅包含F行和2、7列的矩阵。

现在,我们将重复该算法–
步骤1:矩阵不为空,请继续
步骤2:C-2是第一列,如果其中有1,则为最小数。所以选择了
步骤3:F行在C-2处有1,因此被选中。
现在,算法将移至级别3的第一个分支。

级别– 3(选择F行)
步骤4:将F行添加到部分解决方案中
步骤5:C-2和C-7在F行中的值为1。

C-2在F行有1个,C-7在F行有1个

因此,应删除C2,C7和F行。删除后,我们将留下一个空矩阵,以便我们的搜索可以在此处成功终止,并且我们有确切的封面{B,D,F}

子算法在2级回溯,并且在3级没有剩余的行。
它进一步回溯到1级。由于在级别1,我们的算法没有剩余行终止。

在下一篇文章中,我们将讨论如何有效地实现DLX来解决Exact Cover。

参考

  • https://zh.wikipedia.org/wiki/Exact_cover
  • https://zh.wikipedia.org/wiki/Knuth%27s_Algorithm_X