function popcount (x, n) {
if (n !== undefined) {
x &= (1 << n) - 1
}
x -= x >> 1 & 0x55555555
x = (x & 0x33333333) + (x >> 2 & 0x33333333)
x = x + (x >> 4) & 0x0f0f0f0f
x += x >> 8
x += x >> 16
return x & 0x7f
}
用于计算Hamming Weight。我想知道这些常数来自何处,以及通常如何发现这种方法。想知道是否有人知道描述它的资源。
答案 0 :(得分:2)
有掩码选择偶数个k位部分,k = 1给出0x55555555,k = 2给出0x33333333,k = 4给出0x0f0f0f0f。
二进制格式的掩码如下:
0x55555555 = 01010101010101010101010101010101
0x33333333 = 00110011001100110011001100110011
0x0f0f0f0f = 00001111000011110000111100001111
它们也是0xffffffff / 3、0xffffffff / 5和0xffffffff / 17的结果,但是这种算术见解在这种情况下可能没有用。
总体而言,这种计算汉明权重的方法具有树的形式,其中,首先将相邻的两个位相加为2位数字,然后将相邻的2位数字相加为4位数字,依此类推。
所有步骤都可以采用这种形式:
x = (x & m[k]) + ((x >> k) & m[k])
其中m[k]
是选择偶数k位部分的掩码。
但是许多步骤都为他们提供了捷径。例如,要对相邻位求和,只有4种情况需要考虑:
00 -> 00
01 -> 01
10 -> 01
11 -> 10
这可以通过提取两个位并求和来完成,但是x -= x >> 1 & 0x55555555
也可以。这会从2位部分减去最高位,所以
00 -> 00 - 0 = 00
01 -> 01 - 0 = 01
10 -> 10 - 1 = 01
11 -> 11 - 1 = 10
也许这可以通过“聪明和洞察力”发现,无论这些是什么。
在步骤x = (x + (x >> 4)) & 0x0f0f0f0f
(为清楚起见,添加了额外的括号),使用了几个属性。前面步骤的结果是4位字符串的汉明权重,每个存储在4位中,因此最多为0100。这意味着它们中的两个可以就地添加而不累加到下一个更高的部分,因为它们的总和最多可以容纳1000个。因此,该掩码无需在和之前屏蔽两次,而是在和之后屏蔽一次就足够了,该掩码有效地将偶数4位部分零扩展为8位部分。可以通过考虑每个步骤的最大值来发现这一点。
步骤x += x >> 8
具有类似的推论,但效果更好,即使不需要总和后也可以掩盖,这在从下至上的第二个字节和从上至下的字节中留下了一些“杂散位”,但是但这不会破坏下一步:>> 16
从底部开始丢弃第二个字节,最后所有杂散位都被x & 0x7f
删除了。