我创建了一个小函数,它以整数length
作为输入,并返回范围[0:2**length-1]
中所有2**length
整数的二进制表示形式的numpy
array
。
import numpy as np
def get_bitstrings(length):
# We need to binary-fy 2^length numbers.
iterations = 2**length
# Pre-allocate memory.
the_array = np.zeros((iterations, length))
# Go through all decimals in the range [0:iterations-1]
for num in range(iterations):
# Get binary representation in string format with 'length' zeroes padded
t_string = '{f_num:0{f_width}b}'.format(f_num=num, f_width=length)
# Convert to a Python list
t_list = list(t_string)
# Convert to Numpy array and store.
the_array[num,:] = np.array(t_list)
return the_array
if __name__ == '__main__':
var1 = get_bitstrings(2)
var2 = get_bitstrings(3)
print('var1:n{}n'.format(var1))
print('var2:n{}n'.format(var2))
产生:
var1:
[[ 0. 0.]
[ 0. 1.]
[ 1. 0.]
[ 1. 1.]]
var2:
[[ 0. 0. 0.]
[ 0. 0. 1.]
[ 0. 1. 0.]
[ 0. 1. 1.]
[ 1. 0. 0.]
[ 1. 0. 1.]
[ 1. 1. 0.]
[ 1. 1. 1.]]
该过程包括将每个整数的二进制表示形式作为字符串(在其前面填充0,使长度在length
不变),将该字符串转换为Python列表,然后将该列表转换为numpy
array
。
我发现这是满足每个比特都是数组中的一个条目这一要求的唯一方法——即,比特串1010
是1x4
numpy
array
,而不仅仅是1x1
数组中的整数。但我确信还有更好的选择,因此产生了这个问题。
正如你所能想象的,问题是这是低效的。我想知道我是否可以通过使用Python/Numpy技巧来改进这一点。
编辑:我曾经在MATLAB中用这个片段来做这件事:
t_length=5;dc=[0:2^t_length-1]';bc=rem(地板(dc*pow2(-(t_length-1):0)),2);
但当谈到Python/Numpy时,我完全是个傻瓜!也许这会激励一些人。:-)
您可以使用NumPy的广播和矢量化操作来相当有效地做到这一点:
>>> from numpy import arange, newaxis
>>> powers_of_two = 2**arange(4)[::-1]
>>> (arange(2**4)[:, newaxis] & powers_of_two) / powers_of_two
array([[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 0, 1, 1],
[0, 1, 0, 0],
[0, 1, 0, 1],
[0, 1, 1, 0],
[0, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 1],
[1, 0, 1, 0],
[1, 0, 1, 1],
[1, 1, 0, 0],
[1, 1, 0, 1],
[1, 1, 1, 0],
[1, 1, 1, 1]])
简单解释:我们取0到15的所有整数(arange(2**4)
),然后将其整形为给定形状为(16, 1)
的数组(即[:, newaxis]
切片部分)。然后我们取从最高到最低(2**arange(4)[::-1]
)的2次方的逐位和。整形确保了按位和运算作为一种"外部"运算执行:我们将原始arange
的每个元素的按位和与powers_of_two
数组的每个元素进行比较。这是NumPy的广播和切片工作。缺少显式Python级别的for
循环应该比基于for
循环或列表理解的解决方案快得多。
这是一个更时尚、更快速的替代方案:
>>> from numpy import arange, newaxis
>>> arange(2**4)[:,newaxis] >> arange(4)[::-1] & 1
array([[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 0, 1, 1],
[0, 1, 0, 0],
[0, 1, 0, 1],
[0, 1, 1, 0],
[0, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 1],
[1, 0, 1, 0],
[1, 0, 1, 1],
[1, 1, 0, 0],
[1, 1, 0, 1],
[1, 1, 1, 0],
[1, 1, 1, 1]])
和往常一样,如果效率是一个问题,那么您应该充分利用Python以timeit和概要文件模块的形式提供的工具。我的length=16
机器上的计时似乎表明第二种变体比第一种变体快得多:
taniyama:~ mdickinson$ python -m timeit -s "from numpy import arange, newaxis" "arange(1<<16)[:, newaxis] >> arange(16)[::-1] & 1"
100 loops, best of 3: 4.08 msec per loop
taniyama:~ mdickinson$ python -m timeit -s "from numpy import arange, newaxis" "(arange(1<<16)[:, newaxis] & 2**arange(16)[::-1]) / 2**arange(16)[::-1]"
10 loops, best of 3: 21.6 msec per loop
一种方法是使用numpy.binary_repr
。它将产生一个字符串,但您可以很容易地将其转换为int或float数组(只需更改dtype
参数)。例如:
import numpy as np
k = 4
print np.array([list(np.binary_repr(x, k)) for x in range(2**k)], dtype=int)
这产生:
[[0 0 0 0]
[0 0 0 1]
[0 0 1 0]
[0 0 1 1]
[0 1 0 0]
[0 1 0 1]
[0 1 1 0]
[0 1 1 1]
[1 0 0 0]
[1 0 0 1]
[1 0 1 0]
[1 0 1 1]
[1 1 0 0]
[1 1 0 1]
[1 1 1 0]
[1 1 1 1]]
或者,如果你想要一个可读性更强的版本:
def bitstrings(k):
binary = [np.binary_repr(item, width=k) for item in range(2**k)]
return np.array([list(item) for item in binary], dtype=int)