如何在python中快速将int转换为包含二进制表示的列表?

Fra*_*ale 1 python binary optimization performance python-3.x

目前我有以下版本,但它是我的主要瓶颈,而且速度很慢.

def intToBinary(Input):
    bStrInput = format(Input, "016b")
    bStrInput = list(bStrInput)
    bInput = list(map(int, bStrInput))
    return bInput
Run Code Online (Sandbox Code Playgroud)

任何想法如何加快这段代码?

我在Tensorflow项目中使用它,用于整数的热编码转换.该函数采用2字节整数(在[0,65536)范围内)并输出值为0和1的整数列表:

>>> intToBinary(50411)
[1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1]
Run Code Online (Sandbox Code Playgroud)

结果传递给张量tInput = torch.tensor(bInput, dtype=torch.uint8).

Mar*_*ers 6

你的版本略有改进

您的版本可以通过不使用中间变量和转换到列表来避免一些操作码:

def intToBinary(Input):
    return list(map(int, format(Input, "016b")))
Run Code Online (Sandbox Code Playgroud)

纯Python仍然是比特换档选项

但是,您可以通过不再转换为字符串然后再转换为整数来加快速度.如果您只需要位,那么使用位操作:

def int_to_binary(v):
    return [(v >> i) & 1 for i in range(15, -1, -1)]
Run Code Online (Sandbox Code Playgroud)

这将输入整数的位向右移动15,14等步骤,然后屏蔽掉移位的整数,1以便每次获得最右位的位值.

速度比较,使用1000个随机整数将方差降低到可接受的水平:

>>> import sys, platform, psutil
>>> sys.version_info
sys.version_info(major=3, minor=7, micro=0, releaselevel='final', serial=0)
>>> platform.platform(), psutil.cpu_freq().current / 1000, psutil.cpu_count(), psutil.virtual_memory().total // (1024 ** 3)
('Darwin-17.7.0-x86_64-i386-64bit', 2.9, 8, 16)
>>> from timeit import Timer
>>> from random import randrange
>>> testvalues = [randrange(2**16) for _ in range(1000)]
>>> count, total = Timer("for i in tv: t(i)", "from __main__ import intToBinary as t, testvalues as tv").autorange()
>>> (total / count) * (10 ** 3)   # milliseconds
3.2812212200224167
>>> count, total = Timer("for i in tv: t(i)", "from __main__ import int_to_binary as t, testvalues as tv").autorange()
>>> (total / count) * (10 ** 3)   # milliseconds
2.2861225200176705
Run Code Online (Sandbox Code Playgroud)

因此int_to_binary(),生成1000个结果的速度大约是1.5倍,大约2.3毫秒,而优化的字符串操作版本则只有3.3个.

基本循环和函数调用在我的机器上需要7.4微秒:

>>> count, total = Timer("for i in tv: pass", "from __main__ import testvalues as tv; t = lambda i: None").autorange()
>>> (total / count) * (10 ** 3)
0.007374252940062434
Run Code Online (Sandbox Code Playgroud)

因此,对于位操作版本,每次调用的基本时序约为3.27微秒,而2.28微秒.

Numpy能做些什么

如果您正在使用Tensorflow,您还可以使用numpy操作,可以使用该numpy.unpackbits()函数将uint8转换为二进制; uint16需要首先被'查看'为uint8:

import numpy as np

def int_to_bits_np(v):
    return np.unpackbits(np.array([v], dtype=np.uint16).view(np.uint8)).tolist()
Run Code Online (Sandbox Code Playgroud)

这会转换为numpy数组,再次返回到Python整数列表,因此只对一个值有效:

>>> count, total = Timer("for i in tv: t(i)", "from __main__ import int_to_bits_np as t, testvalues as tv").autorange()
>>> (total / count) * (10 ** 3)
2.654717969999183
Run Code Online (Sandbox Code Playgroud)

比你的版本更快,比bithifting慢.

Numpy矢量化选项

可能不希望转换回列表,因为numpy数组已经为您的张量提供了正确的dtype.您还可以在大量值上使用它; 比如输入中的整数1000个整数:

def int_to_bits_array(varray):
    """Convert an array of uint16 values to binary"""
    return np.unpackbits(varray.reshape(varray.shape[0], 1).view(np.uint8), axis=1)
Run Code Online (Sandbox Code Playgroud)

这是方式,方式,方式更快:

>>> testvalues_array = np.array(testvalues, dtype=np.uint16)
>>> int_to_bits_array(testvalues_array)
array([[1, 1, 0, ..., 1, 1, 0],
       [0, 1, 1, ..., 1, 0, 0],
       [1, 1, 1, ..., 0, 0, 0],
       ...,
       [1, 1, 1, ..., 0, 1, 0],
       [0, 0, 0, ..., 1, 1, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)
>>> count, total = Timer("t(tva)", "from __main__ import int_to_bits_array as t, testvalues_array as tva").autorange()
>>> (total / count) * (10 ** 3)  # milliseconds
0.007919690339913358
>>> (total / count) * (10 ** 6)  # microseconds
7.919690339913359
Run Code Online (Sandbox Code Playgroud)

是的,这是1000个值一步转换为二进制,处理所有值在8微秒.这可以线性扩大到更大的数字; 在8毫秒内转换了100万个随机值:

>>> million_testvalues_array = np.random.randint(2 ** 16, size=10 ** 6, dtype=np.uint16)
>>> count, total = Timer("t(tva)", "from __main__ import int_to_bits_array as t, million_testvalues_array as tva").autorange()
>>> (total / count) * (10 ** 3)  # milliseconds
7.9162722200271665
Run Code Online (Sandbox Code Playgroud)