找到一组位串的基础的算法?

时间:2012-07-18 02:49:01

标签: algorithm linear-algebra bit-manipulation

这适用于C ++中的a diff utility I'm writing

我有一个 n 字符集列表{“a”,“abc”,“abcde”,“bcd”,“de”}(取自 k的字母表 = 5个不同的字母)。我需要一种方法来观察整个列表可以通过字符集{“a”,“bc”,“d”,“e”}的分离来构造。也就是说,“b”和“c”是线性相关的,而每隔一对字母都是独立的。

在bit-twiddling版本中,上面的字符集表示为{10000,11100,11111,01110,00011},我需要一种方法来观察它们都可以通过将较小的位串与ORing一起构造设置{10000,01100,00010,00001}。

换句话说,我相信我正在寻找{0,1} k 中一组 n 不同位向量的“离散基础”。 This paper声称一般问题是NP完全......但幸运的是,我只是在寻找小案例的解决方案( k < 32)。

我可以想到用于生成基础的非常愚蠢的算法。例如:对于每个k 2 字母对,尝试演示(通过O( n )搜索)它们是依赖的。但我真的觉得有一个有效的比特纠缠算法,我还没有偶然发现。有谁知道吗?

编辑:毕竟我最终并不需要解决这个问题。但是我仍然想知道是否是一个简单的苦恼解决方案。

4 个答案:

答案 0 :(得分:2)

我在想一个不相交的集合数据结构,比如union find打开它的头部(而不是组合节点,我们将它们分开)。

<强>算法:

创建一个数组main,您可以将所有位置分配到同一组,然后:

for each bitstring curr
  for each position i
    if (curr[i] == 1)
      // max of main can be stored for constant time access
      main[i] += max of main from previous iteration

然后main中的所有不同数字都是你的不同集(可能使用实际的union-find算法)。

示例:

所以,main = 22222。 (我不会将1用作组来减少可能的混淆,因为curr使用位串。)

curr = 10000
main = 42222 // first bit (=2) += max (=2)

curr = 11100
main = 86622 // first 3 bits (=422) += max (=4)

curr = 11111
main = 16-14-14-10-10

curr = 01110
main = 16-30-30-26-10

curr = 00011
main = 16-30-30-56-40

然后用不同的数字分开:

{10000, 01100, 00010, 00001}

<强>改进:

为了降低main增加的速度,我们可以替换

main[i] += max of main from previous iteration

main[i] += 1 + (max - min) of main from previous iteration

编辑:根据j_random_hacker的评论进行修改

答案 1 :(得分:1)

你可以以空间为代价组合愚蠢算法的传递。

创建一个名为violations的向量,其长度为(k - 1) k / 2位(因此,k = 32为496。)对字符集进行单次传递。对于每个字母和每对字母,查找违规(即XOR这些字母的位,OR将结果放入violations中的相应位置。)当您完成时否定并读掉剩下的东西。

答案 2 :(得分:0)

您可以尝试Principal Component Analysis。有些风格的PCA专为binary或更常见的categorical数据而设计。

答案 3 :(得分:0)

由于某人将其显示为NP完整版,因此对于大型词汇,我怀疑你会比完整的可能性O((2 k - )进行强力搜索(可能进行各种修剪)做得更好。 1)* n)。至少在最坏的情况下,可能有些启发式方法在很多情况下会有所帮助,如您所链接的文章中所述。这是你的“愚蠢”方法,推广到所有可能的基础字符串,而不仅仅是长度为2的基础。

然而,对于小词汇,我认为这样的方法会做得更好:

  1. 你的话是否脱节?如果是这样,你就完成了(像“abc”和“def”这样的独立单词的简单例子)

  2. 按位和每对可能的单词执行。这为您提供了一组初始候选基础字符串。

  3. 转到第1步,但不使用原始字词,而是使用当前基础候选字符串

  4. 之后,您还需要包含任何不属于最终接受候选人之一的单个字母。也许还有其他一些小问题,例如未使用过的字母(使用类似按位或所有可能的单词)。

    考虑你的简单例子:

    第一遍给你a,abc,bc,bcd,de,d

    第二遍给你a,bc,d

    簿记给你a,bc,d,e

    我没有证明这是正确的,但我认为直觉上它至少是正确的方向。优势在于使用单词而不是蛮力的方法来使用可能的候选者。有足够多的单词,这种方法会变得很糟糕,但对于高达几百甚至几千的词汇表,我敢打赌它会很快。好消息是,即使对于巨大的k值,它仍然可以工作。

    如果你喜欢这个答案并给予赏金,我会很乐意尝试解决20行代码:)并提出一个更有说服力的证据。对我来说似乎非常可行。