数值积分与常微分方程初值问题数值计算

一.数值积分

常用解法:Newton-Cotes型求积公式。当节点为两个时为梯形公式,节点为三个时为Simpson公式

使用高阶NC公式时虽然精度较高,但是稳定性得不到保证,低阶则相反。为此我们将积分区间划分成等长的子区间,在每个子区间上使用低阶的NC公式进行计算,最后再求和得到近似值

复化梯形公式:我们知道\int_{a}^{b} f(x)dx= \sum_{k=0}^{n-1}\int_{x_k}^{x_{k+1}}f(x)dx,将后式的每一个积分值以中间值乘以区间长度代替:\frac{h}{2}[f(x_k)+f(x_{k+1})]便得到复化梯形公式

复化Simpson公式:在每个子区间上利用Simpson公式就有\sum_{k=0}^{n-1}\int_{x_k}^{x_{k+1}}f(x)dx\approx \sum_{k=0}^{n-1}\frac{h}{6}[f(x_k)+4f(\frac{x_k+x_{k+1}}{2})+f(x_{k+1})]

下面是一个简单的例子:我们通过公式\int_{0}^{1}\frac{4}{1+x^2}dx=\pi \pi的值进行估算


syms x

f=@(x)[4/(1+x^2)];

for i=1:10

  n=2^i;

h(1,i)=1/n;%h为步长,这里我们从0到1积分

x=linspace(0,1,n+1);%注意这里的n+1是节点数

Tn=0;

Sn=0;

error1(1,i)=0;

error2(1,i)=0;

for k=1:n

    Tn=Tn+(h(1,i)/2)*(f(x(1,k))+f(x(1,k+1)));

    Sn=Sn+(h(1,i)/6)*(f(x(1,k))+4*f((x(1,k)+x(1,k+1))/2)+f(x(1,k+1)));

end

fprintf('第%d次复化梯形公式计算结果为:%.10f\n',i,Tn);

error1(1,i)=abs(Tn-pi);

fprintf('误差为:%.10f\n',error1(1,i));

fprintf('第%d次复化Simpson公式计算结果为:%.10f\n',i,Sn);

error2(1,i)=abs(Sn-pi);

fprintf('误差为:%.10f\n',error2(1,i));

end

Romberg(0,1,f);

figure(1),plot(log(h),log(error1));

figure(2),plot(log(h),log(error2));



区间长度每进行一次便缩短一半,通过不断改变h的值可以得到两种方法误差与h之间的对数关系图


复化梯形


复化Simpson

理论上来说收敛阶分别为2阶和4阶,但第二个收敛速度明显快于4阶,可能是由于函数选取较特殊。同时,继续缩短步长h后出现了误差回弹的情况,这是因为我们在子区间上使用了低阶的Newton-Cotes公式来拟合积分值,当区间越来越多达到一个界限时,截断误差相比理论上精度的提高会起到主要影响(理论上来说越小越好),并且matlab中使用double型小数,并不能保证高精度的准确性。

Romberg算法:在进行过梯形的二等分求积之后再对每个区间各二等分一次,步长h不断缩短(在原来的分割基础上不断细化)递推关系式为

T_{2n}=\frac{1}{2}T_n+\frac{h}{2}\sum_{i=0}^{n-1}f(x_{i+\frac{1}{2}})

龙贝格算法又被称作变步长方法,我们直接使用矩阵来递推求解即可。需要注意的是,为了加速收敛,我们使用了Richardson外推加速法。精度设置为1e-10

function Romberg(a,b,f)

T(1,1)=(b-a)/2*(f(a)+f(b));

k=1;

epsilon=1e-10;

error3=1;

while(error3>epsilon)

    sum=0;

    for i=1:2^(k-1)

        sum=sum+f(a+(2*i-1)*(b-a)/2^k);

    end

    T(1,k+1)=0.5*T(1,k)+((b-a)/2^k)*sum;

    for m=1:k

        T(m+1,k+1)=4^m/(4^m-1)*T(m,k+1)-1/(4^m-1)*T(m,k);

    end

    k=k+1;

    error3=abs(T(k,k)-T(k-1,k-1));

end

fprintf('Romberg进行了%d次二等分,近似结果为%.10f,误差为%.11f',k-1,T(k,k),error3);


可以看到进行短短几次等分就能够达到极高的精度

二.常微分方程初值问题

对于常规一阶常微分方程组

f(x)=\left\{
\begin{aligned}
&y^\prime  =  f(x,y) \quad x\in D \\
&y(x_0)  = y_0 \\
\end{aligned}
\right.

我们考虑以Taylor展开为核心的Euler方法,它将一阶导数y^\prime(x_n)通过方程转化为f(x_n,y_n)

得到近似计算公式y_{n+1}=y_n+hf(x_n,y_n)

通过隐式方程改进的Euler公式

\left\{
\begin{aligned}
&\overline{y}_{n+1}=y_n+hf(x_n,y_n)\\
&y_{n+1}=y_n+\frac h2[f(x_n,y_n)+f(x_{n+1},\overline{y}_{n+1})
\end{aligned}
\right.

将斜率值加权组合,我们得到了更高精度的Runge-Kutta方法。根据参数个数的选取,可以得到不同阶的R-K迭代方程组,综合计算量与精度方面的考虑,我们一般使用四阶R-K公式:


图片来源于百度百科,自变量为t

来看一个具体例子

显式欧拉法

function Euler1(a,b,h)

n=(b-a)/h;

y(1,1)=0;

x(1,1)=a;

for k=1:n-1

    x(1,k+1)=a+k*h;

    y(1,k+1)=y(1,k)+h*g(x(1,k),y(1,k));

end

disp('近似解:');

disp(y);

ry=x.^(-2).*(exp(x)-exp(1));

disp('误差:');

disp(abs(y-ry));

plot(x,y,'r',x,ry,'g');


改进欧拉法

function Euler2(a,b,h)

n=(b-a)/h;

y(1,1)=0;

x(1,1)=a;

for k=1:n-1

    x(1,k+1)=a+k*h;

    yba(1,k+1)=y(1,k)+h*g(x(1,k),y(1,k));

    y(1,k+1)=y(1,k)+h/2*(g(x(1,k),y(1,k))+g(x(1,k+1),yba(1,k+1)));

end

disp('近似解:');

disp(y);

ry=x.^(-2).*(exp(x)-exp(1));

disp('误差:');

disp(abs(y-ry));

plot(x,y,'r',x,ry,'g');


R-K方法

function RK4(a,b,h)

n=(b-a)/h;

y(1,1)=0;

x(1,1)=a;

for k=1:n-1

    x(1,k+1)=a+k*h;

    k1=g(x(1,k),y(1,k));

    k2=g(x(1,k)+h/2,y(1,k)+h/2*k1);

    k3=g(x(1,k)+h/2,y(1,k)+h/2*k2);

    k4=g(x(1,k)+h,y(1,k)+h*k3);

    y(1,k+1)=y(1,k)+h/6*(k1+2*k2+2*k3+k4);

end

disp('近似解:');

disp(y);

ry=x.^(-2).*(exp(x)-exp(1));

disp('误差:');

disp(abs(y-ry));

plot(x,y,'r',x,ry,'g');


步长为0.1


步长为0.025



步长为0.1


步长为0.05


曲线几乎完全重合




精度达到1e-5


其中前两个方法均为1阶精度,RK为4阶精度,RK在这个例子中的精度也是最高的

经过对1<x<b中b的取值调整(固定步长为0.1),b在<=20时精度都压缩在1e-2内,但b=30时靠近b的x拟合的误差便达到了三位数,b=35时误差为1e+4级,而b=40后误差达到了1e+6级

分析图像我们猜测这可能是y的取值过大造成的误差,因为截断误差允许的精度范围并不大

查阅资料后发现理论来讲RK算法存在稳定域


实时 RK 算法的稳定性分析——黄振全等

由此我们知道确实存在这样一个临界步长使得算法不稳定。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容