选择位掩码中与选择器位图中的1位重叠的设置位的跨度

Select spans of set bits in a bitmask that overlap with a 1-bit in a selector bitmap

本文关键字:重叠 置位 1位 位图 掩码 选择器 选择      更新时间:2023-10-16

给定:

  • 比特掩码a(例如,std::uint64_t),其包含至少一个集合(1)比特
  • 选择器位掩码ba(即a & b == b)的子集,并且具有至少一个位集

我想在a中选择与b:中的位重叠的连续1位的跨度

a = 0b1111001110001100;
b = 0b0000001010001000;
//c=0b0000001110001100
//    XXXX  YYY   ZZ

XXXX组在c中为0,因为b & XXXX为false。复制ZZ组是因为b设置了Z位中的一个。出于同样的原因,YYY组也被设置在c请注意,b可以在a中的单个组中具有多个设置位

因此,对于a中的每个连续的1s组,如果b在这些位置中的任何位置具有1,则设置c中的所有这些位。一个更复杂的例子:

std::uint64_t a = 0b1101110110101;
std::uint64_t b = 0b0001010010001;
// desired   c == 0b0001110110001
// contiguous groups   ^^^ ^^   ^  that overlap with a 1 in b
assert(a & b == b);           // b is a subset of a
std::uint64_t c = some_magic_operation(a, b);
assert(c == 0b0001110110001);

是否有任何位逻辑指令/内部函数(MMX、SSE、AVX、BMI1/BMI2)或位操作技巧使我能够有效地从ab计算c?(即没有环路)?


附加:

使用Denis答案中的提示,我只能想象基于循环的算法:

std::uint64_t a = 0b0110111001001101;
std::uint64_t b = 0b0100101000001101;
assert(a & b == b); // subset
std::cout << std::bitset< 16 >(a) << std::endl;
std::cout << std::bitset< 16 >(b) << std::endl;
std::uint64_t x = (a + b) & ~a;
std::uint64_t c = 0;
while ((x = (a & (x >> 1)))) { // length of longest 1-series times
    c |= x;
}
std::cout << std::bitset< 16 >(c) << std::endl;

uint64_t的情况下,您可以使用以下技巧:

让我们设置a = 0b11011101101。至少有一个0位是很重要的。位掩码有4个单独的区域,用1个位填充。如果执行c=a+(a&b),则如果在此区域中设置了b的至少一位,则每个1-填充区域都将溢出。所以你可以检查一下,哪个区域被飞越了。例如,如果您想要a的第2和第3区域中的1位,您可以这样做:

    assert(c & 0b00100010000);
    //              ^^^ ^^ this segments overflows