计算机求解线性方程组过程中,更多的是采用数值计算方法求解而取代数学意义上效率更高的求逆运算,其中一个重要的问题是数值的稳定性。
上述线性方程组中为阶方阵,其中实际求解问题中只针对非奇异矩阵的情况下,这里首先介绍一种较为常见的分解方式求解方法。
方法求解
原理为找出满足条件的三个阶方阵使得
其中为下三角矩阵,为上三角矩阵,为置换矩阵,在原方程中会得到
其中定义得到这时该位置向量会被更容易的求得,之后将以类似方式求解,其中做回推:
上述两步和为三角矩阵求解未知数,用前者表示说明:
由于为置换矩阵所以只是被替换顺序的原先列向量,而是对角线元素为的下三角矩阵,那么求解过程可以很容易的表示为:
可以看出方式为朴素的从上到下迭代求解,时间复杂度为,第二步的上三角矩阵反向推得可知:
这里为对角线不为1的上三角矩阵,那么求解过程不仅仅要自下而上并且需要有除法进行,其中计算机内进行过程中会涉及到数值精度问题。
这里的置换矩阵提高了解决问题的数值稳定性,主要解决的是分解矩阵时存在趋近于零的情况,本文暂时讨论分解计算方法,这里给出上述对应的的求解方法:
import numpy as np
def LUPSolve(L, U, pi, b):
n = L.shape[0]
y = np.zeros((n, 1))
x = np.zeros((n, 1))
for i in range(n):
y[i] = b[pi[i]] - np.dot(L[i], y)
for i in range(n-1, -1, -1):
x[i] = (y[i]-np.dot(U[i], x))/U[i, i]
return x
其中各项数据及其输出为:
L = np.array([[1, 0, 0], [0.2, 1, 0], [0.6, 0.5, 1]])
U = np.array([[5, 6, 3], [0, 0.8, -0.6], [0, 0, 2.5]])
b = np.array([3, 7, 8])
pi = np.array([2, 0, 1])
print(LUPSolve(L, U, pi, b))
->[[-1.4] [ 2.2] [ 0.6]]
前三个变量以知,的角色扮演为索引数组取代了运算中置换矩阵的作用
计算分解
首先考虑基础的不含矩阵的分解,那么可以由高斯消元法推得:
此处在方阵中采用的递推式消元,上述公式指明了消元的一个状态,而每个状态都能成功分解一层,由矩阵乘法验证可知:
其中上述式子推导类似高等代数中分块矩阵乘法所应用的情景,最终通过迭代次求解矩阵,可以看出并不用每一层处理后都进行分解,因为在递推的的状态下只需要将每层的数据保留继续计算下一层的直至结束即可,最终上三角部分即为矩阵,下三角部分使用填补对角线即为矩阵。
def LUDecomposition(A):
n = A.shape[0]
L, U = np.eye(n), np.zeros((n, n))
for k in range(n):
U[k, k] = A[k, k]
for i in range(k+1, n):
L[i, k] = A[i, k]/U[k, k]
U[k, i] = A[k, i]
for i in range(k + 1, n):
for j in range(k+1, n):
A[i, j] -= L[i, k]*U[k, j]
return L, U
由于对角线为所以提前设置为单位矩阵,之后每行遍历对应第行:
- 计算到的子矩阵;
- 保留到矩阵;
- 维护子矩阵第列数据与第行数据;
- 计算下一阶段子矩阵:
计算分解
为了解决上述有可能为零的情况,分解再分解过程中加入了选择步骤:
选择当前状态当前列的绝对值最大的行索引交换到当前行,并将过程中的交换内容保存在置换矩阵中,这里我们在数据实现部分将交换保存在了数组中,主要作用是减少占用空间且作为索引数组使用,记作。
其中推导过程下节结合舒尔补做进一步说明,这里给出实现程序:
def LUPDecomposition(A):
n = A.shape[0]
pi = np.zeros((n, 1))
for i in range(n):
pi[i] = i
for k in range(n):
p = 0
for i in range(k, n):
if np.abs(A[i, k]) > p:
p = np.abs(A[i, k])
k_ = i
if p == 0:
error("singular matrix")
pi[k], pi[k_] = pi[k_], pi[k]
A[[k, k_], :] = A[[k_, k], :]
for i in range(k+1, n):
A[i, k] /= A[k, k]
for j in range(k+1, n):
A[i, j] -= A[i, k]*A[k, j]
return A