Python 求两个阵列重叠的算法
我的代码中有一个瓶颈,我正在努力解决 取一个数组A,大小为nxm,只包含1和0。我需要一个算法,该算法需要两行A的所有组合,并计算它们之间的重叠 更具体地说,我需要一个比以下算法更快的替代方案:Python 求两个阵列重叠的算法,python,algorithm,numpy,Python,Algorithm,Numpy,我的代码中有一个瓶颈,我正在努力解决 取一个数组A,大小为nxm,只包含1和0。我需要一个算法,该算法需要两行A的所有组合,并计算它们之间的重叠 更具体地说,我需要一个比以下算法更快的替代方案: for i in range(A.shape[0]): for j in range(A.shape[0]): a=b=c=d=0 for k in range(A.shape[1]): if A[i][k]==1 and A[j][k]==1: a+=1
for i in range(A.shape[0]):
for j in range(A.shape[0]):
a=b=c=d=0
for k in range(A.shape[1]):
if A[i][k]==1 and A[j][k]==1:
a+=1
if A[i][k]==0 and A[j][k]==0:
b+=1
if A[i][k]==1 and A[j][k]==0:
c+=1
if A[i][k]==0 and A[j][k]==1:
d+=1
print(a,b,c,d)
谢谢你的回复 由于
a、b、c、d
都在循环中,因此我假设您希望每个I、j都有它们。我将用[I,j]
中的元素为它们创建一个矩阵,该元素是循环中a,b,c,d
的对应值,不带任何循环。例如a[i,j]
是循环i,j
中a
的值:
A_c = 1-A
a = np.dot(A, A.T)
b = np.dot(A_c, A.T)
c = np.dot(A, A_c.T)
d = np.dot(A_c, A_c.T)
如果您更关心速度,您可以分解和缩短/重用上面等式中的一些计算 由于a、b、c、d
都在循环中,因此我假设您希望每个I、j都有它们。我将用[I,j]
中的元素为它们创建一个矩阵,该元素是循环中a,b,c,d
的对应值,不带任何循环。例如a[i,j]
是循环i,j
中a
的值:
A_c = 1-A
a = np.dot(A, A.T)
b = np.dot(A_c, A.T)
c = np.dot(A, A_c.T)
d = np.dot(A_c, A_c.T)
如果您更关心速度,您可以分解和缩短/重用上面等式中的一些计算 虽然上面的答案绝对正确,但我想继续回答一个更技术性的问题——主要是因为我做了一些与上周你问题中的问题非常相似的事情,并在这一过程中学到了一些很酷的东西
首先,是的,矩阵乘法和向量化是正确的方法。然而,当矩阵变大时,这些可能会变得有点昂贵。让我展示一个N=100和M=100的小基准:
N,M = 100,100
A = np.random.randint(2,size=(N,M))
def type1():
A_c = 1-A
a = np.dot(A, A.T)
b = np.dot(A_c, A.T)
c = np.dot(A, A_c.T)
d = np.dot(A_c, A_c.T)
return a,b,c,d
%timeit -n 100 type1()
>>>3.76 ms ± 48.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
一个简单的加速可以通过以下事实来实现:a+b+c+d=M
。我们实际上不需要找到d;因此,我们可以减少一个昂贵的点产品在这里
def type2():
A_c = 1-A
a = np.dot(A, A.T)
b = np.dot(A_c, A.T)
c = np.dot(A, A_c.T)
return a,b,c,M-(a+b+c)
%timeit -n 100 type2()
>>>2.81 ms ± 15.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
这几乎缩短了一毫秒,但我们可以做得更好。Numpy数组有两种顺序:C-连续
和F-连续
。您可以通过打印A.flags
来检查这一点;默认情况下,A是C连续数组。然而,它的转置A.T表示为一个F-连续数组,当我们将它们传递给dot时,会为A.T创建一个内部副本,因为顺序不匹配
绕过这个问题的一种方法是转到scipy并将我们的程序与BLAS()连接起来,特别是通用矩阵乘法gemm
例程
from scipy.linalg import blas as B
def type3():
A_c = 1-A
a = B.dgemm(alpha=1.0, a=A, b=A, trans_b=True)
b = B.dgemm(alpha=1.0, a=A_c, b=A, trans_b=True)
c = B.dgemm(alpha=1.0, a=A, b=A_c, trans_b=True)
return a,b,c,M-(a+b+c)
%timeit -n 100 type3()
>>>449 µs ± 27 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
时间从毫秒直接下降到了微秒,这真是太棒了。虽然上面的答案是绝对正确的,但我想跟进一个更技术性的答案——主要是因为我做了一些与上周你问题中的问题非常相似的事情,一路上还学到了一些很酷的东西
首先,是的,矩阵乘法和向量化是正确的方法。然而,当矩阵变大时,这些可能会变得有点昂贵。让我展示一个N=100和M=100的小基准:
N,M = 100,100
A = np.random.randint(2,size=(N,M))
def type1():
A_c = 1-A
a = np.dot(A, A.T)
b = np.dot(A_c, A.T)
c = np.dot(A, A_c.T)
d = np.dot(A_c, A_c.T)
return a,b,c,d
%timeit -n 100 type1()
>>>3.76 ms ± 48.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
一个简单的加速可以通过以下事实来实现:a+b+c+d=M
。我们实际上不需要找到d;因此,我们可以减少一个昂贵的点产品在这里
def type2():
A_c = 1-A
a = np.dot(A, A.T)
b = np.dot(A_c, A.T)
c = np.dot(A, A_c.T)
return a,b,c,M-(a+b+c)
%timeit -n 100 type2()
>>>2.81 ms ± 15.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
这几乎缩短了一毫秒,但我们可以做得更好。Numpy数组有两种顺序:C-连续
和F-连续
。您可以通过打印A.flags
来检查这一点;默认情况下,A是C连续数组。然而,它的转置A.T表示为一个F-连续数组,当我们将它们传递给dot时,会为A.T创建一个内部副本,因为顺序不匹配
绕过这个问题的一种方法是转到scipy并将我们的程序与BLAS()连接起来,特别是通用矩阵乘法gemm
例程
from scipy.linalg import blas as B
def type3():
A_c = 1-A
a = B.dgemm(alpha=1.0, a=A, b=A, trans_b=True)
b = B.dgemm(alpha=1.0, a=A_c, b=A, trans_b=True)
c = B.dgemm(alpha=1.0, a=A, b=A_c, trans_b=True)
return a,b,c,M-(a+b+c)
%timeit -n 100 type3()
>>>449 µs ± 27 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
时间从毫秒直接下降到了微秒,这真是太棒了。仅仅是为了它的运动,这里有一个比当前最快的速度快三倍的方法。我们利用了float上矩阵计算更快的优势,特别是float32。此外,我们只进行一次矩阵乘法,通过更便宜的方法推断其他数字:
def pp():
A1 = np.count_nonzero(A,1)
Af = A.astype('f4')
a = Af@Af.T
b = A1-a
c = b.T
d = M-a-b-c
return a,b,c,d
[*map(np.array_equal,pp(),type3())]
# [True, True, True, True]
timeit(pp,number=1000)
# 0.14910832402529195
timeit(type3,number=1000)
# 0.4432948770117946
仅就it运动而言,这里有一种比当前最快的速度快三倍的方法。我们利用了float上矩阵计算更快的优势,特别是float32。此外,我们只进行一次矩阵乘法,通过更便宜的方法推断其他数字:
def pp():
A1 = np.count_nonzero(A,1)
Af = A.astype('f4')
a = Af@Af.T
b = A1-a
c = b.T
d = M-a-b-c
return a,b,c,d
[*map(np.array_equal,pp(),type3())]
# [True, True, True, True]
timeit(pp,number=1000)
# 0.14910832402529195
timeit(type3,number=1000)
# 0.4432948770117946
如前所述,如果A
是一个列表列表(例如A.tolist()
),这将更快A[j][k]
使用数组进行索引的成本很高(与列表相比);尽量避免重复这样做。或者,尝试对A:…`中的行
而不是范围
,并重复A[i]
。等等。你能举例说明“重叠”是什么意思吗?代码准确地解释了我的意思。很难用语言来表达。假设[1,0,0,1]和[0,1,0,1]是两行。然后沿着零轴将它们粘在一起,得到[[1,0,0,1],[0,1,0,1]]。然后我想计算不同列的数量,即(1,1)、(0,0)、(1,0)和(0,1)的数量。numpy
中的速度通常来自“矢量化”,也就是说,将python级别的迭代移动到编译的numpy方法中。这就是尽可能使用整个阵列构建块。在本例中,我将关注k
循环。我还没有试着弄清楚它在做什么,所以无法直接帮助。但是试着想想测试两行的方法,而不需要迭代。类似于row1==row2
和布尔测试,如rows1 | row2
等。如果A
是一个列表列表(例如A.tolist()
),那么编写这样的测试会更快A[j][k]
使用数组进行索引非常昂贵(与列表相比)