这适用于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 )搜索)它们是依赖的。但我真的觉得有一个有效的比特纠缠算法,我还没有偶然发现。有谁知道吗?
编辑:毕竟我最终并不需要解决这个问题。但是我仍然想知道是是否是一个简单的苦恼解决方案。
答案 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的基础。
然而,对于小词汇,我认为这样的方法会做得更好:
你的话是否脱节?如果是这样,你就完成了(像“abc”和“def”这样的独立单词的简单例子)
按位和每对可能的单词执行。这为您提供了一组初始候选基础字符串。
转到第1步,但不使用原始字词,而是使用当前基础候选字符串
之后,您还需要包含任何不属于最终接受候选人之一的单个字母。也许还有其他一些小问题,例如未使用过的字母(使用类似按位或所有可能的单词)。
考虑你的简单例子:
第一遍给你a,abc,bc,bcd,de,d
第二遍给你a,bc,d
簿记给你a,bc,d,e
我没有证明这是正确的,但我认为直觉上它至少是正确的方向。优势在于使用单词而不是蛮力的方法来使用可能的候选者。有足够多的单词,这种方法会变得很糟糕,但对于高达几百甚至几千的词汇表,我敢打赌它会很快。好消息是,即使对于巨大的k值,它仍然可以工作。
如果你喜欢这个答案并给予赏金,我会很乐意尝试解决20行代码:)并提出一个更有说服力的证据。对我来说似乎非常可行。