解基础线性方程组并不困难,基础方法在小学时就以鸡兔同笼问题呈现过了。但当线性方程组的量级扩大到上万倍时,就需要计算机参与运算了。本文介绍常见的三种方法:高斯消去法,列主元消去法,直接三角分解法
此方法与小学时学习的消元法无异,核心思想就是消元
{ 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=b2⋮an1x1+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} ⎝⎜⎛a11⋮an1⋯⋱⋯a1n⋮ann⎠⎟⎞⎝⎜⎜⎜⎛x1x2⋮xn⎠⎟⎟⎟⎞=⎝⎜⎜⎜⎛b1b2⋮bn⎠⎟⎟⎟⎞
简记为 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)⎠⎟⎟⎞⎝⎜⎜⎜⎛x1x2⋮xn⎠⎟⎟⎟⎞=⎝⎜⎜⎜⎜⎛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=n−1,n−2⋯,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
如上文所提到的,高斯消去法在主元为较小的浮点数时会出现较大误差
此时可以引入新方法,即列主元消去法
为避免消元过程中除去小主元所带来的误差,在每次消元前将本列绝对值最大的主元行与本行对换
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=⎝⎜⎜⎜⎛1l21⋮ln11⋮ln2⋱⋯1⎠⎟⎟⎟⎞⎝⎜⎜⎜⎛u11u12u22⋯⋯⋱u1nu2n⋮unn⎠⎟⎟⎟⎞
可以得到计算公式
(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=ari−k=1∑r−1lrkuk,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=(air−k=1∑i−1likukr/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=bi−∑k=1i−1likyk,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=(yi−∑k=i+1niikxk)/iii,i=n−1,n−2,⋯,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
仓库地址