这个(可能)NP完整益智游戏的启发式算法

时间:2014-08-13 18:12:15

标签: algorithm heuristics

我在计算机科学论坛上询问这个问题是否是NP-complete,但是要求编程启发式算法似乎更适合这个网站。所以就这样了。

您将获得一个NxN网格的单位正方形和2N长度为N的二进制字符串。目标是用0和1填充网格,以便每个字符串在网格中只出现一次,水平(从左到右) )或垂直(自上而下)。或者确定不存在这样的解决方案。如果N没有固定,我怀疑这是NP完全问题。然而,是否有任何启发式方法可以加快搜索速度,而不是蛮力尝试所有方法用N个垂直字符串填充网格?

4 个答案:

答案 0 :(得分:0)

听起来你想要填字游戏网格填充算法:

  • 首先,构建2N字符串的2N子集 - 每个子集都具有特定位置的特定位的所有字符串。因此,子集(0,3)是在第3个位置具有0的所有字符串,而子集(1,5)是在第5个位置具有1的所有字符串。

  • 该算法是一个基本的暴力深度拳头搜索,尝试将所有可能的字符串映射到网格中的插槽,并对不可能的分支进行严格修剪

  • 您的搜索状态是一组字符串到插槽的分配,以及一组可能的剩余插槽分配。初始状态有0个赋值和2N个集合,所有这些都包含所有2N个字符串。

  • 在搜索的每个步骤中,从可能的集合中选择最受约束的集合(具有最少元素的集合)。在该槽中依次尝试集合中的每个元素(将其添加到分配中并从集合集中移除),并通过删除所选字符串并将交集集与子集(X,x)相交来约束所有剩余集合集。 N)(在步骤1中计算)其中X是来自所选字符串的位,N是所选字符串的行/列号

  • 如果您在上面选择时找到一个空集,那么到目前为止没有解决方案可以选择

这仍然是EXPTIME,但它的速度和你一样快。由于主要耗时步骤是设置交叉点,因此使用2N位二进制字符串进行集合表示非常快 - 对于N = 32,这些集合适合64位字,并且可以与单个AND指令相交。它还有助于获得POPCOUNT指令,因为您还需要设置大小。

答案 1 :(得分:0)

我记得为我的朋友编写了这款游戏的5x5物理版本,但我当时使用了暴力。我只能想到这种启发式:

考虑带有这8个字符串的4x4地图(从左到右阅读):

1 1 0 1
1 0 0 1
1 0 1 1
1 0 1 0

1 1 1 1
1 0 0 0
0 0 1 1
1 1 1 0

(注意,这已经解决,因为第二个4是前4个转置)

首次尝试:

我们将从左到右选择列。由于8个字符串中的7个字符串以1开头,因此我们会尝试将大多数1 s的字符串放到第一列(这样我们就可以在完成列时更轻松地放置行)。

在第二列中,大多数字符串都为0,因此您也可以尝试将包含大多数零的字符串放到第二行,依此类推。

我会调用 wide-1 预测,因为它一次只能查看一列

(可能)改进:

您可以一次查看2列(wide-2预测,如果我可以这样称呼它)。在这种情况下,从8个字符串开始,前两位的最常见组合是10(5/8),因此您希望选择前两列,以便组合10出现同样多的尽可能(在这种情况下,1111后跟1000在开始时有3个10

(当然你不必在2点停下来)

<强>弱点:

  1. 我不知道这是否有效。我刚刚做好了,并认为它可能有效。
  2. 如果您选择wide-X预测,X
  3. 的可能性数量是指数级的
  4. 如果组合的分布均匀,则绝对会失败。
  5. 你能做什么:

    正如我所说,这个游戏有5x5的物理适应性,只有在那里你还可以从右到左和从下到上排列字符串,如果你找到这个名字,你可以进一步谷歌。遗憾的是我不记得了。

答案 2 :(得分:0)

这可以作为具有O(N ^ 2)变量和约束的0/1整数线性程序来解决。首先,如果字符串i被分配给行j,则变量Xij为1(其中j = 1到N是行,j =(N + 1)到2N是列)。然后网格中的每个方格都有一个变量,表示条目是0还是1.如果方形的位置是(i,j),变量为Yij,那么对应于j的所有X变量的总和在位置i中具有1的字符串等于Yij,并且对应于在位置i中具有0的字符串的行j的所有X变量的总和等于(1-Yij)。类似地,对于第i行和第j行。最后,每个字符串i(在所有行j上求和)的所有X变量Xij的总和等于1.

对于加速0/1整数编程的求解器已经进行了大量研究,因此对于许多示例,这可能经常能够处理相当大的N(如N = 100)。此外,在某些情况下,求解宽松的非整数线性程序并将解决方案四舍五入为0/1可能会在多项式时间内产生有效解。

答案 3 :(得分:0)

我们可以从2N字符串中选择第一个lg 2N行,然后因为2 ^(lg 2N)= 2N,在很多情况下,应该没有很多方法来分配N列,以便长度为lg 2N的前缀受到尊重。然后填写所有行,以便检查它们是否已找到解决方案。我们还可以尝试在开头分配更多行,并在初始行之外填写不同的行组合。 (例如,我们可以尝试填充从网格中任何位置开始的连续行。)

从2N个字符串中分配lg 2N行的运行时间是O((2N)^(lg 2N))= O(2 ^((lg 2N)^ 2)),其增长慢于2 ^ N.分配列以匹配前缀是最难预测运行时间的部分。如果前缀在指定的行中出现K次,并且有M个剩余的字符串具有前缀,则此前缀的赋值数为M *(M-1) ... (M -K + 1)。可能的列分配总数是这些术语在行之间出现的所有前缀的乘积。如果这变得太大,则可以增加最初分配的行数。但是,除非假设NxN网格是随机填充的,否则很难预测最坏情况下的运行时间。