【问题标题】:Bits list to integer in PythonPython中的位列表到整数
【发布时间】:2012-09-09 19:02:29
【问题描述】:

我在 Python 中有这样的列表:[1,0,0,0,0,0,0,0]。我可以像输入 0b10000000 那样将其转换为整数(即​​转换为 128)吗? 我还需要将[1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0] 之类的序列转换为整数(这里它将返回 0b1100000010000000,即 259)。 如果需要,列表的长度始终是 8 的倍数。

【问题讨论】:

  • 这可能对你有帮助:stackoverflow.com/questions/2576712/…
  • 这些列表来自哪里?它们最初是字符输入吗?
  • 它们是从整数计算的。如果你愿意,我可以在这里发布代码。
  • @ghostmansd:我只是想确保它们不是最初的角色。那会改变哪个答案最快。
  • @ghostmansd:您的位列表的平均长度是多少?在 64 位时,字符串版本的性能最好(在我的电脑上)。

标签: python arrays list bit-manipulation


【解决方案1】:

@Akavall 建议的最简单的方法是最快的。下面针对 mult_add_xor 的额外时序表明,python 中的位操作较慢,因为简单的加法“+ bit”比 xor “| bit”快,并且乘以 2 比移位“

import timeit

bit_list = [1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]


def mult_and_add(bit_list):
    output = 0
    for bit in bit_list:
        output = output * 2 + bit
    return output


def mult_and_xor(bit_list):
    output = 0
    for bit in bit_list:
        output = output * 2 | bit
    return output


def shifting(bitlist):
    out = 0
    for bit in bitlist:
        out = (out << 1) | bit
    return out


n = 1000000

a1 = mult_and_add(bit_list)
a2 = mult_and_xor(bit_list)
a3 = shifting(bit_list)

print('a1: ', a1, ' a2: ', a2, ' a3: ', a3)
assert a1 == a2 == a3

t = timeit.timeit('convert(bit_list)',
                  'from __main__ import mult_and_add as convert, bit_list',
                  number=n)
print("mult and add method time is : {} ".format(t))
t = timeit.timeit('convert(bit_list)',
                  'from __main__ import mult_and_xor as convert, bit_list',
                  number=n)
print("mult and xor method time is : {} ".format(t))
t = timeit.timeit('convert(bit_list)',
                  'from __main__ import shifting as convert, bit_list',
                  number=n)
print("shifting method time is : {} ".format(t))

输出:

a1:  49280  a2:  49280  a3:  49280
mult and add method time is : 0.9469406669959426 
mult and xor method time is : 1.0905388034880161 
shifting method time is : 1.2844801126047969 

【讨论】:

    【解决方案2】:

    这个怎么样:

    out = sum([b<<i for i, b in enumerate(my_list)])
    

    或以相反的顺序:

    out = sum([b<<i for i, b in enumerate(my_list[::-1])])
    

    【讨论】:

      【解决方案3】:

      我遇到了一种稍微优于 Martijn Pieters 解决方案的方法,尽管他的解决方案当然更漂亮。我其实对结果有点惊讶,但无论如何......

      import timeit
      
      bit_list = [1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0]
      
      def mult_and_add(bit_list):
          output = 0
          for bit in bit_list:
              output = output * 2 + bit
          return output
      
      def shifting(bitlist):
           out = 0
           for bit in bitlist:
               out = (out << 1) | bit
           return out
      
      n = 1000000
      
      t1 = timeit.timeit('convert(bit_list)', 'from __main__ import mult_and_add as convert, bit_list', number=n)
      print "mult and add method time is : {} ".format(t1)
      t2 = timeit.timeit('convert(bit_list)', 'from __main__ import shifting as convert, bit_list', number=n)
      print "shifting method time is : {} ".format(t2)
      

      结果:

      mult and add method time is : 1.69138722958 
      shifting method time is : 1.94066818592 
      

      【讨论】:

      • 有点令人惊讶的结果,是的。我猜想乘以 2 完全 与移位 1 相同,但这可能是因为 OR 操作比 XOR 操作慢。在您的情况下,加法可以简化为 XOR,因为由于位移,您不会有进位。
      • 知道为什么会这样会很有趣。显然,在处理器硬件层面,移位模式肯定是正确的,并且几十年来众所周知。然而,可能的特殊代码优化不适用于移位,并且适用于乘法和加法,这允许处理器预执行和分支预测更好地工作,或者展开循环或执行某些操作。有一个解释,部分意味着python对or和移位的按位运算支持不是很好。
      • 哦,顺便说一句,在 Python 3.7.7 中,这仍然是真的...Python 3.7.7 (default, Mar 23 2020, 23:19:08) [MSC v.1916 64 bit (AMD64)] IPython 7.13.0 -- An enhanced Interactive Python. %timeit mult_and_add(bit_list) 957 ns ± 3.76 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) %timeit shifting(bit_list) 1.33 µs ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
      【解决方案4】:

      试试这个单行:

      int("".join(str(i) for i in my_list), 2)
      

      如果您关心速度/效率,请查看 Martijn Pieters 的解决方案。

      【讨论】:

      • 这是一个更快的字符串版本:int(''.join('01'[i] for i in bitlist), 2)。仍然不如位移快。
      • @ghostmansd。在 64 位时,字符串版本开始执行得更快(在我的电脑上)。
      【解决方案5】:

      你可以使用位移:

      out = 0
      for bit in bitlist:
          out = (out << 1) | bit
      

      这很容易胜过 A. R. S. 提出的“int cast”方法,或 Steven Rumbalski 提出的带有查找的修改后的转换:

      >>> def intcaststr(bitlist):
      ...     return int("".join(str(i) for i in bitlist), 2)
      ... 
      >>> def intcastlookup(bitlist):
      ...     return int(''.join('01'[i] for i in bitlist), 2)
      ... 
      >>> def shifting(bitlist):
      ...     out = 0
      ...     for bit in bitlist:
      ...         out = (out << 1) | bit
      ...     return out
      ... 
      >>> timeit.timeit('convert([1,0,0,0,0,0,0,0])', 'from __main__ import intcaststr as convert', number=100000)
      0.5659139156341553
      >>> timeit.timeit('convert([1,0,0,0,0,0,0,0])', 'from __main__ import intcastlookup as convert', number=100000)
      0.4642159938812256
      >>> timeit.timeit('convert([1,0,0,0,0,0,0,0])', 'from __main__ import shifting as convert', number=100000)
      0.1406559944152832
      

      【讨论】:

      • 顺便说一句,您能否建议将整数从 0b10000000 转换为 0b000001 而无需创建位列表的方法?
      • @ghostmansd:使用.format() 字符串格式:'0b{0:08b}'.format(integer) 将整数格式化为一个以 0 填充的 8 字符字符串,并以 0b 开头。
      • 是的,我立即想到了“int cast”方法(如果您不太关心效率,这似乎是一个不错的干净解决方案)。但是,是的,这显然更快。
      • 这是一个更快的字符串版本:int(''.join('01'[i] for i in bitlist), 2)。仍然不如位移快。
      • @StevenRumbalski:将其添加到计时列表中。
      【解决方案6】:

      ...或使用bitstring 模块

      >>> from bitstring import BitArray
      >>> bitlist=[1,0,0,0,0,0,0,0]
      >>> b = BitArray(bitlist)
      >>> b.uint
      128
      

      【讨论】:

      • 我以前可以使用bitstringbitarray 模块,但这次我不能。我需要一个不需要额外软件包的解决方案。不过,谢谢!
      • 你没有在问题中说明这一点,所以我想我会添加一个位串解决方案作为 Martijns 出色答案的替代方案:-)
      猜你喜欢
      • 2017-08-12
      • 1970-01-01
      • 1970-01-01
      • 2012-05-06
      • 2020-08-29
      • 1970-01-01
      • 2018-10-09
      • 1970-01-01
      • 2010-12-27
      相关资源
      最近更新 更多