【问题标题】:Find the longest prefix of bit arrays查找位数组的最长前缀
【发布时间】:2012-07-31 08:01:01
【问题描述】:

我正在尝试找到一种快速算法来搜索多个位数组的最长前缀。在我的应用程序中,这些位数组可以无限长且长度可变。例如,如果我有那些​​位数组:

0b1011001
0b1001101
0b1001010
0b1010100

最长的前缀是 10。 我目前正在对位数组进行 ORing 和 NAND 运算,以找到它们共同的 0 和 1,并将结果一起 XOR。

OR
0b1011111

NAND
0b0111111

XOR
0b1100000

有更快的解决方案吗?

【问题讨论】:

  • 对所有数组进行 ORing 似乎是一个非常昂贵的解决方案。当您发现第一个不匹配时,您可以从左到右停止扫描数组。这将是 O(kn),其中 n 是数组的数量,k 是公共前缀的长度。
  • @Haile ORing 和 NANDing 在我的情况下并不是那么昂贵,因为位数组在内部由整数表示。从左到右扫描乍一看似乎很幼稚......
  • 您写道“这些位数组可以无限长并且长度可变”!而且,这并不幼稚。对于任意长数组,它比 ORing 整个数组要快得多。

标签: python algorithm binary bit-manipulation


【解决方案1】:

关于你的方法

它在位数组的数量上可以很好地扩展(线性)。

它不能很好地根据位数组的大小进行缩放,理想情况下它应该根据公共前缀的长度而不是位数组的大小进行缩放。

处于低水平

对位数组中单个字节/字的位操作应该比一次一个地遍历位快得多。 (但不确定 Python 可以为您提供多少低级控制)。

第一个建议

如果这是一种像 C 这样的低级语言,我会以与您类似的方式解决这个问题,但会从其他答案中获得一些想法。

在我的示例中,我假设计算机是 64 位机器。

我从(OR NAND XOR)每个位数组的前 64 位开始,(这些是 64 位机器上的基本操作,复杂度仅为 O(# of arrays))。

然后快速找到结果中第一个设置位的位置,(大多数计算机内置或至少在优化的汇编代码中都有一些快速方法,for C,如果有设置位,则返回该值.

否则,在每个位数组的接下来的 64-127 位上重复。

(您将需要以某种方式处理不同长度的位数组,可能通过找到该组的最小长度位数组,然后将其用作最大值。)

这种方法的好处是它在位数组的数量上是线性的,并且是公共前缀的长度是线性的。

第二个建议

如果有大量位数组,您可以通过使用并行来获得加速。

首先,您可以在运行 NAND 的同时运行 OR。

有了更多的独创性,您可以应用以下内容:

如果我有 4 个位数组 A、B、C、D

代替 (((A OR B) OR C) OR D)

我可以做 (A OR B) OR (C OR D)。

在这两种情况下,执行相同数量的 OR。

但是第二种方法可以有效地并行化(实际上第二种方法在单核机器上可能更快,因为通常 CPU 实际上会有多个 ALU。)

写出逻辑有点棘手,因为您不能使用带有单个临时变量的单个 for 循环来保存 OR 的结果。

您可以想象将子结果存储在一个长度为位数组数量一半的数组中。将 A OR B 的子结果存储在 array[0] 中,将 C OR D 的子结果存储在 array[1] 中,然后执行 array[0] OR array[1]。 (您可以将该结果存储在一个长度为一半的新数组中,或者覆盖数组中的值以节省空间和内存分配)。

将工作分成足够大的块,以保持整个计算机忙碌而不会引入太多开销。

使用足够多的处理器,您可以处理位数组数量的复杂性,而不是线性的。但实际上,在 6 核机器上获得 5 倍的加速可能会相当不错。

【讨论】:

  • 这很有帮助。我实际上同时在做 OR 和 NAND。我还使用整数来存储位数组,所以我正在执行字节/字操作。第二个建议是我正在寻找的那种优化。
【解决方案2】:

您不需要对所有阵列进行 ORing 或 NAND 运算(这将非常昂贵,因为它们的长度是任意的)。您可以在发现第一个不匹配时停止从左到右扫描数组。这将是 O(kn),其中 n 是数组的数量,k 是公共前缀的长度。

我的python很糟糕,所以我只举一个非常简单的例子,有2个固定等长的数组为了清楚起见

a = [1,0,1,1,0,0,1]
b = [1,0,1,1,0,1,1]

for x in xrange(0,7):
    if a[x] != b[x]:
        print a[0:x]
        break

output:
[1, 0, 1, 1, 0]

显然你必须解决这个问题,如果你理解代码背后的逻辑,我想我会很容易。

  • 在所有数组的第 x 位上迭代 x,直到发现不匹配(即数组不具有所有相同的位值),或者直到最短数组的末尾
  • 输出 array1 的前 x 位。

【讨论】:

    【解决方案3】:

    在某些情况下,最佳解决方案是使用prefix trees,它的复杂度为 O(n),其中 n 是二进制字符串的共享前缀的总和,但系数很大。

    【讨论】:

    • 这适用于任意数量的数组,不会因为对数的增加而爆炸,所以我会赞成它。 (虽然,实际上并不需要以不同的结构恢复所有信息)。
    • 刚刚意识到 OP 不需要 2 个位数组的最长公共前缀,而是所有位数组中最长的前缀。
    【解决方案4】:

    假设您有输入字符串 s1,s2,s3 ...

    1. 让 s = commonSubString(s1,s2)
    2. 对于 i=3..n
      1. s = commonSubString(s,s2)
    3. 返回

    在最坏的情况下 s1 和 s2 可以相同,在这种情况下,您可以使用启发式方法(例如,首先将 s 的初始长度限制为 k=100。如果最终 s 的长度仍然为 k=100,则重复整个过程,但从每个字符串的位置 k+1 开始)。

    【讨论】:

      猜你喜欢
      • 2013-10-31
      • 2012-02-01
      • 2014-10-13
      • 1970-01-01
      • 2011-12-23
      • 2017-01-23
      • 1970-01-01
      • 2018-09-30
      • 1970-01-01
      相关资源
      最近更新 更多