选择位掩码中与选择器位图中的 1 位重叠的设置位范围

Posted

技术标签:

【中文标题】选择位掩码中与选择器位图中的 1 位重叠的设置位范围【英文标题】:Select spans of set bits in a bitmask that overlap with a 1-bit in a selector bitmap 【发布时间】:2016-05-19 04:31:04 【问题描述】:

给定:

一个位掩码a(比如std::uint64_t),其中至少包含一组(1)位。 选择器位掩码b,它是a(即a & b == b)的子集,并且至少设置了一个位。

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

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

c 中的 XXXX 组为 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? (即没有循环)?


补充:

使用丹尼斯回答中的提示,我只能想象基于循环的算法:

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;

【问题讨论】:

“一个段”是否包括长度为 1 的段? @M.M 是的,1-segment 只是 1-s 的非零长度序列。 我花了大约 10 分钟来解码您的描述,因此我进行了编辑以澄清未来的读者。我假设您无意中遗漏了英特尔的 BMI1/BMI2 扩展,而不是因为Haswell is too new。您确实提到了 AVX,这绝不是基线:即使在 Skylake Celeron/Pentium 上也不支持,只有 i3 和更高版本。谢谢,英特尔 :( 实际上,没有任何 BMI 指令本身显然是有用的。不过,可能 pext / pdep 作为构建块。你绝对需要你指定格式的输出吗? Denis 的答案丢失了有关每个组有多大的信息,但每个匹配组的结果中确实有 1。 @PeterCordes 感谢您的指正。非常感谢。 【参考方案1】:

如果是uint64_t,你可以这样做:

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

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

【讨论】:

在我的真实情况下,MSB 为零。不错。 解释:a&amp;b == b,所以这一步是多余的。使用&amp; ~a 进行屏蔽会删除a 中选定组的进位以外的所有位。

以上是关于选择位掩码中与选择器位图中的 1 位重叠的设置位范围的主要内容,如果未能解决你的问题,请参考以下文章

位掩码 - C中的按位运算

有效地找到匹配位掩码的第一个元素

通过权限位掩码从 MySQL 数据库中选择用户?

位掩码选择向量/集元素?

C中的位掩码

这个按位汉明(31,26)编码器如何在 C 中工作? (位掩码)