在一个128位的小流中找到一个重复的对称位模式

Find a repeating symmetric bit pattern in a small stream of 128 bits

本文关键字:一个 对称 模式 128位      更新时间:2023-10-16

我如何快速扫描一组128位的完全相等的重复二进制模式,如010101…或0011001100…?

我有一些128位块,并希望看看它们是否与1的数量等于0的数量的模式相匹配,例如010101....或00110011…或0000111100001111…但不是001001001…

问题是模式可能不在它们的边界上开始,所以模式00110011..可以从0110011开始…,并将结束1位移位也(注意128位不是圆形的,所以开始不连接到结束)

010101年……情况很简单,它只是0xAAAA…或0 x5555……然而,随着图案变长,排列也变长。目前,我使用重复移位值,如在这个问题中概述的最快的方式来扫描位流中的位模式,但更快的东西会很好,因为我在这个例程中花费了所有CPU的70%。其他海报有一般情况下的解决方案,但我希望我的模式的对称性质可能导致更理想的东西。

如果有帮助的话,我只对长达63位的模式感兴趣,最感兴趣的是2模式(0101…00110011……0000111100001111……等)虽然存在5个1/5个0等模式,但这些非2次幂序列小于0.1%,因此如果它有助于更快地处理常见情况,则可以忽略。

完美解决方案的其他约束条件是汇编指令数量少,没有随机内存访问(即,大型彩虹表不理想)。

编辑。更精确的图案细节。

我最感兴趣的是0011和0000,1111和0000,0000,1111,1111和160/1和32 0/1(逗号仅用于可读性)的模式,其中每个模式在128位内连续重复。重复部分长度不是2、4、8、16、32位的模式不那么有趣,可以忽略。(例如000111…)

扫描的复杂性在于模式可以从任何位置开始,而不仅仅是在01或10过渡。因此,例如,下面所有的都将匹配00001111的4位重复模式…(为了便于阅读,每隔4位逗号)(省略号表示重复相同)

0000, 1111……或0001,1110……或0011,1100……或0111,1000……或1111,0000……或1110,0001……或1100,0011……0111年或1000年,

在128位内,相同的模式需要重复,存在两个不同的模式是不感兴趣的。这不是一个有效的模式。0000、1111、0011、0011……因为我们从4位重复变成了2位重复。

我已经验证了1的数量是64,这对于所有的2次方模式都是正确的,现在需要确定有多少位组成重复模式(2,4,8,16,32)以及模式移位了多少。例如模式0000,1111是一个4位模式,移位0。在0111年,1000年……是一个4位模式移位3。

让我们从模式在其边界上开始的情况开始。您可以检查第一个位并使用它来确定您的状态。然后开始循环遍历块,检查第一个位,增加一个计数,左移并重复,直到找到相反的位。现在可以使用这个初始长度作为bitset的长度。将计数重置为1,然后计数下一组相反的位。当您切换时,检查长度与初始长度,如果它们不相等,就会出错。这是一个快速的函数-它似乎可以像预期的那样处理字符,并且应该不会太难扩展它来处理32字节的块。

unsigned char myblock = 0x33;
unsigned char mask = 0x80, prod = 0x00;
int setlen = 0, count = 0, ones=0;
prod = myblock & mask;
if(prod == 0x80)
  ones = 1;
for(int i=0;i<8;i++){
  prod = myblock & mask;
  myblock = myblock << 1;
  if((prod == 0x80 && ones) || (prod == 0x00 && !ones)){
    count++;
  }else{
    if(setlen == 0) setlen = count;
    if(count != setlen){
      printf("Bad blockn");
      return -1;
    }
    count = 1;
    ones = ( ones == 1 ) ? 0 : 1;
  }
}
printf("Good block of with % repeating bitsn",setlen);
return setlen;

现在处理有偏移的块,我建议计算位数,直到第一次"翻转"。存储这个数字,然后运行上面的例程,直到遇到长度不等于其他集合的最后一个片段。将初始位添加到最后一个片段的长度,然后您应该能够将其与其他集合的长度进行正确的比较。

这段代码非常小,并且通过缓冲区进行位移位不需要CPU做太多的工作。我很想看看这个解决方案最终如何与您当前的解决方案进行比较。

这类问题的一般解决方案是为模式创建一个良好的散列函数,并将每个模式存储在散列映射中。一旦为模式创建了散列映射,就可以尝试使用输入流在表中查找。我还没有代码,但如果你在代码中被击中,请告诉我。

我考虑过创建一个状态机,这样每下一个字节(从16个字节中)都会推进它的状态,并且在大约16个状态转换之后,您将识别出模式。但这看起来不太有希望。数据结构和逻辑看起来更复杂。

相反,为什么不预先计算所有126个模式(从01到32个0 + 32个1),对它们进行排序并执行二进制搜索?这将给你最多7次二分查找迭代。并且您不需要存储每个模式的所有16个字节,因为它的一半是相同的。这为模式数组提供了126*16/2=1008字节。每个模式还需要2个字节来存储零(1)次运行的长度以及相对于您认为未移位的模式的移位。总共126*(16/2+2)=1260字节的数据(应该对数据缓存比较温和)和非常简单和微小的二进制搜索算法。基本上,它只是对你在问题中提到的答案的改进。

你可能想在4-5次二进制搜索迭代后切换到线性搜索。这可能会给整个算法一个小小的提升。

最终,获胜者是由测试/分析决定的。这就是你应该做的,获得一些实现并在真实系统中的真实数据上比较它们。

在整个128流中重复模式的限制使得组合的数量有限,并且序列将具有易于检查的属性:

需要反复检查高低部是否相同;如果它们是相反的,检查特定长度是否包含连续的

 8-bit repeat at offset 3:  00011111 11100000 00011111 11100000
 ==> high and low 16 bits are the same
 00011111 11100000 ==> high and low parts are inverted.
 Not same, nor inverted means rejection of pattern.

这时,我们需要检查是否有一个1的序列——在左边加上'1',检查它是否是2的幂:n==(n &