【问题标题】:64 bit popcount using bit tricks in Python在 Python 中使用位技巧的 64 位 popcount
【发布时间】:2020-01-27 22:42:43
【问题描述】:

我想在 Python 中实现 64 位 popcount 的小技巧。我尝试复制this code如下:

def popcount_test(y):
    y -= ((y >> 1) & 0x5555555555555555)
    y = (y & 0x3333333333333333) + (y >> 2 & 0x3333333333333333)
    return ((y + (y >> 4)) & 0xf0f0f0f0f0f0f0f) * 0x101010101010101 >> 56

不幸的是,这是不对的。我们可以通过对 int 1234 执行 popcount 来看到这一点。

popcount_test(1234)
261

bin(1234).count('1')
5

在 Python 中正确的位技巧是什么?

可以通过以下方式进行进一步测试:

import random
num = random.randint(0, 2**64-1)
print(popcount_test(num), bin(num).count('1'))

【问题讨论】:

  • 与类似的 C 代码最明显的区别是 Python 中的整数乘法是“无限精度”,但 C 代码只保留了乘积的最低有效 64 位。因此,首先,尝试通过& 屏蔽产品,并使用0xffffffffffffffff 对其进行屏蔽。
  • @TimPeters 对。或者&0xff>> 56 之后(很容易看到它会修复所示情况)。
  • 我可以确认上述两个修复都有效(我测试了与@HeapOverflow 建议的相同的东西,但之前的一项操作 Tim Peters 会做同样的事情)。
  • 在实践中,我也会先转换 &0xff。不过,我给出的答案是让它尽可能地无意识地模仿 C 代码的实际作用;-)

标签: python bit-manipulation


【解决方案1】:

问题是 C 版本期望乘法结果只产生低位 64 位,但 Python 使用扩展精度整数,所以你得到了整个结果。您可以通过在移位后将结果屏蔽为 8 位来修复它:

def popcount_test(y):
    y -= ((y >> 1) & 0x5555555555555555)
    y = (y & 0x3333333333333333) + (y >> 2 & 0x3333333333333333)
    return (((y + (y >> 4)) & 0xf0f0f0f0f0f0f0f) * 0x101010101010101 >> 56) & 0xff

这会产生以下内容:

>>> popcount_test(1234)
5
>>> 

【讨论】:

    【解决方案2】:

    为了使解决方案显而易见,我在此处添加它,但归功于@TimPeters 和@Heap-Overflow

    def popcount_test(y):
        y -= ((y >> 1) & 0x5555555555555555)
        y = (y & 0x3333333333333333) + (y >> 2 & 0x3333333333333333)
        return ((((y + (y >> 4)) & 0xf0f0f0f0f0f0f0f) * 0x101010101010101)  >> 56) & 0xff
    

    这就是 Python 在Modules/mathmodule.c 中的做法:

    static unsigned long
    count_set_bits(unsigned long n)
    {
        unsigned long count = 0;
        while (n != 0) {
            ++count;
            n &= n - 1; /* clear least significant bit */
        }
        return count;
    }
    

    【讨论】:

    • 如果可能的话,我想用更少的操作来完成它。因此,黑客。
    • 认真对待我的评论 ;-) 你得到的答案是 261 = 256 + 5。这是你想要的答案,除了第 2**8 位也已设置。在 C 代码中,最终右移 56 显然永远会返回大于 255 的结果(它将最高有效字节移动到最低有效字节位置)。但是因为你的 product 还没有被削减到 64 位(C 会自动这样做),所以你的 Python 代码没有做同样的事情。
    猜你喜欢
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 2016-11-30
    • 2015-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多