【数值分析】解线性方程组的直接解法

前言

解基础线性方程组并不困难,基础方法在小学时就以鸡兔同笼问题呈现过了。但当线性方程组的量级扩大到上万倍时,就需要计算机参与运算了。本文介绍常见的三种方法:高斯消去法,列主元消去法,直接三角分解法

方法

高斯消去法

求解公式

此方法与小学时学习的消元法无异,核心思想就是消元
{ a 11 x 1 + a 12 x 2 ⋯ + a 1 n x n = b 1 a 21 x 1 + a 22 x 2 ⋯ + a 2 n x n = b 2 ⋮ a n 1 x 1 + a n 2 x 2 ⋯ + a n n x n = b n \left \{ \begin{array}{c} a_{11}x_1+a_{12}x_2\cdots+a_{1n}x_n=b_1 \\ a_{21}x_1+a_{22}x_2\cdots+a_{2n}x_n=b_2 \\ \vdots \\ a_{n1}x_1+a_{n2}x_2\cdots+a_{nn}x_n=b_n \end{array} \right. a11x1+a12x2+a1nxn=b1a21x1+a22x2+a2nxn=b2an1x1+an2x2+annxn=bn

可以写为矩阵形式
( a 11 ⋯ a 1 n ⋮ ⋱ ⋮ a n 1 ⋯ a n n ) ( x 1 x 2 ⋮ x n ) = ( b 1 b 2 ⋮ b n ) \begin{pmatrix} a_{11} & \cdots & a_{1n} \\ \vdots & \ddots & \vdots \\ a_{n1} & \cdots & a_{nn} \end{pmatrix} \begin{pmatrix} x_{1} \\ x_{2} \\ \vdots \\ x_{n} \end{pmatrix} =\begin{pmatrix} b_{1} \\ b_{2} \\ \vdots \\ b_{n} \end{pmatrix} a11an1a1nannx1x2xn=b1b2bn

简记为 A x = b Ax=b Ax=b

经过n次消元可得
( a 11 ( 1 ) ⋯ a 1 n ( 1 ) ⋱ ⋮ a n n ( n ) ) ( x 1 x 2 ⋮ x n ) = ( b 1 ( 1 ) b 2 ( 2 ) ⋮ b n ( n ) ) \begin{pmatrix} a_{11}^{(1)} & \cdots & a_{1n}^{(1)} \\ & \ddots & \vdots \\ & & a_{nn}^{(n)} \end{pmatrix} \begin{pmatrix} x_{1} \\ x_{2} \\ \vdots \\ x_{n} \end{pmatrix} =\begin{pmatrix} b_{1}^{(1)} \\ b_{2}^{(2)} \\ \vdots \\ b_{n}^{(n)} \end{pmatrix} a11(1)a1n(1)ann(n)x1x2xn=b1(1)b2(2)bn(n)

得求解公式
{ x n = b n ( n ) / a n n ( n ) , x k = ( b k ( k ) − ∑ j = k + 1 n a k j ( k ) x j ) / a k k ( k ) , k = n − 1 , n − 2 ⋯   , 1. \left \{ \begin{array}{c} x_n=b_n^{(n)}/a_{nn}^{(n)},\\ x_k=(b_k^{(k)}-\sum_{j=k+1}^na_{kj}^{(k)}x_j)/a_{kk}^{(k)}, k=n-1,n-2\cdots,1. \end{array} \right. {xn=bn(n)/ann(n),xk=(bk(k)j=k+1nakj(k)xj)/akk(k),k=n1,n2,1.

代码

main.m

clear;clc
A=input('(A,b)=');
n=size(A,1);
for i=1:n
    for j=i+1:n
        A(j,:)=-A(j,i)/A(i,i)*A(i,:)+A(j,:);
    end
    disp(['(A(' num2str(i) '),b)=']);
    disp(A);
end

x(n,1)=0;
x(n)=A(n,n+1)/A(n,n);
for i=size(A,1)-1:-1:1
    x(i)=((A(i,n+1))-f_sum(A,x,i,n))/A(i,i);
end

disp(x);

f_sum.m


function sum=f_sum(A,x,i,n)
sum=0;
for j=i+1:n
    sum=sum+A(i,j)*x(j);
end

缺点

  • 该算法时间复杂度较高,不适合规模大的计算
  • 在主元为绝对值较小的浮点数时结果误差较大,不适合精度要求高的运算
  • 出现主元为0的情况无法计算

列主元消去法

求解公式

如上文所提到的,高斯消去法在主元为较小的浮点数时会出现较大误差
此时可以引入新方法,即列主元消去法
为避免消元过程中除去小主元所带来的误差,在每次消元前将本列绝对值最大的主元行与本行对换

代码

main.m

clear;clc
A=input('(A,b)=');
n=size(A,1);

for i=1:n
    [p,]=find(A(:,i)==max(A(i:n,i)),1);
    A([i p],:)=A([p i],:);
    for j=i+1:n
        A(j,:)=-A(j,i)/A(i,i)*A(i,:)+A(j,:);
    end
    disp(['(A(' num2str(i) '),b)=']);
    disp(A);
end

x(n,1)=0;
x(n)=A(n,n+1)/A(n,n);
for i=size(A,1)-1:-1:1
    x(i)=((A(i,n+1))-f_sum(A,x,i,n))/A(i,i);
end

disp(x);

f_sum.m

function sum=f_sum(A,x,i,n)
sum=0;
for j=i+1:n
    sum=sum+A(i,j)*x(j);
end

解析

其中,寻找绝对值最大主元代码如下:

 [p,]=find(A(:,i)==max(A(i:n,i)),1);

对于find()函数,常见用法如下:

i = find(X)
i = find(X, k) % 返回前k个不为零的元素索引值
i = find(X, k, 'first') % 返回前k个不为零的元素索引值
i = find(X, k, 'last') % 返回后k个不为零的元素索引值
[r,c] = find(X, ...) % 返回矩阵X中非零元素的行和列的索引值
[r,c,v] = find(X, ...) % 返回矩阵X中非零元素的行和列的索引值以及该不为零元素

这里需要获取最大值所在第几行的值,所以只需要取[r,c]的第一个值,第二个留空即可
如果有多个条件满足find,该函数会返回一个向量,这里只需要第一个最大值,所以在find中加了参数,即find(A,1)

这里行对换的代码如下:

A([i p],:)=A([p i],:);

列对换同理,不再赘述

直接三角分解法

求解公式

对于系数矩阵 A A A,在其为非奇异矩阵的情况下,有分解式
A = L U A=LU A=LU
其中 L L L为单位下下三角矩阵, U U U为上三角矩阵
A = ( 1 l 21 1 ⋮ ⋮ ⋱ l n 1 l n 2 ⋯ 1 ) ( u 11 u 12 ⋯ u 1 n u 22 ⋯ u 2 n ⋱ ⋮ u n n ) A=\begin{pmatrix} 1 & & & \\ l_{21} &1 & &\\ \vdots & \vdots & \ddots & \\ l_{n1} & l_{n2} & \cdots & 1 \end{pmatrix} \begin{pmatrix} u_{11} &u_{12} &\cdots & u_{1n}\\ &u_{22} &\cdots &u_{2n}\\ & & \ddots & \vdots \\ && & u_{nn} \end{pmatrix} A=1l21ln11ln21u11u12u22u1nu2nunn

可以得到计算公式
(1)
u 1 i = a 1 i , ( i = 1 , 2 ⋯   , n ) , l i 1 = a i 1 / u 11 , ( i = 2 , 3 ⋯   , n ) . u_{1i}=a_{1i},(i=1,2\cdots ,n), \\ l_{i1}=a_{i1}/u_{11},(i=2,3\cdots ,n). u1i=a1i,(i=1,2,n)li1=ai1/u11,(i=2,3,n).
计算 U U U的第 r r r行, L L L的第 r r r列元素 ( r = 2 , 3 ⋯   , n ) (r=2,3\cdots ,n) (r=2,3,n)
(2)
u r i = a r i − ∑ k = 1 r − 1 l r k u k , i = r , r + 1 , ⋯   , n ; u_{ri}=a_{ri}-\sum_{k=1}^{r-1}l_{rk}u_k,i=r,r+1,\cdots ,n; \\ uri=arik=1r1lrkuk,i=r,r+1,,n;
(3)
l i r = ( a i r − ∑ k = 1 i − 1 l i k u k r / u r r , i = r + 1 , ⋯   , n , 且 r ≠ n . l_{ir}=(a_{ir}-\sum_{k=1}^{i-1}l_{ik}u_{kr}/u_{rr},i=r+1,\cdots ,n,且r\neq n. lir=(airk=1i1likukr/urr,i=r+1,,n,r=n.

求解 L y = b Ly=b Ly=b U x = y Ux=y Ux=y
(4)
{ y 1 = b 1 , y i = b i − ∑ k = 1 i − 1 l i k y k , i = 2 , 3 , ⋯   , n ; \left \{ \begin{array}{c} y_1=b_1, \\ y_i=b_i-\sum_{k=1}^{i-1}l_{ik}y_k,i=2,3,\cdots,n; \end{array} \right. {y1=b1,yi=bik=1i1likyk,i=2,3,,n;
(5)
{ x n = y n / u n n , x i = ( y i − ∑ k = i + 1 n i i k x k ) / i i i , i = n − 1 , n − 2 , ⋯   , 1 ; \left \{ \begin{array}{c} x_n=y_n/u_{nn}, \\ x_i=(y_i-\sum_{k=i+1}^ni_{ik}x_k)/i_{ii},i=n-1,n-2,\cdots ,1; \end{array} \right. {xn=yn/unn,xi=(yik=i+1niikxk)/iii,i=n1,n2,,1;

代码

main.m

clear;clc

%% input
A=input('A=');
b=input('b=');
n=size(A,1);

%%l
for i=1:n
    l(i,i)=1;
end

%% u1i lil
u(1,:)=A(1,:);
l(2:n,1)=A(2:n,1)./u(1,1);

%% uri lir
for r=2:n
    for i=r:n
        u(r,i)=A(r,i)-f_sum1(l,u,r,i);
        if i==r
            continue
        end
        l(i,r)=(A(i,r)-f_sum2(l,u,r,i))/u(r,r); 
    end
end

%% y
y(1)=b(1);
for i=2:n
    y(i)=b(i)-f_sum3(l,y,i);
end

%% x
x(n)=y(n)/u(n,n);
for i=n-1:-1:1
    x(i)=(y(i)-f_sum4(u,x,i,n))/u(i,i);
end

%% output
x

f_sum1.m

function sum=f_sum1(l,u,r,i)
sum=0;
for k=1:r-1
    sum=sum+l(r,k)*u(k,i);
end

f_sum2.m

function sum=f_sum2(l,u,r,i)
sum=0;
for k=1:r-1
    sum=sum+l(i,k)*u(k,r);
end

f_sum3.m

function sum=f_sum3(l,y,i)
sum=0;
for k=1:i-1
    sum=sum+l(i,k)*y(k);
end

f_sum4.m

function sum=f_sum4(u,x,i,n)
sum=0;
for k=i+1:n
    sum=sum+u(i,k)*x(k);
end

仓库地址

本文所有涉及的代码均已上传至GitHub
仓库地址

你可能感兴趣的:(数学,matlab,线性代数,线性代数,matlab)