bit twiddling hacks 网站没有这个特殊的操作,虽然它有the one that inspired this answer。
这个想法是离线计算一个可以插入以下模板的幻数列表。该模板由一个基本步骤组成,重复 6 = lg 64 次:纠正输出位的索引 mod 2**k for k = 1, 2, ..., 6,假设在每个步骤开始时索引为正确的 mod 2**(k-1)。
例如,假设我们希望变换
x = a.b..c.d
76543210
进入
....abcd
76543210.
位 a 位于位置 7 并且需要转到 3(正确位置 mod 2)。位b 位于5 位置,需要转到2(位置不正确mod 2)。位c 位于2 位置,需要转到1(位置不正确mod 2)。位 d 位于位置 0 并且需要保留(正确位置 mod 2)。第一个中间步骤是像这样移动b 和c。
a..b..cd
76543210
这是通过
完成的
x = (x & 0b10000001) | ((x >>> 1) & 0b00010010);
//76543210 //76543210
这里>>> 表示逻辑移位,0bxxxxxxxx 表示大端二进制字面量。现在我们有两个问题:一个是奇数位,一个是偶数位。使这个算法快速的原因是这些现在可以并行处理。
为了完整起见,其他两个操作如下。位 a 现在位于位置 7 并且需要转到 3(正确位置 mod 4)。位 b 现在位于位置 6 并且需要转到 4(不正确的位置 mod 4)。位 c 和 d 需要保留(正确位置 mod 4)。得到
a....bcd
76543210,
我们这样做
x = (x & 0b10000011) | ((x >>> 2) & 0b00000100);
//76543210 //76543210
位 a 现在位于位置 7 并且需要转到 3(不正确的位置 mod 8)。位 b、c 和 d 需要保留(正确位置 mod 8)。得到
....abcd
76543210,
我们这样做
x = (x & 0b00000111) | ((x >>> 4) & 0b00001000);
//76543210 //76543210
这是 Python 的一些概念证明(抱歉)。
def compute_mask_pairs(retained_indexes):
mask_pairs = []
retained_indexes = sorted(retained_indexes)
shift = 1
while (retained_indexes != list(range(len(retained_indexes)))):
mask0 = 0
mask1 = 0
for (i, j) in enumerate(retained_indexes):
assert (i <= j)
assert ((i % shift) == (j % shift))
if ((i % (shift * 2)) != (j % (shift * 2))):
retained_indexes[i] = (j - shift)
mask1 |= (1 << j)
else:
mask0 |= (1 << j)
mask_pairs.append((mask0, mask1))
shift *= 2
return mask_pairs
def remove_bits_fast(mask_pairs, x):
for (log_shift, (mask0, mask1)) in enumerate(mask_pairs):
x = ((x & mask0) | ((x >> (2 ** log_shift)) & mask1))
return x
def remove_bits_slow(retained_indexes, x):
return sum(((((x // (2 ** j)) % 2) * (2 ** i)) for (i, j) in enumerate(sorted(retained_indexes))))
def test():
k = 8
for mask in range((2 ** k)):
retained_indexes = {i for i in range(k) if (((mask // (2 ** k)) % 2) == 0)}
mask_pairs = compute_mask_pairs(retained_indexes)
for x in range((2 ** k)):
assert (remove_bits_fast(mask_pairs, x) == remove_bits_slow(retained_indexes, x))
test()