汉明距离常数从何而来

时间:2019-03-05 23:55:36

标签: cryptography bit-manipulation hamming-distance

The function

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。我想知道这些常数来自何处,以及通常如何发现这种方法。想知道是否有人知道描述它的资源。

1 个答案:

答案 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删除了。