我有两个NxN矩阵,我想将它们相乘:A和B.在NumPy中,我用过:
import numpy as np
C = np.dot(A, B)
然而,我碰巧知道,对于矩阵B,只有行n和列n是非零的(这直接来自产生矩阵的分析公式,并且毫无疑问总是如此)。
希望利用这一事实并减少产生C所需的乘法次数,我将上述内容替换为:
import numpy as np
for row in range(0, N):
for col in range(0, N):
if col != n:
C[row, col] = A[row, n]*B[n, col] #Just one scalar multiplication
else:
C[row, col] = np.dot(A[row], B[:, n])
从分析上看,这应该降低总复杂度如下:在一般情况下(不使用任何奇特的技巧,只是基本矩阵乘法)C = AB,其中A和B都是NxN,应该是O(N ^ 3) 。也就是说,所有N行必须乘以所有N列,并且这些点积中的每一个包含N次乘法=> O(N N N)= O(N ^ 3)。#
然而,如上所述,利用B的结构应当为O(N ^ 2 + N ^ 2)= O(2N ^ 2)= O(N ^ 2)。也就是说,所有N行必须乘以所有N列,但是,对于所有这些行(除了那些涉及'B [:,n]'的那些),只需要一个标量乘法:只有一个'B [:,m]'的元素对于m!= n,它不为零。当n == m时,将发生N次(对于必须乘以B的n列的A的每一行一次),必须发生N个标量乘法。#
但是,第一个代码块(使用np.dot(A,B))要快得多。我知道(通过类似:Why is matrix multiplication faster with numpy than with ctypes in Python?之类的信息)np.dot的低级实现细节可能会归咎于此。所以我的问题是:如何在不牺牲NumPy的实现效率的情况下利用矩阵B的结构来提高乘法效率,而不在c 中构建我自己的低级矩阵乘法?
这种方法是许多变量的数值优化的一部分,因此,O(N ^ 3)是难以处理的,而O(N ^ 2)可能会完成工作。
感谢您的帮助。另外,我是SO的新手,所以请原谅任何新手的错误。
答案 0 :(得分:6)
如果我正确理解A
和B
,那么我不理解for
循环以及为什么你不只是乘以两个非零向量:
# say A & B are like this:
n, N = 3, 5
A = np.array( np.random.randn(N, N ) )
B = np.zeros_like( A )
B[ n ] = np.random.randn( N )
B[:, n] = np.random.randn( N )
取B的非零行和列:
rowb, colb = B[n,:], np.copy( B[:,n] )
colb[ n ] = 0
将A
乘以这两个向量:
X = np.outer( A[:,n], rowb )
X[:,n] += np.dot( A, colb )
验证检查:
X - np.dot( A, B )
N=100
:
%timeit np.dot(A, B)
1000 loops, best of 3: 1.39 ms per loop
%timeit colb = np.copy( B[:,n] ); colb[ n ] = 0; X = np.outer( A[:,n], B[n,:] ); X[:,n] += np.dot( A, colb )
10000 loops, best of 3: 98.5 µs per loop
答案 1 :(得分:1)
我计时,使用sparse
更快:
import numpy as np
from scipy import sparse
from timeit import timeit
A = np.random.rand(100,100)
B = np.zeros(A.shape, dtype=np.float)
B[3] = np.random.rand(100)
B[:,3] = np.random.rand(100)
sparse_B = sparse.csr_matrix(B)
n = 1000
t1 = timeit('np.dot(A, B)', 'from __main__ import np, A, B', number=n)
print 'dense way : {}'.format(t1)
t2 = timeit('A * sparse_B', 'from __main__ import A, sparse_B',number=n)
print 'sparse way : {}'.format(t2)
结果:
dense way : 1.15117192268
sparse way : 0.113152980804
>>> np.allclose(np.dot(A, B), A * sparse_B)
True
随着B
的行数增加,使用稀疏矩阵进行乘法的时间优势也应增加。