将位移位到掩码中给定位置的快速方法

时间:2018-11-15 18:44:04

标签: c++ bit-manipulation

我正在寻找一种快速方法来遍历掩码中设置的所有可能的位分配。

示例:

mask = 0b10011

结果= {0b00000,           0b00001,           0b00010,           0b00011,           0b10000,           0b10001,           0b10010,           0b10011}

我需要遍历所有这些。

目前,我使用与此类似的代码,效果很好:

int count = popCount(mask);
uint64_t number = 0;
for(uint64_t number = 0; number < (1 << count); ++number)
{
    uint64_t result = shiftBits(mask, number, count);
    //work with result
    //only some light operations
}
// shiftBits(0b10011, 0b101, 3) == 0b10001
// shiftBits(0b10011, 0b111, 3) == 0b10011
// shiftBits(0b10011, 0b000, 3) == 0b00000
uint64_t shiftBits(uint64_t mask, uint64_t number, int count) {
    uint64_t result = 0;
    for(int i = 0; i < count; ++i)
    { 
        uint64_t least = (mask & ~(mask - 1)); // get uint64_t with only least significant 1 set
        uint64_t bitSet = number & uint64_t(1); // check if last bit in number is set
        result |= least * bitSet; // if last bit is set, then set corresponding position in result
        mask &= mask - 1; // clear lsb set in mask
        number = number >> 1; // make 2nd lsb the next one to check
    }
    return result;
}

shiftBits示例:

mask =   0b10011001
number = 0b1  01  0 = 0b1010
result = 0b10001000

但是我想知道是否有人知道更快的方法来执行shiftBits中完成的操作,或者是否有更快的方法来创建此分配。 也许有些魔术或某种方式没有“写后读”和“读后写”冲突?

1 个答案:

答案 0 :(得分:9)

更新版本,用一个更简单的表达式替换升序枚举:

uint64_t i = 0;
do {
    // use i
    i = (i - mask) & mask;
} while (i);

这确实与旧答案具有相同的作用,但是节省了操作。您可以将其视为减去-1而不是加1,但这是被屏蔽的-1。


无需移动:您可以进行带掩码的增量。此处的想法是通过将掩码的零点暂时设置为1,然后再将其清除,从而使进位通过零点。例如:

uint64_t i = 0;
do {
    // use i
    i = ((i | ~mask) + 1) & mask;
} while (i);

向后迭代稍微简单一点,因为减量是通过零借位的,而被掩盖的位已经是零,例如:

uint64_t i = 0;
do {
    i = (i - 1) & mask;
    // use i
} while (i);