在我在C中工作的数值求解器中,我需要反转一个2x2矩阵然后在右侧乘以另一个矩阵:
C = B . inv(A)
我一直在使用倒置2x2矩阵的以下定义:
a = A[0][0];
b = A[0][1];
c = A[1][0];
d = A[1][1];
invA[0][0] = d/(a*d-b*c);
invA[0][1] = -b/(a*d-b*c);
invA[1][0] = -c/(a*d-b*c);
invA[1][1] = a/(a*d-b*c);
在我的求解器的前几次迭代中,这似乎给出了正确的答案,然而,经过几个步骤,事情开始增长并最终爆炸。
现在,与使用SciPy的实现相比,我发现相同的数学不会爆炸。我能找到的唯一区别是SciPy代码使用scipy.linalg.inv()
,它在内部使用LAPACK来执行反转。
当我用上述计算替换对inv()
的调用时,Python版本确实会爆炸,所以我很确定这是问题所在。计算中的微小差异正在蔓延,这使我相信它是一个数值问题 - 对于反演操作并不完全令人惊讶。
我正在使用双精度浮点数(64位),希望数值问题不会成为问题,但显然情况并非如此。
但是:我想在我的C代码中解决这个问题,而不需要调用像LAPACK这样的库,因为将它移植到纯C的全部原因是让它在目标系统上运行。此外,我想了解这个问题,而不仅仅是呼唤黑匣子。最终,如果可能的话,我也希望它以单精度运行。
所以,我的问题是,对于这样一个小矩阵,有一种数值上更稳定的方法来计算A的倒数吗?
感谢。
修改:目前正试图通过求解C
来确定我是否可以avoid the inversion。
答案 0 :(得分:6)
不要反转矩阵。几乎总是,在不反转矩阵的情况下,您可以更快,更准确地完成使用逆转完成的事情。矩阵求逆本质上是不稳定的,将其与浮点数混合会产生麻烦。
说C = B . inv(A)
与说你要为C解决AC = B
是一样的。
您可以通过将每个B
和C
拆分为两列来实现此目的。解决A C1 = B1
和A C2 = B2
将产生C。
答案 1 :(得分:5)
你的代码很好;但是,它会从四次减法中冒出loss of precision。
考虑使用更高级的技术,例如matfunc.py中使用的技术。该代码使用QR decomposition实现的Householder reflections执行反转。使用iterative refinement进一步改善了结果。
答案 2 :(得分:3)
计算行列式并不稳定。更好的方法是使用Gauss-Jordan进行部分旋转,这样你就可以在这里轻松搞定。
让我们解决系统(使用c,f = 1,0然后c,f = 0,1得到逆)
a * x + b * y = c
d * x + e * y = f
在伪代码中,这是
if a == 0 and d == 0 then "singular"
if abs(a) >= abs(d):
alpha <- d / a
beta <- e - b * alpha
if beta == 0 then "singular"
gamma <- f - c * alpha
y <- gamma / beta
x <- (c - b * y) / a
else
swap((a, b, c), (d, e, f))
restart
这比行列式+ comatrix更稳定(beta
是行列式*某个常数,以稳定的方式计算)。你可以计算完全的旋转等价物(即可能交换x和y,因此a
的第一个除法是a
是a,b,d,e中最大的数量。 ),在某些情况下这可能更稳定,但上述方法对我来说效果很好。
这相当于执行LU分解(如果要存储此LU分解,则存储gamma,beta,a,b,c)。
计算QR分解也可以明确地完成(并且如果正确地执行它也非常稳定),但它更慢(并且涉及取平方根)。选择是你的。
如果你需要更高的精度(上面的方法是稳定的,但有一些舍入误差,与特征值的比例成正比),你可以“解决纠正”。
确实,假设您使用上述方法解决了A * x = b
x
。您现在计算A * x
,并且发现它与b
不完全相同,存在轻微错误:
A * x - b = db
现在,如果您在dx
中解决了A * dx = db
,那么
A * (x - dx) = b + db - db - ddb = b - ddb
其中ddb
是由A * dx = db
的数值求解引起的错误,通常远小于db
(因为db
远小于b
})。
您可以迭代上述过程,但通常需要一步来恢复整机精度。
答案 3 :(得分:1)
使用Jacobi方法,这是一种迭代方法,只涉及“反转”A的主对角线,这非常简单,并且比反转整个矩阵更不容易出现数值不稳定。
答案 4 :(得分:1)
我同意Jean-Victor你应该使用雅可比方法。这是我的榜样:
#Helper functions:
def check_zeros(A,I,row, col=0):
"""
returns recursively the next non zero matrix row A[i]
"""
if A[row, col] != 0:
return row
else:
if row+1 == len(A):
return "The Determinant is Zero"
return check_zeros(A,I,row+1, col)
def swap_rows(M,I,row,index):
"""
swaps two rows in a matrix
"""
swap = M[row].copy()
M[row], M[index] = M[index], swap
swap = I[row].copy()
I[row], I[index] = I[index], swap
# Your Matrix M
M = np.array([[0,1,5,2],[0,4,9,23],[5,4,3,5],[2,3,1,5]], dtype=float)
I = np.identity(len(M))
M_copy = M.copy()
rows = len(M)
for i in range(rows):
index =check_zeros(M,I,i,i)
while index>i:
swap_rows(M, I, i, index)
print "swaped"
index =check_zeros(M,I,i,i)
I[i]=I[i]/M[i,i]
M[i]=M[i]/M[i,i]
for j in range(rows):
if j !=i:
I[j] = I[j] - I[i]*M[j,i]
M[j] = M[j] - M[i]*M[j,i]
print M
print I #The Inverse Matrix