【笔记】常微分方程(2)

欧拉法

【笔记】常微分方程(2)_第1张图片

% 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都是在此基础上的改进。

【笔记】常微分方程(2)_第2张图片

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

其所得图形见下图。

【笔记】常微分方程(2)_第3张图片

注:本问题可进一步利用四阶Runge-Kutta法求解。

刚性方程组

【笔记】常微分方程(2)_第4张图片

      解:先写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.

可见计算速度太慢。

【笔记】常微分方程(2)_第5张图片

      这个问题的特点是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.

可见计算速度大大提高了。

导弹系统的改进

   【笔记】常微分方程(2)_第6张图片

例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)_第7张图片

      (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');

【笔记】常微分方程(2)_第8张图片

上图中曲线上方为打击范围。由于θ=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)。

      应该说,三种算法各有千秋。在线算法灵活,容易调整参数和模型,但速度慢。

离线算法事先计算好,实时使用查询方式,不需计算,速度极快。模拟算法比较直观、生动。

【笔记】常微分方程(2)_第9张图片

你可能感兴趣的:(笔记)