简体   繁体   English

这种神奇的位计数方法是如何工作的?

[英]How does this magic bit counting method work?

While working on the XKCD April Fool'sskein hash collision problem I ran across this strange, fast, multiplicative method of counting the set bits in a word:在处理 XKCD 愚人节的 skein哈希冲突问题时,我遇到了一种奇怪的、快速的、乘法计算单词中设置位的方法

c = (v * 0x200040008001ULL & 0x111111111111111ULL) % 0xf;

Why does this work / what's going on?为什么这有效/发生了什么? Can we generalize this method (for example, to work for our 128-bit values from the problem)?我们可以推广这个方法吗(例如,从问题中处理我们的 128 位值)?

Also, I can't help but think it's related to this question about moving bits around using a clever magic number .另外,我不禁认为它与这个关于使用巧妙的幻数移动位的问题有关。

This doesn't count the set bits in a 32-bit word, actually, since the output by the nature of the modulo operator must be less than 0xf (aka 15).这实际上不计算 32 位字中的设置位,因为模运算符的性质输出必须小于0xf (也称为 15)。

First, let's pay special attention to the modulo operator.首先,让我们特别注意模运算符。 Why 15?为什么是15? And why are we masking to the least significant bit in each nybble?为什么我们要屏蔽每个 nybble 中的最低有效位?

Well, note that each least significant nybble bit is of the value 16^k for some k .好吧,请注意,对于某些k ,每个最低有效 nybble 位的值为16^k k Note that 16 mod 15 is 1, therefore 16^k mod 15 is 1 for any non-negative integer value of k .需要注意的是16 mod 15是1,因此16^k mod 15是1的任意非负整数值k

This is convenient since it means that 16^k1 + 16^k2 + ... + 16^kn = n mod 15 .这很方便,因为这意味着16^k1 + 16^k2 + ... + 16^kn = n mod 15

Put another way, the modulo operator is effectively counting the number of set least significant nybble bits due to the above math -- as long as no other bits in the nybbles are set.换句话说,由于上述数学运算,模运算符有效地计算设置的最低有效 nybble 位的数量——只要 nybble 中没有设置其他位。 (They'd just get in the way.) (他们只会碍事。)

However, we don't want to just count specially formatted bits in nybbles.但是,我们不想只计算 nybbles 中特殊格式的位。 We want to count the number of bits set in an arbitrary value.我们想要计算设置在任意值中的位数。 The trick is to get those value bits into those specially formatted nybbles by moving the bits around.诀窍是通过移动这些位来将这些值位放入那些特殊格式的 nybbles 中。 The ultimate order of the nybbles isn't important, as long as we can move one bit of the value to one nybble. nybble 的最终顺序并不重要,只要我们可以将一位值移动到一个 nybble 中即可。 In theory since we're using 64-bit values to do the counting we can map each bit in a 16 bit value to its own nybble, giving 4 * 16 = 64 bits total, just within our 64-bit allowance.理论上,由于我们使用 64 位值进行计数,我们可以将 16 位值中的每一位映射到它自己的 nybble,总共4 * 16 = 64位,正好在我们的 64 位允许范围内。 However, note that because we're using modulo 15, any value with 15 or 16 set bits will display as 0 or 1, respectively.但是,请注意,因为我们使用的是模 15,所以任何具有 15 位或 16 位设置位的值将分别显示为 0 或 1。

Now let's refocus on the strange constant: 0x200040008001ULL现在让我们重新关注这个奇怪的常量: 0x200040008001ULL

Let's take note of which bits are set (where bit 0 is the least significant bit): 0, 15, 30, and 45. You may have noticed they're spaced in 15 bit intervals.让我们注意设置了哪些位(其中位0是最低有效位):0、15、30 和 45。您可能已经注意到它们以 15 位为间隔。 This is convenient because for values that are less than 2^15 this multiplication just creates multiple shifted copies of the value in a 64-bit word.这很方便,因为对于小于2^15值,此乘法只会在 64 位字中创建该值的多个移位副本。 But when values become equal than or greater than 2^15 the copies start overlapping additively which is no longer useful for counting the bits particularly.但是当值变得等于或大于2^15 ,副本开始叠加重叠,这对于特别计数位不再有用。 That's okay, though, because with that modulo operation we aren't even able to reliably count up to 15 bits of information anyway.不过,这没关系,因为通过这种模运算,我们甚至无法可靠地计算出多达 15 位的信息。 (However, if the result of the modulo operation is 0, we know either all or none of the bits are set, again assuming we only get values less than 2^15.) (然而,如果模运算的结果是 0,我们知道所有位都被设置,或者没有,再次假设我们只得到小于 2^15 的值。)

So, we have shifted copies of our 15-bit number in our 64-bit register.因此,我们在 64 位寄存器中移动了 15 位数字的副本。 The second step is that the mask extracts only the least significant bits of each nybble.第二步是掩码仅提取每个 nybble 的最低有效位。 Because the lowest significant bit of each nybble is equivalent to 1 (mod 15) the modulo operator effectively counts the number of least significant bits set in the nybbles.因为每个 nybble 的最低有效位等于1 (mod 15)所以模运算符有效地计算了 nybble 中设置的最低有效位的数量。

The only detail remaining is to make sure that each bit in our 15-bit number lands in a least significant nybble bit slot exactly once.剩下的唯一细节是确保我们的 15 位数字中的每一位都恰好落在最低有效 nybble 位槽中一次。

Let's check:让我们检查:

The first bit set, 0, doesn't shift the value at all, giving our value bits 0 through 14.
This places value value bits 0, 4, 8, and 12 in a least significant nybble bit slot.

The second bit set, 15, gives our value bits 15 through 29.
This places our value bits 1, 5, 9, and 13 in bits 16, 20, 24, and 28.

The third bit set, 30, gives our value bits 30 through 44.
This places our value bits 2, 6, 10, and 14 in bits 32, 36, 40, and 44.

Finally, the forth bit set, 45, gives our value bits 45 through 59.
This places our value bits 3, 7, 11, and 15 in bits 48, 52, 56, and 60.

Bits accounted for:
0, 4, 8,  and 12
1, 5, 9,  and 13
2, 6, 10, and 14
3, 7, 11, and 15

It's easy to visually verify that this maps 16 bits.很容易直观地验证这是否映射了 16 位。 However, note the mask is actually 15 1 's, not 16. So the bit placed in the last nybble (starting at bit 60, representing bit 15 of our value, the highest bit of a 16-bit value) is effectively ignored.但是,请注意掩码实际上是 15 1 ,而不是 16。因此,放置在最后一个 nybble 中的位(从位 60 开始,代表我们值的第 15 位,即 16 位值的最高位)实际上被忽略了。

With that, the total technique is complete:这样,整个技术就完成了:

  1. Use multiplication to map each bit into a least significant nybble bit.使用乘法将每个位映射到最低有效 nybble 位。
  2. Use a mask to select only the desired nybble bits.使用掩码仅选择所需的 nybble 位。
  3. Note that a least significant nybble bit is equivalent to 1 (mod 15) .请注意,最低有效 nybble 位等效于1 (mod 15)
  4. Therefore, (mod 15) will simply add those bits together... up to 14 bits set.因此, (mod 15)将简单地将这些位加在一起……最多设置 14 位。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM