【笔记】常微分方程(2)
欧拉法
% M函数euler.m
function[t,y]=euler(odefun,tspan,y0,h)
t=tspan(1):h:tspan(2);
y(1)=y0;
for i=1:length(t)-1
y(i+1)=y(i)+h*feval(odefun,t(i),y(i));
end
t=t';
y=y';
先保存euler.m,再在命令窗口中执行:
>> odefun=inline('y-2*t/y','t','y');
>> [t,y]=euler(odefun,[0,4],1,0.01);
>> n=length(t);
>> e=sqrt(sum((sqrt(1+2*t)-y).^2)/n);
>> [n,e]
ans =
401.0000 0.3209
欧拉法只有一阶精度,所以实际应用效率比较差,只能解低精度问题。ode23,ode45都是在此基础上的改进。
clear
f=sym('y+2*x/y^2');
a=0;
b=2;
h=0.4;
n=(b-a)/h+1;
x=0;
y=1;
szj=[x,y];
for i=1:n-1
y=y+h*subs(f,{'x','y'},{x,y});
x=x+h;
szj=[szj;x,y];
end
szj
plot(szj(:,1),szj(:,2))
数据结构为:
szj =
0 1.0000
0.4000 1.4000
0.8000 2.1233
1.2000 3.1145
1.6000 4.4593
2.0000 6.3074
其所得图形见下图。
注:本问题可进一步利用四阶Runge-Kutta法求解。
刚性方程组
解:先写M函数eg2fun.m:
% M函数eg2fun.m
function f=eg2fun(t,y)
f(1)=-0.01*y(1)-99.99*y(2);
f(2)=-100*y(2);
% 保证f输出时是列向量
f=f(:);
若用ode45求解有:
>> clear;tic
>> [t,y]=ode45(@eg2fun,[0,400],[2,1]);
>> toc,plot(t,y);
Elapsed time is 60.810675 seconds.
可见计算速度太慢。
这个问题的特点是y2下降很快,而y1下降太慢。一方面,由于y2下降太快,为了保证数据稳定性,步长h需足够小;另一方面,由于y1下降太慢,为了反映解的完整性,时间区间需足够长,这就造成计算量太大。这类方程组称为刚性方程组或病态方程组。ode45不适用于病态方程组,下面用ode45s求解。
>> clear;tic;
>> [t,y]=ode15s(@eg2fun,[0,400],[2,1]);
>> toc,plot(t,y);
Elapsed time is 8.816475 seconds.
可见计算速度大大提高了。
导弹系统的改进
例3 在导弹系统中设a=90km/h,b=450km/h,T=0.1h。现要求d,θ的有效范围。
解:有两个极端情形容易算出。若θ=0,即敌舰正好背向行驶,即x轴正向。那么导弹直线飞行,击中时间为
t=d/(b-a)<T
得d=T(b-a)=36km。若θ=pi,即迎面驶来,类似有d=T(b+a)=54km。一般地,应有36<d<54。下面我们考虑三种算法解例3。
(1) 在线算法:写M函数eg3fun.m。为了防止分母为0,加了一个小正数1e-8。并且使用附加参数a,b,d,theta传递。
% eg3fun.m
function dy=missilefun(t,y,a,b,d,theta)
dydx=(a*t*sin(theta)-y(2)+1e-8)/...
(abs(d+a*t*cos(theta)-y(1))+1e-8);
dy(1)=b/(1+dydx^2)^0.5;
dy(2)=b/(1+dydx^(-2))^0.5;
dy=dy(:);
在指令窗口中执行:
>> clear
>> close
>> a=90;
>> b=450;
>> d=50;
>> theta=pi/2;
>> [t,y]=ode45(@eg3fun,[0,0.1],[0,0],[],a,b,d,theta);
>> plot(y(:,1),y(:,2));
>> max(y(:,1)-d-a*t*cos(theta))
ans =
-5.7410
由于在T=0.1h内,式x(t)>=d+at cos(theta)不成立,所以敌舰不在有效打击范围,应等近一些再发射。图形见下图。
(2) 离线算法:首先对于所有可能的d和θ,计算击中所需时间,从而对不同θ,得d的临界值。具体应用时直接查表判断。编写m脚本文件eg3_2.m。
clear;
close;
a=90;
b=450;
d=50;
theta=pi/2;
i=1;
for d=54:-1:36
for theta=0:0.1:pi
[t,y]=ode45(@eg3fun,[0,0.1],[0,0],[],a,b,d,theta);
if max(y(:,1)-d-a*t*cos(theta))>0,
range(i,:)=[d,theta];
i=i+1;
break;
end
end
end
figure;
plot(range(:,1),range(:,2));
xlabel('d');
ylabel('theta');
上图中曲线上方为打击范围。由于θ=1.57,d=50在曲线下方,这样即可知不在打击范围内。
(3) 计算机模拟:一个较基本但形象的方法。对于任意选定的参数a,b,d,θ,T下面的M函数提供一个导弹追击敌舰演示工具。其中使用了MATLAB动画制作指令getframe和动画播放指令movie。
% M函数eg3_3fun.m
function m=eg3_3fun(a,b,d,theta,T)
[t,y]=ode45(@eg3fun,[0,T],[0,0],[],a,b,d,theta);
x=[d+a*t*cos(theta),a*t*sin(theta)];
n=length(t);
j=n;
for i=1:n
plot(x(i,1),x(i,2),'o',y(i,1),y(i,2),'r.');
axis([0 max(x(:,1)) 0 max(x(:,2))]);
hold on;
m(i)=getframe;
if y(i,1)>=x(i,1),
j=i;
break;
end
end
hold off;
movie(m);
legend('敌舰','导弹',2);
if j<n,
hold on;
plot(y(j,1),y(j,2),'rh','markersize',18);
hold off;
title(['导弹将在第',num2str(t(j)),'小时击中敌舰']);
else
title(['导弹在',num2str(T),'小时内不能击中敌舰']);
end
对于敌舰速度a=90km/h,导弹速度b=450km/h,距离d=30km,敌舰行驶角度θ=0.3π,反应时间T=0.1h,在指令窗口执行
>> eg3_3fun(90,450,30,0.3*pi,0.1);
得动画。可见导弹约在t=0.08时击中敌舰,位置约在(34,5.5)。
应该说,三种算法各有千秋。在线算法灵活,容易调整参数和模型,但速度慢。
离线算法事先计算好,实时使用查询方式,不需计算,速度极快。模拟算法比较直观、生动。