【问题标题】:Counting non-zero bits in a list of 32-bit integers计算 32 位整数列表中的非零位
【发布时间】:2015-07-21 09:37:37
【问题描述】:

编辑:问题已解决,不过请注意下面的代码,哈哈,我从一开始就有一些概念上的错误。

我尝试计算各种 32 位整数中的非零位的时间比我愿意承认的要长,但似乎总是有点不对劲。

例如,数字的正确数量的非零位:

13676 9190872 136669 -17621 -1631 -183 15570 0 495 468656377 -1340216 -91

应该是:

8 12 10 26 25 27 8 0 8 18

但是,无论我尝试什么,我总是会在至少几个(尤其是负数)上得到几个数字。

目前我正在使用:

def bitCount():
    numbers = input().split()
    answer = []
    for num in numbers:
        data = bin(((1 << 32) + int(num)) & -5)
        bitCount = 0
        for char in data[3:]:
            if char == '1':
                bitCount += 1
        answer.append(str(bitCount))
    print(' '.join(answer))
bitCount()

上面的一组数字生成:7 12 9 25 24 26 8 0 7 18 19 26

我做错了什么?我将如何解决这个问题?我无法想象它太复杂,但经过数小时的搜索、阅读和实验,我觉得我一定是遗漏了一些明显的东西。任何帮助将不胜感激。

【问题讨论】:

  • 你为什么要data[3:]?您只想从bin 表示中去除前两个 字符,而不是前三个。但实际上,为什么还要创建带有0bbin 表示只是为了实现它们?如果你想使用字符串,为什么不format(n, 'b')
  • 无论如何,无论你从((1 &lt;&lt; 32) + int(num)) &amp; -5) 得到的“C 位旋转技巧”列表,(a) 它在 Python 中都不起作用,因为 Python 具有任意长度的整数,而不是固定位-size ints,(b)你复制错了,(c)它可能不会比在 Python 中以明显的方式更快,即使是这样,两个版本对于紧凑的内部来说都太慢了循环,而且对于其他任何事情都足够快,所以谁在乎呢?

标签: python python-3.x bit-manipulation


【解决方案1】:

你的整个方法在 Python 中没有意义。

-17621 中的 1 位数,也就是 -100010011010101 二进制数,为 7。如果您期望 26,您不是在询问该数字中 1 的位数,而是在询问对于解释为 C 32 位无符号整数的数字的 C 32 位 2 的补码有符号整数表示中的 1 位数。如果你想在 Python 中这样做,你必须明确地要求它。例如,您可以使用num % 0x100000000

同时,无论您从 (1 &lt;&lt; 32) + num &amp; -5 中获得的所有位旋转技巧列表,假设 C int32 数学而不是实际的整数算术,所以它会再次出错。另外,我很确定你复制错了。另外,没有理由在 Python 中使用这些技巧 - 很可能它实际上会更慢,而不是更快,但无论哪种方式,它都会太慢,无法在紧密循环中执行无数次,并且速度快到可以做几次,所以这种挤出最后 5% 的优化在 Python 中比在 C 中更没有意义。(尽管许多这些旧技巧实际上在 C 中也会减慢速度,使用现代编译器和 CPU……)

通过再次执行[3:] 来取消最初的1 位假定您有一个32 位表示,这又是错误的。 (这就是为什么你所有的正数都偏离了 1 ——你正在淘汰前 1 位。)

所以,让我们简单点:

answer = []
for num in numbers:
    data = int(num) % 0x100000000
    bits = format(data, 'b')
    answer.append(str(bits.count('1')))
print(' '.join(answer))

请注意,我使用了format(data, 'b'),所以我不必在开头敲掉0b

或者,如果你想让它更紧凑:

print(' '.join(str(format(int(num)%0x100000000, 'b').count('1')) for num in numbers))

无论哪种方式,你都会得到:

8 12 10 26 25 27 8 0 8 18 20 28

...确实比您的预期输出多两个数字,但是您的输入也比您的预期输出多两个数字,所以我认为这是可以预期的。

【讨论】:

    【解决方案2】:

    要诱使 python 转换为二进制补码,您可以将 32 位 1(即 0xFFFFFFFF)的位掩码应用于 n。如果 n 为正,则结果为 n。但是,如果 n 为负数,则位掩码操作将返回一个正数,该正数的所有位都与负数 n 的二进制补码表示相同。因此,无论哪种情况,位数都是您所期望的。

    从那里您可以简单地计算“bin”函数产生的位数。

    [bin(n & 0xFFFFFFFF).count("1") for n in numbers]
    

    您的示例输入的结果是

    [8, 12, 10, 26, 25, 27, 8, 0, 8, 18, 20, 28]
    

    除了给你期望的答案之外,this other question 建议 bin(n).count 是找到整数汉明权重的最快方法

    【讨论】:

    • 您的解决方案还为我的示例输入返回了不正确的输出。
    • 您不同意哪个数字,为​​什么?
    • -17621 即 -0b100010011010101 如何得到 26?
    • 但我更喜欢你的回答 :) 紧凑而简单。
    • @AlfredRossi:如果你用 C 语言来思考,那么 26 是有意义的。 -17621 作为 C int32(在 2 的补码机器上)重新解释为 C uint32 是 4294949675,其中有 26 位。他的整个算法都基于 C int32 术语的思考,而不仅仅是他的预期结果。
    【解决方案3】:

    试试这个:

    #!/usr/bin/env python                                                           
    
    
    def bit_counter(number):                                                        
    
        suffix = 2                                                                  
        if number < 0:                                                              
            suffix = 3                                                              
    
        bit = bin(number)                                                           
        counter = 0                                                                 
        for i in bit[suffix:]:                                                      
            if int(i) == 1:                                                         
                counter += 1                                                        
    
        return counter                                                              
    
    
    def main():                                                                     
        a = 13676                                                                   
        print bit_counter(a)                                                        
        a = -13676                                                                  
        print bit_counter(a)                                                        
    
    
    if __name__ == "__main__":                                                      
        main()          
    

    它适用于负数和非负数。

    【讨论】:

      【解决方案4】:

      整数中设置为 1 的位数称为其汉明权重种群计数

      有几种快速算法可以执行这样的计算。查看以下来源:

      1. Bit array维基百科上的文章
      2. Bit Twiddling Hack
      3. 回复Stackoverflow question #109023
      4. Bit Manipulation page 在 Python Wiki 上。
      5. The Aggregate Magic Algorithms(针对 32 位整数进行了优化)。

      我首选的通用算法是:

      def bit_count(n):
          """Return the number of bits set to 1 in the integer number 'n'.
             This is called the Hamming weight or the population count of 'n'.
             The algorithm performs as many iterations as there are set bits.
             References:
               The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988.
               Peter Wegner in CACM 3 (1960), 322.
          """
          assert n >= 0, 'Argument of bit_count() must be non-negative'
          count = 0
          while n:
              n &= n - 1
              count += 1
          return count
      

      对于 32 位非负整数,以下函数更快:

      def bit_count_parallel_32(n):
          """Return the number of set bits (1) present in the integer number 'n'.
             This algorithm accepts only 32-bit non-negative integer numbers.
          """
          assert 0 <= n < 2**32, ('Argument of bit_count_parallel_32() must be '
              'non-negative and less than %d (2**32)') % 2**32
          n = n - ((n >> 1) & 0x55555555)
          n = (n & 0x33333333) + ((n >> 2) & 0x33333333)
          return (((n + (n >> 4) & 0x0F0F0F0F) * 0x01010101) & 0xffffffff) >> (8 + 16)
      

      如果有人需要,这里是 64 位非负整数的版本:

      def bit_count_parallel_64(n):
          """Return the number of set bits (1) present in the integer number 'n'.
             This algorithm accepts only 64-bit non-negative integer numbers.
          """
          assert 0 <= n < 2**64, ('Argument of bit_count_parallel_64() must be '
              'non-negative and less than %d (2**64)') % 2**64
          n = n - ((n >> 1) & 0x5555555555555555)
          n = (n & 0x3333333333333333) + ((n >> 2) & 0x3333333333333333)
          return (((n + (n >> 4) & 0x0F0F0F0F0F0F0F0F) * 0x0101010101010101) \
              & 0xffffffffffffffff) >> (8 + 16 + 32)
      

      但是请注意,所有这些算法都不适用于负整数。如果需要使用负数,可以使用前面的函数来统计负数的2补码表示中设置的位数,例如:

      >>> bit_count(-3 & 0xFF)
      7
      

      还可以访问专门介绍伟大书籍的网站Hacker's Delight (Wayback Machine)。

      【讨论】:

        【解决方案5】:

        我认为你做的比你需要的要困难得多。我不明白bin((1 &lt;&lt; 32) + int(num) &amp; -5) 应该做什么。正如您所注意到的,bin(number) 返回数字的二进制表示。所以现在你所要做的就是数一数。所以这会起作用:

        for num in numbers:
            data = bin(num)
            bitCount = 0
            for char in data:
                if char == '1':
                    bitCount += 1
        

        【讨论】:

          【解决方案6】:

          最简单的方法是使用gmpy

          import gmpy
          [gmpy.popcount(n & 0xFFFFFFFF) for n in numbers]
          #>>> [8, 12, 10, 26, 25, 27, 8, 0, 8, 18, 20, 28]
          

          与字符串操作相比,这也可能相当快。

          另一种方法是使用 Numpy:

          import numpy
          
          def count_bits_u32(u32):
            u32 = (u32 & 0x55555555) + ((u32 & 0xAAAAAAAA) >> 1)
            u32 = (u32 & 0x33333333) + ((u32 & 0xCCCCCCCC) >> 2)
            u32 = (u32 & 0x0F0F0F0F) + ((u32 & 0xF0F0F0F0) >> 4)
            u32 = (u32 & 0x00FF00FF) + ((u32 & 0xFF00FF00) >> 8)
            u32 = (u32 & 0x0000FFFF) + ((u32 & 0xFFFF0000) >> 16)
            return u32
          
          count_bits_u32(numpy.array(numbers, dtype="uint32"))
          #>>> array([ 8, 12, 10, 26, 25, 27,  8,  0,  8, 18, 20, 28], dtype=uint32)
          

          这对于大型值列表可能会更有效,因为它会打包整数并使用矢量化操作。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-07-14
            • 1970-01-01
            • 1970-01-01
            • 2014-01-11
            • 2021-01-05
            • 1970-01-01
            • 2013-09-18
            相关资源
            最近更新 更多