我在两个相同长度的大二进制序列之间执行按位'&'时遇到问题,我需要找到出现1的索引。
我用numpy做到了,这是我的代码:
>>> c = numpy.array([[0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1],[0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]]) #initialize 2d array
>>> c = c.all(axis=0)
>>> d = numpy.where(c)[False] #returns indices
我检查了时间。
>>> print("Time taken to perform 'numpy.all' : ",timeit.timeit(lambda :c.all(axis=0),number=10000))
>>> Time taken to perform 'numpy.all' : 0.01454929300234653
此操作比我预期的要慢。
然后,作为比较,我执行了一个基本的按位'&'操作:
>>> print("Time taken to perform bitwise & :",timeit.timeit('a = 0b0000000001111111111100000001111111111; b = 0b0000000001111111111100000001111111111; c = a&b',number=10000))
>>> Time taken to perform bitwise & : 0.0004252859980624635
这比numpy快
我使用numpy是因为它允许在具有1的位置查找索引,但是numpy.all运算符要慢得多。
与第一种情况一样,我的原始数据将是数组列表。如果我将此列表转换为二进制数,然后像第二种情况一样执行计算,会不会有任何用途?
答案 0 :(得分:1)
我认为您无法超越a&b
的速度(实际计算只是一堆基本的CPU操作,我很确定您的timeit
的结果> 99%高架)。例如:
>>> from timeit import timeit
>>> import numpy as np
>>> import random
>>>
>>> k = 2**17-2
>>> a = random.randint(0, 2**k-1) + 2**k
>>> b = random.randint(0, 2**k-1) + 2**k
>>> timeit('a & b', globals=globals())
2.520026927930303
这是> 100k位,只需要约2.5 us。
无论如何,&
的成本将与生成索引列表或索引数组的成本相形见。
numpy
本身就具有大量开销,因此对于像您这样的简单操作,需要检查它是否值得。
所以让我们先尝试一个纯python解决方案:
>>> c = a & b
>>> timeit("[x for x, y in enumerate(bin(c), -2) if y=='1']", globals=globals(), number=1000)
7.905808186973445
大约8毫秒,并且比&
操作要高几个数量级。
numpy怎么样?
让我们首先移动列表理解:
>>> timeit("np.where(np.fromstring(bin(c), np.uint8)[2:] - ord('0'))[0]", globals=globals(), number=1000)
1.0363857130287215
因此,在这种情况下,我们的速度提高了约8倍。如果我们要求结果为列表,则缩小到约4倍:
>>> timeit("np.where(np.fromstring(bin(c), np.uint8)[2:] - ord('0'))[0].tolist()", globals=globals(), number=1000)
1.9008758360287175
我们还可以让numpy进行二进制转换,这会带来另一个小的加速:
>>> timeit("np.where(np.unpackbits(np.frombuffer(c.to_bytes(k//8+1, 'big'), np.uint8))[1:])[0]", globals=globals(), number=1000)
0.869781385990791
总结:
&
留给纯Python 请注意,所有这些都带有警告,我的纯Python代码不一定是最佳的。例如,使用查找表,我们可以更快一些:
>>> lookup = [(np.where(i)[0]-1).tolist() for i in np.ndindex(*8*[2])]
>>> timeit("[(x<<3) + z for x, y in enumerate(c.to_bytes(k//8+1, 'big')) for z in lookup[y]]", globals=globals(), number=1000)
4.687953414046206
答案 1 :(得分:0)
>>> c = numpy.random.randint(2, size=(2, 40)) #initialize
>>> c
array([[1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0],
[1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1]])
访问此选项会给您两个速度下降:
and
操作,其中可能包括从完整整数到布尔值的转换。您严重阻碍了all
测试;结果就不足为奇了。
答案 2 :(得分:0)
观察到的因素是以下事实的直接结果:c=numpy.array([[0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1],[0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1]])
是int
上的数组,而int
是以32位编码的
因此,当您使用c.all()时,您正在对37 * 32 = 1184位进行操作
但是a = 0b0000000001111111111100000001111111111
由37位组成,因此当您进行a&b
时,操作是在37位上。
因此,使用numpy数组的成本要高32倍。
让我们测试一下
import timeit
import numpy as np
print("Time taken to perform bitwise & :",timeit.timeit('a=0b0000000001111111111100000001111111111; b = 0b0000000001111111111100000001111111111; c = a&b',number=320000))
a = 0b0000000001111111111100000001111111111
b = 0b0000000001111111111100000001111111111
c=np.array([a,b])
print("Time taken to perform 'numpy.all' : ",timeit.timeit(lambda :c.all(axis=0),number=10000))
我执行&
操作320000次,all()
操作10000次。
Time taken to perform bitwise & : 0.01527938833025152
Time taken to perform 'numpy.all' : 0.01583387375572265
是同一回事!
现在回到您最初的问题,您想知道大二进制数中位为1的索引。
也许您可以尝试bitarray模块提供的功能
a = bitarray.bitarray('0000000001111111111100000001111111111')
b = bitarray.bitarray('0000000001111111111100000001111111111')
i=0
data = list()
for c in a&b:
if(c):
data.append(i)
i=i+1
print (data)
输出
[9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36]