计算机求解线性方程组

0. 引言

在很多工程求解问题中,大部分的数值计算都可以归结为求解线性方程组的问题,所以求解线性方程组是数值计算中最为重要的一部分。其中还有很多其他数值计算中,其中的关键步骤都涉及到线性方程组的求解。由克莱姆法则可知,线性方程组 A x = b Ax=b Ax=b中,系数矩阵的行列式 det ⁡ ( A ) ≠ 0 \det(A)\neq 0 det(A)=0,则线性方程组具有唯一解。克莱姆法则在计算高维度情况下的矩阵运算中计算量很大,不适合求解一般情况下的线性方程组。常见的求解线性方程组的方法有两种,即直接方法和迭代方法。直接方法是指通过有限的步骤计算直接求解出方程组的精确解的方法。迭代的方法值得是用极限的过程去逐步逼近线性方程组精确解的方法。本节将实现这几种求解的方法。

1. 直接消元法

直接消元的方法是一种便捷的求解线性方程组的方法。高斯消去方法是最为古老的一种线性方程组求解的方法。通过改进高斯消去法得到更加便捷的主元素消去法、矩阵的三角分解(即Doolittle分解方法),是用于求解低阶稠密方程组的有效方法。特点是,通过消元将一般的线性方程组求解问题转化为上三角方程组求解问题。

1.1 高斯消元法

1.1.1 顺序高斯消去法

一般地,设 n n n阶线性方程组为
{ a 11 ( 0 ) x 1 + a 12 ( 0 ) x 2 + . . . + a 1 n ( 0 ) x n = b 1 ( 0 ) a 21 ( 0 ) x 1 + a 22 ( 0 ) x 2 + . . . + a 2 n ( 0 ) x n = b 2 ( 0 ) : a n 1 ( 0 ) x 1 + a n 2 ( 0 ) x 2 + . . . + a n n ( 0 ) x n = b n ( 0 ) \begin{cases} a_{11}^{(0)}x_{1}+a_{12}^{(0)}x_{2}+...+a_{1n}^{(0)}x_{n}=b_{1}^{(0)}\\ a_{21}^{(0)}x_{1}+a_{22}^{(0)}x_{2}+...+a_{2n}^{(0)}x_{n}=b_{2}^{(0)}\\ :\\ a_{n1}^{(0)}x_{1}+a_{n2}^{(0)}x_{2}+...+a_{nn}^{(0)}x_{n}=b_{n}^{(0)}\\ \end{cases} a11(0)x1+a12(0)x2+...+a1n(0)xn=b1(0)a21(0)x1+a22(0)x2+...+a2n(0)xn=b2(0):an1(0)x1+an2(0)x2+...+ann(0)xn=bn(0)
写成增广矩阵的方式为
( A ( 0 ) ∣ b ( 0 ) ) = [ a 11 ( 0 ) a 12 ( 0 ) . . . a 1 n ( 0 ) b 1 ( 0 ) a 21 ( 0 ) a 22 ( 0 ) . . . a 2 n ( 0 ) b 2 ( 0 ) : : : : : a n 1 ( 0 ) a n 2 ( 0 ) . . . a n n ( 0 ) b n ( 0 ) ] (A^{(0)}|b^{(0)})=\left[\begin{array}{cccc} a_{11}^{(0)}&a_{12}^{(0)}&...&a_{1n}^{(0)}&b_{1}^{(0)}\\ a_{21}^{(0)}&a_{22}^{(0)}&...&a_{2n}^{(0)}&b_{2}^{(0)}\\ :&:&:&:&:\\ a_{n1}^{(0)}&a_{n2}^{(0)}&...&a_{nn}^{(0)}&b_{n}^{(0)}\\ \end{array}\right] (A(0)b(0))=a11(0)a21(0):an1(0)a12(0)a22(0):an2(0)......:...a1n(0)a2n(0):ann(0)b1(0)b2(0):bn(0)
l i 1 = a i 1 ( 0 ) / a 11 ( 0 ) , ( i = 2 , 3 , . . . , n ) l_{i1}=a_{i1}^{(0)}/a_{11}^{(0)},(i=2,3,...,n) li1=ai1(0)/a11(0),(i=2,3,...,n),将第一行的 − l i 1 -l_{i1} li1倍加到第 i i i行上,那么矩阵变为
( A ( 1 ) ∣ b ( 1 ) ) [ a 11 ( 0 ) a 12 ( 0 ) . . . a 1 n ( 0 ) b 1 ( 0 ) 0 a 22 ( 1 ) . . . a 2 n ( 1 ) b 2 ( 1 ) : : : : : a n 1 ( 1 ) a n 2 ( 1 ) . . . a n n ( 1 ) b n ( 1 ) ] (A^{(1)}|b^{(1)})\left[\begin{array}{cccc} a_{11}^{(0)}&a_{12}^{(0)}&...&a_{1n}^{(0)}&b_{1}^{(0)}\\ 0&a_{22}^{(1)}&...&a_{2n}^{(1)}&b_{2}^{(1)}\\ :&:&:&:&:\\ a_{n1}^{(1)}&a_{n2}^{(1)}&...&a_{nn}^{(1)}&b_{n}^{(1)}\\ \end{array}\right] (A(1)b(1))a11(0)0:an1(1)a12(0)a22(1):an2(1)......:...a1n(0)a2n(1):ann(1)b1(0)b2(1):bn(1)
其中,式子中
a i j ( 1 ) = a i j ( 0 ) − l i 1 a 1 j ( 0 ) , ( i = 2 , 3 , . . . , n ; j = 1 , 2 , . . . , n ) a_{ij}^{(1)}=a_{ij}^{(0)}-l_{i1}a_{1j}^{(0)},(i=2,3,...,n;j=1,2,...,n) aij(1)=aij(0)li1a1j(0),(i=2,3,...,n;j=1,2,...,n)

b i ( 1 ) = b i ( 0 ) − l i 1 b 1 ( 0 ) , ( i = 2 , 3 , . . . , n ) b_{i}^{(1)}=b_{i}^{(0)}-l_{i1}b_{1}^{(0)},(i=2,3,...,n) bi(1)=bi(0)li1b1(0),(i=2,3,...,n)

k k k步骤的时候,设主元素 a k k ( k − 1 ) ≠ 0 a_{kk}^{(k-1)}\neq 0 akk(k1)=0时,设 l i k = a i k ( k − 1 ) / a k k ( k − 1 ) l_{ik}=a_{ik}^{(k-1)}/a_{kk}^{(k-1)} lik=aik(k1)/akk(k1),并且将 ( A ( k − 1 ) ∣ b ( k − 1 ) ) (A^{(k-1)|b^{(k-1)}}) (A(k1)b(k1))的第 k k k行的 − l i k -l_{ik} lik倍加到第 i i i ( i = k + 1 , k + 2 , . . . , n ) (i=k+1,k+2,...,n) (i=k+1,k+2,...,n)得到:
( A ( k ) ∣ b ( k ) ) = [ a 11 ( 0 ) a 12 ( 0 ) . . . a 1 k ( 0 ) a 1 ( k + 1 ) ( 0 ) . . . a 1 n ( 0 ) b 1 ( 0 ) : : : : : : a k k ( k − 1 ) a k ( k + 1 ) ( 0 ) . . . a k n ( k − 1 ) b k ( k − 1 ) a ( k + 1 ) ( k + 1 ) ( k ) . . . a ( k + 1 ) n ( k ) b n ( k ) : : : : a n ( k + 1 ) ( k ) . . . a n n ( k ) b n ( k ) ] (A^{(k)}|b^{(k)})=\left[\begin{array}{cccc} a_{11}^{(0)}&a_{12}^{(0)}&...&a_{1k}^{(0)}&a_{1(k+1)}^{(0)}&...&a_{1n}^{(0)}&b_{1}^{(0)}\\ &&:&:&:&:&:&:\\ &&&a_{kk}^{(k-1)}&a_{k(k+1)}^{(0)}&...&a_{kn}^{(k-1)}&b_{k}^{(k-1)}\\ &&&&a_{(k+1)(k+1)}^{(k)}&...&a_{(k+1)n}^{(k)}&b_{n}^{(k)}\\ &&&&:&:&:&:\\ &&&&a_{n(k+1)}^{(k)}&...&a_{nn}^{(k)}&b_{n}^{(k)}\\ \end{array}\right] (A(k)b(k))=a11(0)a12(0)...:a1k(0):akk(k1)a1(k+1)(0):ak(k+1)(0)a(k+1)(k+1)(k):an(k+1)(k)...:......:...a1n(0):akn(k1)a(k+1)n(k):ann(k)b1(0):bk(k1)bn(k):bn(k)

式中
a i j ( k ) = a i j ( k − 1 ) − l i k a k j ( k − 1 ) , ( i , j = k + 1 , k + 2 , . . . , n ) a_{ij}^{(k)}=a_{ij}^{(k-1)}-l_{ik}a_{kj}^{(k-1)},(i,j=k+1,k+2,...,n) aij(k)=aij(k1)likakj(k1),(i,j=k+1,k+2,...,n)

b i ( k ) = b i ( k − 1 ) − l i k b k ( k − 1 ) , ( i = k + 1 , k + 2 , . . . , n ) b_{i}^{(k)}=b_{i}^{(k-1)}-l_{ik}b_{k}^{(k-1)},(i=k+1,k+2,...,n) bi(k)=bi(k1)likbk(k1),(i=k+1,k+2,...,n)

所以经过 n − 1 n-1 n1步消元之后,可以得到原方程组的同解三角形方程组 A ( n − 1 ) x = b ( n − 1 ) A^{(n-1)}x=b^{(n-1)} A(n1)x=b(n1),即
[ a 11 ( 0 ) a 12 ( 0 ) . . . a 1 n ( 0 ) a 22 ( 1 ) . . . a 2 n ( 1 ) : . . . : a n n ( n − 1 ) ] [ x 1 x 2 : x n ] = [ b 1 ( n − 1 ) b 2 ( n − 1 ) : b n ( n − 1 ) ] \left[\begin{array}{cccc} a_{11}^{(0)}&a_{12}^{(0)}&...&a_{1n}^{(0)}\\ &a_{22}^{(1)}&...&a_{2n}^{(1)}\\ &:&...&:\\ &&&a_{nn}^{(n-1)}\\ \end{array}\right] \left[\begin{array}{cccc} x_{1}\\ x_{2}\\ :\\ x_{n}\\ \end{array}\right]= \left[\begin{array}{cccc} b_{1}^{(n-1)}\\ b_{2}^{(n-1)}\\ :\\ b_{n}^{(n-1)}\\ \end{array}\right] a11(0)a12(0)a22(1):.........a1n(0)a2n(1):ann(n1)x1x2:xn=b1(n1)b2(n1):bn(n1)

综上所述,整个方程的求解过程如下所示
k = 1 , 2 , . . . , n − 1 k=1,2,...,n-1 k=1,2,...,n1逐次计算
{ l i k = a i k ( k − 1 ) / a k k ( k − 1 ) , ( i = k + 1 , k + 2 , . . . , n ) a i j ( k ) = a i j ( k − 1 ) − l i k a k j ( k − 1 ) , ( i , j = k + 1 , k + 2 , . . . , n ) b i ( k ) = b i ( k − 1 ) − l i k b i ( k − 1 ) , ( i = k + 1 , . . . , n ) \begin{cases} l_{ik}=a_{ik}^{(k-1)}/a_{kk}^{(k-1)}&,(i=k+1,k+2,...,n)\\ a_{ij}^{(k)}=a_{ij}^{(k-1)}-l_{ik}a_{kj}^{(k-1)}&,(i,j=k+1,k+2,...,n)\\ b_{i}^{(k)}=b_{i}^{(k-1)}-l_{ik}b_{i}^{(k-1)}&,(i=k+1,...,n) \end{cases} lik=aik(k1)/akk(k1)aij(k)=aij(k1)likakj(k1)bi(k)=bi(k1)likbi(k1),(i=k+1,k+2,...,n),(i,j=k+1,k+2,...,n),(i=k+1,...,n)

逐步回代求得原方程组的解,回代过程如下所示:
{ x n = b n ( n − 1 ) / a n n ( n − 1 ) x k = ( b k ( k − 1 ) − ∑ j = k + 1 n a k j ( k − 1 ) x j ) / a k k ( k − 1 ) , ( k = n − 1 , n − 2 , . . . , 1 ) \begin{cases} x_{n}=b_{n}^{(n-1)}/a_{nn}^{(n-1)}\\ x_{k}=(b_{k}^{(k-1)}-\sum\limits_{j=k+1}^{n}a_{kj}^{(k-1)}x_{j})/a_{kk}^{(k-1)}&,(k=n-1,n-2,...,1)\\ \end{cases} xn=bn(n1)/ann(n1)xk=(bk(k1)j=k+1nakj(k1)xj)/akk(k1),(k=n1,n2,...,1)

一般地,如果 n n n阶矩阵 A A A的顺序主子式均不为零,那么高斯消去法中求解线性方程组 A x = b Ax=b Ax=b的主元素满足 a k k ( k − 1 ) ≠ 0 a_{kk}^{(k-1)}\neq 0 akk(k1)=0
代码实现如下所示:

def GaussianSeq(Mat,Bais):
    if (Mat.shape[0]!=Mat.shape[1]) or (Mat.shape[0]!=Bais.shape[0]):
        raise ValueError("size of matrix doesn't match!")
    A = Mat.copy()
    b = Bais.copy()
    n = A.shape[0]
    # 消元过程
    for k in range(n):
        for i in range(k+1,n):
            l_ik = A[i,k]/A[k,k]
            for j in range(k,n):
                A[i, j] = A[i, j] - l_ik * A[k, j]
            b[i] = b[i] - l_ik*b[k]
    # 回代过程
    x = np.zeros((n,))
    x[n-1] = b[n-1]/A[n-1,n-1]
    for k in range(n-2,-1,-1):
        x[k] = (b[k]-np.sum(A[k,k+1:]*x[k+1:]))/A[k,k]
    return x

1.1.2 主元素高斯消去法

主元素高斯消去法主要解决的是舍入误差的扩大和传播而提出来的。事实上,从消元过程中可以看出来,当 ∣ l i k ∣ = ∣ a i k ( k − 1 ) / a k k ( k − 1 ) ∣ > 1 |l_{ik}|=|{a_{ik}^{(k-1)}}/{a_{kk}^{(k-1)}}|>1 lik=aik(k1)/akk(k1)>1时候,若 a k j ( k ∗ − 1 ) a_{kj}^{(k*-1)} akj(k1)有舍入误差 e k − 1 e_{k-1} ek1,则经过计算 a i j ( k ) = a i j ( k − 1 ) − l i k a k j ( k − 1 ) a_{ij}^{(k)}=a_{ij}^{(k-1)}-l_{ik}a_{kj}^{(k-1)} aij(k)=aij(k1)likakj(k1) a k j a_{kj} akj会有比 e k − 1 e_{k-1} ek1更大的舍入误差 e k = ∣ l i k ∣ e k − 1 e_{k}=|l_{ik}|e_{k-1} ek=likek1。解决的方法是用交换的方法或者交换未知数次序的方法,选择绝对值尽可能大的系数作为第 k k k次消元过程中的主元素 a k k ( k − 1 ) a_{kk}^{(k-1)} akk(k1)。所以基本的算法过程如下所示:
输入: 矩阵系数 A A A b b b

( A ∣ b ) (A|b) (Ab) A n × ( n + 1 ) A_{n\times{(n+1)}} An×(n+1)

 for  k = 1 , 2 , . . . , n − 1 \text{ for }k=1,2,...,n-1  for k=1,2,...,n1

消元过程:按照第 k k k列选定主元素,确定 r r r使得 ∣ a r k ∣ = max ⁡ k ≤ i ≤ k + 1 ∣ a i k ∣ |a_{rk}|=\max\limits_{k\leq i \leq k+1}|a_{ik}| ark=kik+1maxaik

  • 判断 ∣ a r k ∣ |a_{rk}| ark是否等于0,如果是,则系数矩阵为奇异矩阵,退出计算过程;如果不是,则
    • 如果 r > k r>k r>k,则
      • 对于 j = k + 1 , . . . , n + 1 j=k+1,...,n+1 j=k+1,...,n+1交换 r , k r,k r,k两行元素: a r j ↔ a k j a_{rj}\leftrightarrow a_{kj} arjakj
    • 对于 i = k + 1 , k + 2 , . . . i=k+1,k+2,... i=k+1,k+2,...,计算
      • a i k = a i k / a k k a_{ik}=a_{ik}/a_{kk} aik=aik/akk
        • 对于 j = k + 1 , k + 2 , . . . , n , n + 1 j=k+1,k+2,...,n,n+1 j=k+1,k+2,...,n,n+1,计算
          • a i j = a i j − a i k a k j a_{ij}=a_{ij}-a_{ik}a_{kj} aij=aijaikakj
             end for  \text{ end for }  end for 

回代过程: a n n + 1 = a n n + 1 / a n n a_{nn+1}=a_{nn+1}/a_{nn} ann+1=ann+1/ann
 for  k = 1 , 2 , . . . , n − 1 \text{ for }k=1,2,...,n-1  for k=1,2,...,n1

  • 计算 a k n + 1 = ( a k n + 1 − ∑ j = k + 1 n a k j a j n + 1 ) / a k k a_{kn+1}=(a_{kn+1}-\sum\limits_{j=k+1}^{n}a_{kj}a_{jn+1})/a_{kk} akn+1=(akn+1j=k+1nakjajn+1)/akk

 end for  \text{ end for }  end for 
输出: 未知数方程的解 x = ( a 1 n + 1 , a 2 n + 1 , . . . , a n n + 1 ) x=(a_{1n+1},a_{2n+1},...,a_{nn+1}) x=(a1n+1,a2n+1,...,ann+1)
用numpy实现过程为:

def GaussianMain(Mat,Bais):
    if (Mat.shape[0]!=Mat.shape[1]) or (Mat.shape[0]!=Bais.shape[0]):
        raise ValueError("size of matrix doesn't match!")
    A = Mat.copy()
    b = Bais.copy()
    n = A.shape[0]
    # 消元过程
    for k in range(n):
        # 选定主元素
        r = k
        for j in range(k,n):
            if np.abs(A[j,k])>np.abs(A[r,k]):
                r = j
        # 交换行
        A[[r, k], :] = A[[k, r], :]
        b[r],b[k] = b[k],b[r]
        if np.abs(A[k,k])< 1e-150:
            raise np.linalg.LinAlgError
        for i in range(k+1,n):
            l_ik = A[i,k]/A[k,k]
            for j in range(k,n):
                A[i, j] = A[i, j] - l_ik * A[k, j]
            b[i] = b[i] - l_ik*b[k]

    # 回代过程
    x = np.zeros((n,))
    x[n-1] = b[n-1]/A[n-1,n-1]
    for k in range(n-2,-1,-1):
        x[k] = (b[k]-np.sum(A[k,k+1:]*x[k+1:]))/A[k,k]
    return x

1.1.3 高斯-约当(Gauss-Jordan)消去法

高斯消去法是将系数矩阵 A A A变为上三角矩阵。高斯约当消去法是将对角线上方和下方的元素都消去,使得系数矩阵变为单位矩阵,这样就不需要回代过程。
所以计算过程变为:
{ a k j ( k ) = a k j ( k − 1 ) / a k k ( k − 1 ) , ( j = k , k + 1 , . . . , n ) a i j ( k ) = a i j ( k − 1 ) − a i k ( k − 1 ) × a k j ( k ) , ( i = 1 , 2 , . . . , n , i ≠ k , j = k , k + 1 , . . . , n ) b k ( k ) = b k ( k − 1 ) / a k k ( k − 1 ) b k ( k ) = b k ( k − 1 ) − a i k ( k − 1 ) × b k ( k ) \begin{cases} a_{kj}^{(k)}=a_{kj}^{(k-1)}/a_{kk}^{(k-1)}&,(j=k,k+1,...,n)\\ a_{ij}^{(k)}=a_{ij}^{(k-1)}-a_{ik}^{(k-1)}\times a_{kj}^{(k)}&,(i=1,2,...,n,i\neq k,j=k,k+1,...,n)\\ b_{k}^{(k)}=b_{k}^{(k-1)}/a_{kk}^{(k-1)}\\ b_{k}^{(k)}=b_{k}^{(k-1)}-a_{ik}^{(k-1)}\times b_{k}^{(k)}\\ \end{cases} akj(k)=akj(k1)/akk(k1)aij(k)=aij(k1)aik(k1)×akj(k)bk(k)=bk(k1)/akk(k1)bk(k)=bk(k1)aik(k1)×bk(k),(j=k,k+1,...,n),(i=1,2,...,n,i=k,j=k,k+1,...,n)

用numpy实现为:

def Gauss_Jordan(Mat,Bais):
    if (Mat.shape[0] != Mat.shape[1]) or (Mat.shape[0] != Bais.shape[0]):
        raise ValueError("size of matrix doesn't match!")
    A = Mat.copy()
    b = Bais.copy()
    n = A.shape[0]
    # 消元过程
    for k in range(n):
        # 选定主元素
        r = k
        for j in range(k, n):
            if np.abs(A[j, k]) > np.abs(A[r, k]):
                r = j
        # 交换行
        A[[r, k], :] = A[[k, r], :]
        b[r], b[k] = b[k], b[r]
        if np.abs(A[k, k]) < 1e-150:
            raise np.linalg.LinAlgError

        tmp = A[k, k]
        for j in range(k,n):
            A[k,j] = A[k,j]/tmp
        b[k] = b[k]/tmp
        for i in range(n):
            if i!=k:
                tmp = A[i,k]
                for j in range(k,n):
                    A[i,j] = A[i,j] - tmp *A[k,j]
                b[i] = b[i] - tmp*b[k]
    return b

1.2 三角分解方法

这种算法也称作是Doolittle分解法,或者是LU分解。一般地,任意一个矩阵不一定有LU分解。有以下定理:
定理一:对任意矩阵 A ∈ R n × n A\in{R^{n\times n}} ARn×n,若 A A A的顺序主子式均不为零,则 A A A具有唯一的LU分解。
定理二:若矩阵 A ∈ R n × n A\in{R^{n\times n}} ARn×n非奇异,并且其 L U LU LU分解存在,则 A A A L U LU LU分解是唯一的。
这样,如果对于线性方程组 A x = b Ax=b Ax=b中系数矩阵 A = L U A=LU A=LU,那么方程可以写为 ( L U ) x = b (LU)x=b (LU)x=b。所以设
{ U x = y L y = b \begin{cases} Ux=y\\ Ly=b\\ \end{cases} { Ux=yLy=b

所以进一步我们改进这种方程的求解方法。

A = [ a 11 a 12 . . . a 1 n a 21 a 22 . . . a 2 n : : : : a n 1 a n 2 . . . a n n ] = [ l 11 l 21 l 22 : : l n 1 l n 2 . . . l n n ] [ u 11 u 12 . . . u 1 n u 22 . . . u 2 n : : u n n ] A= \left [\begin{array}{cccc} a_{11}&a_{12}&...&a_{1n}\\ a_{21}&a_{22}&...&a_{2n}\\ :&:&:&:\\ a_{n1}&a_{n2}&...&a_{nn}\\ \end{array}\right] =\left [\begin{array}{cccc} l_{11}\\ l_{21}&l_{22}\\ :&:\\ l_{n1}&l_{n2}&...&l_{nn} \end{array}\right] \left [\begin{array}{cccc} u_{11}&u_{12}&...&u_{1n}\\ &u_{22}&...&u_{2n}\\ &&:&:\\ &&&u_{nn}\\ \end{array}\right] A=a11a21:an1a12a22:an2......:...a1na2n:ann=l11l21:ln1l22:ln2...lnnu11u12u22......:u1nu2n:unn
我们在这里设 l i i = 1 , ( i = 1 , 2 , . . , n ) l_{ii}=1,(i=1,2,..,n) lii=1,(i=1,2,..,n)
这样可以得到,对于 k = 1 , 2 , . . . , n k=1,2,...,n k=1,2,...,n
u k j = a k j − ∑ r = 1 k − 1 l k r u r j , ( j = k , k + 1 , . . . , n ) l i k = ( a i k − ∑ r = 1 k − 1 l i r u r k ) / u k k , ( i = k + 1 , k + 2 , . . . , n ) u_{kj}=a_{kj}-\sum_{r=1}^{k-1}l_{kr}u_{rj},(j=k,k+1,...,n)\\ l_{ik}=(a_{ik}-\sum_{r=1}^{k-1}l_{ir}u_{rk})/u_{kk},(i=k+1,k+2,...,n) ukj=akjr=1k1lkrurj,(j=k,k+1,...,n)lik=(aikr=1k1lirurk)/ukk,(i=k+1,k+2,...,n)
回代过程为:
{ y 1 = b 1 y k = b k − ∑ j = 1 k − 1 l k j y i , ( k = 2 , 3 , . . . , n ) \begin{cases} y_{1}=b_{1}\\ y_{k}=b_{k}-\sum\limits_{j=1}^{k-1}l_{kj}y_{i}&,(k=2,3,...,n) \end{cases} y1=b1yk=bkj=1k1lkjyi,(k=2,3,...,n)

{ x n = y n / u n n x k = ( y k − ∑ j = k + 1 n u k j x j ) / u k k , ( k = n − 1 , n − 2 , . . . , 1 ) \begin{cases} x_{n}=y_{n}/u_{nn}\\ x_{k}=(y_{k}-\sum\limits_{j=k+1}^{n}u_{kj}x_{j})/u_{kk}&,(k=n-1,n-2,...,1) \end{cases} xn=yn/unnxk=(ykj=k+1nukjxj)/ukk,(k=n1,n2,...,1)
用numpy实现为:

def Doolittle_LU(A):
    if A.shape[0]!=A.shape[1]:
        raise ValueError("size of matrix doesn't match!")
    L_U_mat = np.zeros(A.shape)
    n = A.shape[0]
    for k in range(n):
        for j in range(k,n):
            if k==0:
                L_U_mat[k,j] = A[k,j]
            else:
                L_U_mat[k,j] = A[k,j] - np.sum(L_U_mat[k,:k]*L_U_mat[:k,j])
        for i in range(k+1,n):
            if k==0:
                L_U_mat[i,k] = A[i,k]/L_U_mat[k,k]
            else:
                L_U_mat[i,k] = (A[i,k] -np.sum(L_U_mat[i,:k]*L_U_mat[:k,k]))/L_U_mat[k,k]
    #L = np.tril(L_U_mat,k=-1)
    #U = np.triu(L_U_mat,k=0)
    #L = L + np.diag([1.0]*n)
    return L_U_mat
def Doolittle_solve(A,b):
    if A.shape[0]!=b.shape[0]:
        raise ValueError("size of matrix doesn't match!")
    L_U_mat = Doolittle_LU(A)
    n = L_U_mat.shape[0]
    y = np.zeros((n,))
    for k in range(n):
        if k==0:
            y[k] = b[k]
        else:
            y[k] = b[k] - np.sum(L_U_mat[k,:k]*y[:k])
    x = np.zeros((n,))
    for k in range(n-1,-1,-1):
        if k == n-1:
            x[k] = y[k]/L_U_mat[k,k]
        else:
            x[k] = (y[k] - np.sum(L_U_mat[k,k+1:]*x[k+1:]))/L_U_mat[k,k]
    return x

一般地,对于对角分解方法中 A = L U A=LU A=LU L L L的对角线元素为 1 1 1的时候,称为Doolittle分解;当 U U U的对角线元素为 1 1 1的时候,称为Crout分解。

1.3 解对角方程的追赶法

有一种特殊的方程被称作是系数矩阵对角占优的线性方程组,设 A x = f Ax=f Ax=f,式子中
A = [ b 1 c 1 a 2 b 2 c 2 : : : a n − 1 b n − 1 c n − 1 a n b n ] , f = [ f 1 f 2 : f n ] A=\left [\begin{array}{cccc} b_{1}&c_{1}\\ a_{2}&b_{2}&c_{2}\\ &&:&:&:\\ &&&a_{n-1}&b_{n-1}&c_{n-1}\\ &&&&a_{n}&b_{n} \end{array}\right ], f=\left [\begin{array}{cccc} f_{1}\\ f_{2}\\ :\\ f_{n}\\ \end{array}\right ] A=b1a2c1b2c2::an1:bn1ancn1bn,f=f1f2:fn
并且满足
{ ∣ b 1 ∣ > ∣ c 1 ∣ > 0 ∣ b i ∣ ≥ ∣ a i ∣ + ∣ c i , ∣ ( a i c i ≠ 0 ) ∣ b n ∣ > ∣ a n ∣ > 0 \begin{cases} |b_{1}|>|c_{1}|>0\\ |b_{i}|\geq |a_{i}|+|c_{i}&,|(a_{i}c_{i}\neq 0)\\ |b_{n}|>|a_{n}|>0 \end{cases} b1>c1>0biai+cibn>an>0,(aici=0)

我们对矩阵 A A A进行Crout分解,即
A = L U = [ l 1 a 2 l 2 a 3 l 3 : : a n l n ] [ 1 u 1 1 u 2 1 u 3 : : 1 u n ] A=LU=\left [\begin{array}{cccc} l_{1}\\ a_{2}&l_{2}\\ &a_{3}&l_{3}\\ &&:&:\\ &&&a_{n}&l_{n} \end{array}\right ] \left [\begin{array}{cccc} 1&u_{1}\\ &1&u_{2}\\ &&1&u_{3}\\ &&&:&:\\ &&&&1&u_{n} \end{array}\right ] A=LU=l1a2l2a3l3::anln1u11u21u3::1un

这样解对角占优的方程过程如下所示:
{ l 1 = b 1 u i = c i / l i l i + 1 = b i + 1 − a i + 1 u i , ( i = 2 , . . . , n − 1 ) \begin{cases} l_{1}=b_{1}\\ u_{i}=c_{i}/l_{i}\\ l_{i+1}=b_{i+1}-a_{i+1}u_{i}&,(i=2,...,n-1)\\ \end{cases} l1=b1ui=ci/lili+1=bi+1ai+1ui,(i=2,...,n1)

回代过程为
{ y 1 = f 1 / l 1 y i = ( f i − a i y i − 1 ) / l i , ( i = 2 , . . . , n ) \begin{cases} y_{1}=f_{1}/l_{1}\\ y_{i}=(f_{i}-a_{i}y_{i-1})/l_{i}&,(i=2,...,n)\\ \end{cases} { y1=f1/l1yi=(fiaiyi1)/li,(i=2,...,n)
{ x n = y n x i = y i − u i x i + 1 , ( i = n − 1 , . . . , 1 ) \begin{cases} x_{n}=y_{n}\\ x_{i}=y_{i}-u_{i}x_{i+1}&,(i=n-1,...,1) \end{cases} { xn=ynxi=yiuixi+1,(i=n1,...,1)

1.4 误差分析

有一种线性方程组被称作是病态方程组,这类方程组有这样的一个特点。设线性方程为 A x = b Ax=b Ax=b,那么若 A A A或者 b b b有微小的扰动 ∣ ∣ δ A ∣ ∣ ||\delta A|| δA ∣ ∣ δ b ∣ ∣ ||\delta b|| δb时候,它的解 x x x扰动很大 ∣ ∣ δ x ∣ ∣ ||\delta x|| δx,这样的方程组被称为是病态方程组,相应的矩阵称为病态矩阵。反之,如若 ∣ ∣ δ x ∣ ∣ ||\delta x|| δx比很小,称方程为良态方程。一般称为 ∣ ∣ δ x ∣ ∣ ∣ ∣ x ∣ ∣ \frac{||\delta x||}{||x||} xδx ∣ ∣ δ A ∣ ∣ ∣ ∣ A ∣ ∣ \frac{||\delta A||}{||A||} AδA ∣ ∣ δ b ∣ ∣ ∣ ∣ b ∣ ∣ \frac{||\delta b||}{||b||} bδb为相对扰动。线性方程有扰动的时候,方程则变为
( A + δ A ) ( x + δ x ) = b + δ b (A+\delta A)(x+\delta x)=b+\delta b (A+δA)(x+δx)=b+δb

(1) δ A = 0 , δ b ≠ 0 , b ≠ 0 \delta A=0,\delta b\neq 0,b\neq 0 δA=0,δb=0,b=0时候,则方程化为:
A ( x + δ x ) = b + δ b A(x+\delta x)=b+\delta b A(x+δx)=b+δb

由于 A x = b Ax=b Ax=b则会有
A δ x = δ b A\delta x=\delta b Aδx=δb

由范数的定义可以得到
∣ ∣ δ x ∣ ∣ ≤ ∣ ∣ A − 1 ∣ ∣ ⋅ ∣ ∣ δ b ∣ ∣ , ∣ ∣ b ∣ ∣ ≤ ∣ ∣ A ∣ ∣ ⋅ ∣ ∣ x ∣ ∣ ||\delta x||\leq||A^{-1}||\cdot||\delta b||,||b||\leq||A||\cdot||x|| δxA1δb,bAx

所以得到
∣ ∣ δ x ∣ ∣ ⋅ ∣ ∣ b ∣ ∣ ≤ ∣ ∣ A ∣ ∣ ⋅ ∣ ∣ A − 1 ∣ ∣ ⋅ ∣ ∣ x ∣ ∣ ⋅ ∣ ∣ δ b ∣ ∣ ||\delta x||\cdot||b||\leq||A||\cdot||A^{-1}||\cdot||x||\cdot||\delta b|| δxbAA1xδb

从而有
∣ ∣ δ x ∣ ∣ ∣ ∣ x ∣ ∣ ≤ ∣ ∣ A ∣ ∣ ⋅ ∣ ∣ A − 1 ∣ ∣ ⋅ ∣ ∣ δ b ∣ ∣ ∣ ∣ b ∣ ∣ \frac{||\delta x||}{||x||}\leq||A||\cdot||A^{-1}||\cdot{\frac{||\delta b||}{||b||}} xδxAA1bδb

(2) δ A ≠ 0 , δ b = 0 , A ≠ 0 \delta A\neq0,\delta b=0,A\neq0 δA=0,δb=0,A=0时候,则方程化为
( A + δ A ) ( x + δ x ) = b (A+\delta A)(x+\delta x)=b (A+δA)(x+δx)=b

所以得到
δ A ( x + δ x ) + A δ x = 0 , δ x = − A δ A ( x + δ x ) \delta A(x+\delta x)+A\delta x=0,\delta x=-A\delta A(x+\delta x) δA(x+δx)+Aδx=0,δx=AδA(x+δx)

于是又有
∣ ∣ δ x ∣ ∣ ≤ ∣ ∣ A − 1 ∣ ∣ ⋅ ∣ ∣ δ A ∣ ∣ ⋅ ∣ ∣ x + δ x ∣ ∣ ||\delta x||\leq||A^{-1}||\cdot||\delta A||\cdot||x+\delta x|| δxA1δAx+δx

从而有
∣ ∣ δ x ∣ ∣ ∣ ∣ x + δ x ∣ ∣ ≤ ∣ ∣ A ∣ ∣ ⋅ ∣ ∣ A − 1 ∣ ∣ ⋅ ∣ ∣ δ A ∣ ∣ ∣ ∣ A ∣ ∣ \frac{||\delta x||}{||x+\delta x||}\leq||A||\cdot||A^{-1}||\cdot{\frac{||\delta A||}{||A||}} x+δxδxAA1AδA

定义条件数 cond ( A ) = ∣ ∣ A ∣ ∣ ⋅ ∣ ∣ A − 1 ∣ ∣ \text{cond}(A)=||A||\cdot||A^{-1}|| cond(A)=AA1。一般情况下,当系数矩阵的条件数较大的时候为病态方程,较小的时候为良态方程。
解决病态方程求解方法有以下的方法:
①采用高精度的运算方法计算;
②采用预处理的方法改善系数矩阵的条件数,例如可以寻找非奇异的对角矩阵 P , Q P,Q P,Q将方程 A x = b Ax=b Ax=b化为
{ P A Q y = P b y = Q − 1 x \begin{cases} PAQy=Pb\\ y=Q^{-1}x\\ \end{cases} { PAQy=Pby=Q1x

③当矩阵 A A A的元素数量级较大的时候,对于系数矩阵 A A A的行或者列乘以适当的比例因子,使得矩阵 A A A所有的行列按照 ∞ \infty 范数大体上有相同的长度,使得系数矩阵 A A A均衡,使得 A A A条件数得到基本的改善。

2. 迭代法

一般情况下,对于低维度的系数矩阵求解可以直接使用高斯消去方法求解线性方程组。但是对于高维度的线性方程矩阵,求解显得力不从心。迭代方法是一种线性方程组的近似求解方法。所以这里提出了几种矩阵求解的迭代方法。

2.1 雅克比(Jacobi)迭代法

设线性方程组 A x = b Ax=b Ax=b,将系数矩阵 A A A进行分解处理: A = D − L − U A=D-L-U A=DLU,其中矩阵 D , L , U D,L,U D,L,U如下所示
D = [ a 11 a 22 a 33 : a n n ] D=\left[ \begin{array}{cccc} a_{11}\\ &a_{22}\\ &&a_{33}\\ &&&:\\ &&&&a_{nn} \end{array}\right] D=a11a22a33:ann

− L = [ 0 a 21 0 a 31 a 32 0 a 41 a 42 a 43 : : : : : a n 1 a n 2 a n 3 . . . a n ( n − 1 ) 0 ] -L=\left[ \begin{array}{cccc} 0\\ a_{21}&0\\ a_{31}&a_{32}&0\\ a_{41}&a_{42}&a_{43}&:\\ :&:&:&:\\ a_{n1}&a_{n2}&a_{n3}&...&a_{n(n-1)}&0\\ \end{array}\right] L=0a21a31a41:an10a32a42:an20a43:an3::...an(n1)0

− U = [ 0 a 12 a 13 . . . a 1 n 0 a 23 : a 2 n : : : 0 a ( n − 1 ) n 0 ] -U=\left[ \begin{array}{cccc} 0&a_{12}&a_{13}&...&a_{1n}\\ &0&a_{23}&:&a_{2n}\\ &&:&:&:\\ &&&0&a_{(n-1)n}\\ &&&&0\\ \end{array}\right] U=0a120a13a23:...::0a1na2n:a(n1)n0

则线性方程组化为 ( D − L − U ) x = b (D-L-U)x=b (DLU)x=b,从而得到 D x = ( L + U ) x + b Dx=(L+U)x+b Dx=(L+U)x+b,于是得到
x = D − 1 ( L + U ) x + D − 1 b = ( I − D − 1 A ) x + D − 1 b x=D^{-1}(L+U)x+D^{-1}b=(I-D^{-1}A)x+D^{-1}b x=D1(L+U)x+D1b=(ID1A)x+D1b

B J = I − D − 1 A , f J = D − 1 b B_{J}=I-D^{-1}A,f_{J}=D^{-1}b BJ=ID1A,fJ=D1b,可见通过以下的表达式进行递推可以实现方程的求解:
x ( k + 1 ) = B J x ( k ) + f J x^{(k+1)}=B_{J}x^{(k)}+f_{J} x(k+1)=BJx(k)+fJ

设线性方程组为 ∑ j = 1 n a i j x j = b i , ( i = 1 , 2 , . . . , n ) \sum\limits_{j=1}^{n}a_{ij}x_{j}=b_{i},(i=1,2,...,n) j=1naijxj=bi,(i=1,2,...,n),假设系数矩阵 A A A是非奇异矩阵,并且 a i i ≠ 0 , ( i = 1 , 2 , . . . , n ) a_{ii}\neq 0,(i=1,2,...,n) aii=0,(i=1,2,...,n),得到的等价形式:
x i = 1 a i i ( b i − ∑ j = 1 , j ≠ i n a i j x j ) x_{i}=\frac{1}{a_{ii}}(b_{i}-\sum\limits_{j=1,j\neq i}^{n}a_{ij}x_{j}) xi=aii1(bij=1,j=inaijxj)

取初始向量 x ( 0 ) = ( x 1 ( 0 ) , x 2 ( 0 ) , . . . , x n ( 0 ) ) T x^{(0)}=(x_{1}^{(0)},x_{2}^{(0)},...,x_{n}^{(0)})^{T} x(0)=(x1(0),x2(0),...,xn(0))T,使用迭代的方法可以得到迭代公式:
x i ( k + 1 ) = 1 a i i ( − ∑ j = 1 , j ≠ i n a i j x j ( k ) + b i ) x_{i}^{(k+1)}=\frac{1}{a_{ii}}(-\sum\limits_{j=1,j\neq i}^{n}a_{ij}x_{j}^{(k)}+b_{i}) xi(k+1)=aii1(j=1,j=inaijxj(k)+bi)

这就是雅克比迭代法。

2.2 高斯-赛德尔迭代法

雅克比迭代法是一种同步迭代的方法,它计算过程中不能够充分利用计算过程中的变量。所以对于雅克比迭代方法提出了一种改进的迭代方法,如果每一次计算出一个新的分量便立即用它取代对应的旧的变量进行迭代,可能收敛更快,故而这种方法称作高斯-赛德尔迭代方法,迭代的公式为:
x i ( k + 1 ) = 1 a i i ( − ∑ j = 1 i − 1 a i j x j ( k + 1 ) − ∑ j = i n a i j x j ( k ) + b i ) x_{i}^{(k+1)}=\frac{1}{a_{ii}}(-\sum\limits_{j=1}^{i-1}a_{ij}x_{j}^{(k+1)}-\sum\limits_{j=i}^{n}a_{ij}x_{j}^{(k)}+b_{i}) xi(k+1)=aii1(j=1i1aijxj(k+1)j=inaijxj(k)+bi)

依然将系数矩阵进行分解 A = D − L − U A=D-L-U A=DLU,将方程变为 D x = L x + U x + b Dx=Lx+Ux+b Dx=Lx+Ux+b。将新的分量代替旧分量,得到
D x ( k + 1 ) = L x ( k + 1 ) + U x ( k ) + b Dx^{(k+1)}=Lx^{(k+1)}+Ux^{(k)}+b Dx(k+1)=Lx(k+1)+Ux(k)+b

( D − L ) x ( k + 1 ) = U x ( k ) + b (D-L)x^{(k+1)}=Ux^{(k)}+b (DL)x(k+1)=Ux(k)+b

所以得到迭代表达式 x ( k + 1 ) = ( D − L ) − 1 U x ( k ) + ( D − L ) − 1 b x^{(k+1)}=(D-L)^{-1}Ux^{(k)}+(D-L)^{-1}b x(k+1)=(DL)1Ux(k)+(DL)1b,设系数 B G − S = ( D − L ) − 1 U B_{G-S}=(D-L)^{-1}U BGS=(DL)1U f G − S = ( D − L ) − 1 b f_{G-S}=(D-L)^{-1}b fGS=(DL)1b,所以得到
x ( k + 1 ) = B G − S x ( k ) + f G − S x^{(k+1)}=B_{G-S}x^{(k)}+f_{G-S} x(k+1)=BGSx(k)+fGS

2.3 超松弛迭代法

逐次超松弛迭代法可以看作带参数的 ω \omega ω的高斯-赛德尔迭代方法,是一种修正或加速,是求解大型稀疏矩阵的有效方法之一。
在高斯-赛德尔迭代方法中,由公式
x i ( k + 1 ) = 1 a i i ( − ∑ j = 1 i − 1 a i j x j ( k + 1 ) − ∑ j = i n a i j x j ( k ) + b i ) x_{i}^{(k+1)}=\frac{1}{a_{ii}}(-\sum\limits_{j=1}^{i-1}a_{ij}x_{j}^{(k+1)}-\sum\limits_{j=i}^{n}a_{ij}x_{j}^{(k)}+b_{i}) xi(k+1)=aii1(j=1i1aijxj(k+1)j=inaijxj(k)+bi)

得到
x i ( k + 1 ) = ( 1 − ω ) x i ( k ) + ω x i ( k + 1 ) = x i ( k ) + ω ( x i ( k + 1 ) − x i ( k ) ) x_{i}^{(k+1)}=(1-\omega)x_{i}^{(k)}+\omega x_{i}^{(k+1)}=x_{i}^{(k)}+\omega(x_{i}^{(k+1)}-x_{i}^{(k)}) xi(k+1)=(1ω)xi(k)+ωxi(k+1)=xi(k)+ω(xi(k+1)xi(k))

其中 ω \omega ω称作是松弛因子。
现在有两种构造超松弛迭代算法,即基于G-S迭代方法和Jacobi迭代方法:
A. 基于G-S迭代方法
{ x i ( k + 1 ) = x i ( k ) + Δ x i Δ x i = ω r i ( k + 1 ) r i ( k + 1 ) = 1 a i i ( − ∑ j = 1 i − 1 a i j x j ( k + 1 ) − ∑ j = i n a i j x j ( k ) + b i ) \begin{cases} x_{i}^{(k+1)}=x_{i}^{(k)}+\Delta x_{i}\\ \Delta x_{i}=\omega r_{i}^{(k+1)}\\ r_{i}^{(k+1)}=\frac{1}{a_{ii}}(-\sum\limits_{j=1}^{i-1}a_{ij}x_{j}^{(k+1)}-\sum\limits_{j=i}^{n}a_{ij}x_{j}^{(k)}+b_{i})\\ \end{cases} xi(k+1)=xi(k)+ΔxiΔxi=ωri(k+1)ri(k+1)=aii1(j=1i1aijxj(k+1)j=inaijxj(k)+bi)

显然 ω = 1 \omega=1 ω=1的时候,上式就是G-S迭代公式。
B. 基于Jacobi迭代方法
{ x i ( k + 1 ) = x i ( k ) + Δ x i Δ x i = ω r i ( k + 1 ) r i ( k + 1 ) = 1 a i i ( − ∑ j = 1 n a i j x j ( k ) + b i ) \begin{cases} x_{i}^{(k+1)}=x_{i}^{(k)}+\Delta x_{i}\\ \Delta x_{i}=\omega r_{i}^{(k+1)}\\ r_{i}^{(k+1)}=\frac{1}{a_{ii}}(-\sum\limits_{j=1}^{n}a_{ij}x_{j}^{(k)}+b_{i})\\ \end{cases} xi(k+1)=xi(k)+ΔxiΔxi=ωri(k+1)ri(k+1)=aii1(j=1naijxj(k)+bi)

一般地, ω \omega ω的取值决定着不同的松弛迭代方法: 0 < ω < 1 0<\omega<1 0<ω<1时候称作低松弛方法; ω = 1 \omega=1 ω=1时候为G-S方法或者是Jacobi方法; 1 < ω < 2 1<\omega<2 1<ω<2时候是(渐次)超松弛方法。

2.4 迭代方法的收敛性

一般地,有迭代收敛方法的基本定理:假设有迭代方程组 x = B x + f x=Bx+f x=Bx+f,对于任意的初始向量 x ( 0 ) x^{(0)} x(0),迭代公式 x ( k + 1 ) = B x ( k ) + f x^{(k+1)}=Bx^{(k)}+f x(k+1)=Bx(k)+f收敛的充要条件是迭代矩阵 B B B的谱半径 ρ ( B ) < 1 \rho(B)<1 ρ(B)<1
A x = b Ax=b Ax=b,如果 A A A是严格对角占优,则Jacobi迭代法、G-S迭代法均收敛。

对于迭代方法和直接求解的方法求解过程,以上具体代码可以参见笔者的github,上面有详细的计算过程。

小结

本小结详细介绍线性方程组的求解方法,在实际应用中应当合理利用这些方法来求解线性方程组来解决实际问题。

参考文献

[1] https://wenku.baidu.com/view/e1bca4b63968011ca2009117.html
[2] 计算方法,电子工业出版社

猜你喜欢

转载自blog.csdn.net/Zhang_Pro/article/details/105526907