数值计算是工科专业经常接触到的课程,介绍的算法包括代数方程组求解、函数插值、数值积分与微分、常微分方程求解。本文不对算法原理进行介绍,读者可参考应用数学、数值分析、工程数学等教材。(能找过来的同学估计都是上过课的)
线性方程组:Gauss消元方法(列主元素),追赶法,迭代法
非线性方程组:不动点法、牛顿迭代法
function [x] = myGauss(A,B)
% Gauss消元方法(列主元素)求解方程组
% 此处显示详细说明
% A 系数矩阵
% B b向量
[n1,n2]=size(A);
n=length(B);
if (n1~=n || n2~=n)
error("参数输入有误");
end
if isrow(B)
B=B';
end
disp('增广矩阵A|B为:');
disp([A,B]);
x=zeros(n,1);
for i=1:n-1
[~,m_index]=max(abs(A([i:n],i)));
m_index=m_index+i-1;
if (i~=m_index)
A([i,m_index],:)=A([m_index,i],:);
B([i,m_index])=B([m_index,i]);
disp(['交换第' num2str(i) '和第' num2str(m_index) '行:']);
disp([A,B]);
end
for j=i+1:n
l=A(j,i)/A(i,i);
A(j,:)=A(j,:)-A(i,:)*l;
B(j)=B(j)-B(i)*l;
end
disp(['第' num2str(i) '次消元:']);
disp([A,B]);
end
%回代
x(n)=B(n)/A(n,n);
for i=n-1:-1:1
sum=0;
for k=i+1:n
sum=sum+A(i,k)*x(k);
end
x(i)=(B(i)-sum)/A(i,i);
end
disp('方程组的解为:');
disp(x);
end
输入参数a,b,c,f分别为上图中的ai,bi,ci,fi写成数组形式
function [x] = zhuiganfa(a,b,c,f)
% 追赶法解三对角方程组
% a,b,c皆为行向量
na=length(a);
nb=length(b);
nc=length(c);
n=length(f);
if (any([na+1,nb,nc+1,n]-n))
error("参数输入有误");
end
a=[0 a];
A=zeros(n,n);
for i=1:n
A(i,i)=b(i);
end
for i=1:n-1
A(i+1,i)=a(i+1);
A(i,i+1)=c(i);
end
disp('方程组增广矩阵为:');
disp([A,f']);
l=zeros(1,n);
y=zeros(1,n);
x=zeros(1,n);
l(2)=a(2)/b(1);
for i=3:n
l(i)=a(i)/(b(i-1)-c(i-2)*l(i-1));
end
disp(['l2 … l' num2str(n) '分别为:']);
disp(l(2:n));
y(1)=f(1);
for i=2:n
y(i)=f(i)-l(i)*y(i-1);
end
disp(['y1 … y' num2str(n) '分别为:']);
disp(l([2:n]));
x(n)=y(n)/(b(n)-c(n-1)*l(n));
for i=n-1:-1:2
x(i)=(y(i)-c(i)*x(i+1))/(b(i)-c(i-1)*l(i));
end
x(1)=(y(1)-c(1)*x(2))/b(1);
disp('方程组的解为:');
disp(x);
end
包括Doolittle分解,Crout分解,LDU分解
function [L,D,U]=LDU(A,t)
% 对矩阵A进行分解
% t参数对应'Doo'、'Cro'、'LDU',默认Doolittle分解
if nargin<2
t='Doo';
end
b=size(A);
n=b(1);
if b(1)~=b(2)
error('A不是方阵');
end
if n~=rank(A)
error('A不满秩');
end
%初始化
L=eye(n,n);
D=zeros(n,n);
U=zeros(n,n);
for k=1:n
for j=k:n
sum_j=0;
sum_i=0;
for r=1:k-1
sum_j=sum_j+L(k,r)*U(r,j);
sum_i=sum_i+L(j,r)*U(r,k);
end
U(k,j)=A(k,j)-sum_j;
L(j,k)=(A(j,k)-sum_i)/U(k,k);
end
D(k,k)=U(k,k);
end
L_1=L*D; % Crout分解的L,下三角矩阵
U_1=D^(-1)*U; % Crout分解的U,单位上三角矩阵
if strcmp(t,'Doo')
disp('矩阵的Doolittle分解:');
disp(' L='); disp(L);
disp(' U='); disp(U);
D=U;
elseif strcmp(t,'Cro')
disp('矩阵的Crout分解:');
disp(' L='); disp(L_1);
disp(' U='); disp(U_1);
L=L_1;
D=U_1;
elseif strcmp(t,'LDU')
disp('矩阵的LDU分解:');
disp(' L='); disp(L);
disp(' D='); disp(D);
disp(' U='); disp(U_1);
U=U_1;
end
包括Jacobi法、Gauss-Seidel法、SOR法
function [x] = diedai(A,b,x0,s,e)
% 迭代法求解线性方程组
% A为方阵,b为列向量,x0为初始向量
% s取1、2、3分别对应Jacobi法、Gauss-Seidel法、SOR法
% e 求解精度,默认0.0001
if nargin < 5
e=0.0001;
if nargin < 4
s=1;
end
end
if isrow(b)
b=b';
end
format long
[m,n]=size(A);
n1=length(b);
if m~=n || n1~=n
error("输入参数维度有误");
end
% format long;
D=zeros(n);
L=zeros(n);
U=zeros(n);
for i=1:n
D(i,i)=A(i,i);
for j=i+1:n
U(i,j)=-A(i,j);
L(j,i)=-A(j,i);
end
end
if s==1
M=D^(-1)*(L+U);
f=D^(-1)*b;
elseif s==2
M=(D-L)^(-1)*U;
f=(D-L)^(-1)*b;
elseif s==3
w=input('请输入松弛因子w:');
M=(D-w*L)^(-1)*((1-w)*D+w*U);
f=w*(D-w*L)^(-1)*b;
else
warning('指定算法参数有误,已使用Jacobi迭代法求解')
M=D^(-1)*(L+U);
f=D^(-1)*b;
end
N=1; %迭代次数
d=1; %精度
N0=30; %最大迭代次数
while N<=N0 && d>e
x=M*x0+f;
disp(['第',num2str(N),'次迭代近似解']);
disp(sprintf('%.5f ',x')); %#ok<DSPS>
d=max(abs(x-x0));
N=N+1;
x0=x;
end
end
function [x] = budongdian(G,x,x0,e)
% 不动点求解非线性方程组
% G 不动点映射(sym类型)
% x 待求解变量
if nargin<4
e=0.0001;
end
x=sym(x);
N=1; %迭代次数
N0=30; %最大迭代次数
d=1;
format long
while N<=N0 && d>e
x1=vpa(subs(G,x,x0));
disp(['第',num2str(N),'次迭代近似解']);
disp(sprintf('%.8f',x1)); %#ok<DSPS>
d=norm(x1-x0);
N=N+1;
x0=x1;
end
x=double(x1);
end
function [x] = Newton1(f,x,x0,e)
% Newton迭代法求解一维非线性函数零点
% f 待求解函数(sym类型)
% x 待求解变量
if nargin<4
e=0.0001;
end
x=sym(x);
df=diff(f,x);
N=1; %迭代次数
N0=30; %最大迭代次数
d=1; %精度
format long
while N<=N0 && d>e
x1=x0-vpa(subs(f,x,x0)/subs(df,x,x0));
disp(['第',num2str(N),'次迭代近似解']);
disp(sprintf('%.8f',x1)); %#ok<DSPS>
d=norm(x1-x0);
N=N+1;
x0=x1;
end
x=double(x1);
end
function [y,C,R] = Newton(x,X,Y,n)
%求x处牛顿插值结果y、差商表、其误差估计
%输入:X是n+1个节点横坐标向量,Y是纵坐标向量
%输出:C为差商表,R为估计误差f~(n+1)(x)的系数
%format long
xn=length(X);yn=length(Y);
if xn~=yn
error("输入节点错误");
else
if nargin < 4
n=xn-1;
end
if xn < n+1
error("输入节点数量错误");
end
end
C=zeros(xn,xn+1);
if isrow(X)
C(:,1) = X';
C(:,2) = Y';
else
C(:,1) = X;
C(:,2) = Y;
end
for j=3:xn+1
for i=j-1:xn
C(i,j)=( C(i,j-1)-C(i-1,j-1) )/( C(i,1)-C(i+2-j,1) );
end
end
fprintf(' x f(x) ');
for g=1:n
fprintf('%d阶差商 ',g);
end
fprintf('\n');
disp(C);
y=0;
for k=1:n+1
w=1;
for m=1:k-1
w=w*(x-C(m,1));
end
y=y+C(k,k+1)*w;
end
%误差估计
w=1;
for i=1:xn
w=w*(x-X(i));
end
R=w/factorial(n+1);
function [result] = fuhuajifen(f, x_Low, x_Up, n, a)
% 数值积分。a=1(默认):复化梯形 a=2:复化 Simpson
% f :被积函数句柄
% x_Low:积分区间下界
% x_Up :积分区间上界
% n :等分数量
if nargin < 5
a=1;
if nargin < 4
error('参数过少')
end
end
h = (x_Up - x_Low)/n;
result = 0;
if a==1
for i = 1:n
result = result + h/2*(f(x_Low+h*(i-1))+f(x_Low+h*i));
end
elseif a==2
for i = 1:n
result = result + h/6*(f(x_Low+h*(i-1))+4*f(x_Low+h*(i-1)+h/2)+f(x_Low+h*i));
end
else
error('选择算法序号有误')
end
end
function [result] = Romberg(f, x_Low, x_Up, e)
% Romberg 数值积分
% f :被积函数句柄
% x_Low:积分区间下界
% x_Up :积分区间上界
% e :精度(默认0.0001)
if nargin < 4
e=0.0001;
if nargin < 3
error('参数过少')
end
end
c=10; %Romberg算法过程表的初始行数目
R=zeros(c,4);
for k=0:4
n=2^k;
h = (x_Up - x_Low)/n;
T=0;
for i = 1:n
T = T + h/2*(f(x_Low+h*(i-1))+f(x_Low+h*i));
end
R(k+1,1)=T;
end
for j=2:4
a=4^(j-1);
for i=1:(6-j)
R(i,j)=(a*R(i+1,j-1)-R(i,j-1))/(a-1);
end
end
ee=R(2,4)-R(1,4);
while ee>e
k=k+1;
n=2^k;
h = (x_Up - x_Low)/n;
T=0;
for i = 1:n
T = T + h/2*(f(x_Low+h*(i-1))+f(x_Low+h*i));
end
R(k+1,1)=T;
for j=2:4
a=4^(j-1);
R(k-j+2,j)=(a*R(k-j+3,j-1)-R(k-j+2,j-1))/(a-1);
end
ee=R(k-2,4)-R(k-3,4);
end
disp(' T S C R');
disp(R(1:k+1,:))
result=R(k-2,4);
function [y] = myEuler(f, x0, y0, x)
% 改进Euler法
% f :微分方程函数句柄 y'=f(x,y)
% x0:初始x值 x0
% y0:初始y值 y(x0)
% x :待求x值,可以为数组
if nargin < 4
error("输入参数太少");
end
x=sort(x);
if x0 > min(x)
[xx,index] = sort([x0,x]);
d=find(index==1);
xx1=fliplr(xx(1:d));
[yy1] = fEuler(f, xx1, y0);
yy1=fliplr(yy1);
xx2=xx(d:end);
[yy2] = fEuler(f, xx2, y0);
y=[yy1(1:end-1),yy2(2:end)];
disp(x); disp(y);
else
xx3=[x0,x];
[yy3] = fEuler(f, xx3, y0);
y=yy3(2:end);
disp(x); disp(y);
end
end
function [yyn] = fEuler(ff, xxn, y00)
% 子函数
l=length(xxn);
yyn=zeros(1,l); yyn(1)=y00;
for i=2:l
h=xxn(i)-xxn(i-1);
yb=yyn(i-1)+h*ff(xxn(i-1),yyn(i-1));
yyn(i) = yyn(i-1)+h/2*(ff(xxn(i-1),yyn(i-1))+ff(xxn(i),yb));
end
end
function [x, y] = RungeKutta4(f, x0, y0, x)
% 四阶 Runge-Kutta 法解一阶常微分方程
% f :微分方程函数句柄 y'=f(x,y)
% x0:初始x值 x0
% y0:初始y值 y(x0)
% x :待求x值,可以为数组
if nargin < 4
error("输入参数太少");
end
x=sort(x);
if x0 > min(x)
[xx,index] = sort([x0,x]);
d=find(index==1);
xx1=fliplr(xx(1:d));
[yy1] = fRK(f, xx1, y0);
yy1=fliplr(yy1);
xx2=xx(d:end);
[yy2] = fRK(f, xx2, y0);
y=[yy1(1:end-1),yy2(2:end)];
disp(x); disp(y);
else
xx3=[x0,x];
[yy3] = fRK(f, xx3, y0);
y=yy3(2:end);
disp(x); disp(y);
end
end
function [yyn] = fRK(ff, xxn, y00)
% 子函数
l=length(xxn);
yyn=zeros(1,l); yyn(1)=y00;
for i=1:l-1
h=xxn(i+1)-xxn(i);
k1 = ff(xxn(i), yyn(i));
k2 = ff(xxn(i)+0.5*h, yyn(i)+k1*h/2);
k3 = ff(xxn(i)+0.5*h, yyn(i)+k2*h/2);
k4 = ff(xxn(i)+h, yyn(i)+ k3*h);
yyn(i+1) = yyn(i)+h*(k1+2*k2+2*k3+k4)/6;
end
end