作为复杂任务的一部分,我需要计算矩阵辅因子。我以一种简单的方式使用这个漂亮的代码来计算矩阵子。这是我的代码:
def matrix_cofactor(matrix):
C = np.zeros(matrix.shape)
nrows, ncols = C.shape
for row in xrange(nrows):
for col in xrange(ncols):
minor = matrix[np.array(range(row)+range(row+1,nrows))[:,np.newaxis],
np.array(range(col)+range(col+1,ncols))]
C[row, col] = (-1)**(row+col) * np.linalg.det(minor)
return C
事实证明,这个矩阵辅因子代码是瓶颈,我想优化上面的代码片段。关于如何做到这一点,有什么想法吗?
如果矩阵是可逆的,则辅因子与逆相关:
def matrix_cofactor(matrix):
return np.linalg.inv(matrix).T * np.linalg.det(matrix)
这会产生较大的加速(对于50x50矩阵,约为1000x(。主要原因是根本的:这是一个O(n^3)
算法,而基于次要det的算法是O(n^5)
。
这可能意味着,对于不可逆矩阵,也有一些聪明的方法来计算辅因子(即,不使用上面使用的数学公式,而是使用其他等效的定义(。
如果你坚持基于det的方法,你可以做以下事情:
大部分时间似乎都花在det
内部。(请查看line_profiler自行查找。(您可以尝试通过将Numpy与"英特尔MKL"链接来加快这一部分,但除此之外,没有什么可做的。
你可以像这样加快代码的另一部分:
minor = np.zeros([nrows-1, ncols-1])
for row in xrange(nrows):
for col in xrange(ncols):
minor[:row,:col] = matrix[:row,:col]
minor[row:,:col] = matrix[row+1:,:col]
minor[:row,col:] = matrix[:row,col+1:]
minor[row:,col:] = matrix[row+1:,col+1:]
...
根据矩阵的大小,这将获得大约10-50%的总运行时间。原始代码具有Python range
和列表操作,它们比直接切片索引慢。你也可以试着更聪明,只复制次要部分中实际发生变化的部分——然而,在上述变化之后,几乎100%的时间都花在了numpy.linalg.det
中,因此对其他部分的进一步优化没有那么大意义。
np.array(range(row)+range(row+1,nrows))[:,np.newaxis]
的计算不依赖于col
,因此您可以将其移动到内部循环之外并缓存值。根据您拥有的列数,这可能会进行较小的优化。
我建议使用SVD ,而不是使用逆和行列式
def cofactors(A):
U,sigma,Vt = np.linalg.svd(A)
N = len(sigma)
g = np.tile(sigma,N)
g[::(N+1)] = 1
G = np.diag(-(-1)**N*np.product(np.reshape(g,(N,N)),1))
return U @ G @ Vt
from sympy import *
A = Matrix([[1,2,0],[0,3,0],[0,7,1]])
A.adjugate().T
输出(它是辅因子矩阵(是:
Matrix([
[ 3, 0, 0],
[-2, 1, -7],
[ 0, 0, 3]])