龙格——库塔(Rungekutta)法求解常微分方程

1.龙格库塔法的基本原理

该算法是构建在数学支持的基础之上的。对于一阶精度的拉格朗日中值定理有:

对于微分方程

y'=f(x,y)
y(n+1)=y(n)+h*K1
K1=f(xn,yn)
当用点xn处的斜率近似值K1与右端点xn+1处的斜率K2的算术平均值作为平均斜率K*的近似值,那么就会得到二阶精度的改进拉格朗日中值定理:
y(n+1)=y(n)+[h*( K1+ K2)/2]
K1=f(xn,yn)
K2=f(x(n)+h,y(n)+h*K1)
依次类推,如果在区间[xn,xn+1]内多预估几个点上的斜率值K1、K2、……Km,并用他们的加权平均数作为平均斜率K*的近似值,显然能构造出具有很高精度的高阶计算公式。经数学推导、求解,可以得出四阶龙格-库塔公式,也就是在工程中应用广泛的经典龙格-库塔算法:
y(n+1)=y(n)+h*( K1+ 2*K2 +2*K3+ K4)/6
K1=f(x(n),y(n))
K2=f(x(n)+h/2,y(n)+h*K1/2)
K3=f(x(n)+h/2,y(n)+h*K2/2)
K4=f(x(n)+h,y(n)+h*K3)

注:

通常所说的龙格-库塔法是指四阶而言的,我们可以仿二阶、三阶的情形推导出常用的标准四阶龙格-库塔法公式

2.龙格-库塔(Runge-Kutta)方法


经典四阶法

在各种龙格-库塔法当中有一个方法十分常用,以至于经常被称为“RK4”或者就是“龙格-库塔法”。该方法主要是在已知方程导数和初值信息,利用计算机仿真时应用,省去求解微分方程的复杂过程。 [1]

令初值问题表述如下。

image

则,对于该问题的RK4由如下方程给出:

image

其中

image
image
image
image

这样,下一个值(yn+1)由现在的值(yn)加上时间间隔(h)和一个估算的斜率的乘积所决定。该斜率是以下斜率的加权平均:

  • k1是时间段开始时的斜率;

  • k2是时间段中点的斜率,通过欧拉法采用斜率k1来决定y在点tn+h/2的值;

  • k3也是中点的斜率,但是这次采用斜率k2决定y值;

  • k4是时间段终点的斜率,其y值用k3决定。

当四个斜率取平均时,中点的斜率有更大的权值:

image

RK4法是四阶方法,也就是说每步的误差是h阶,而总积累误差为h阶。

注意上述公式对于标量或者向量函数(y可以是向量)都适用。


显式法

显式龙格-库塔法是上述RK4法的一个推广。它由下式给出 [1]

image

其中

image
image
image
image
image

(注意:上述方程在不同著述中有不同但却等价的定义)。

要给定一个特定的方法,必须提供整数s(级数),以及系数aij(对于1 ≤j<is),bi(对于i= 1, 2, ...,s)和ci(对于i= 2, 3, ...,s)。

龙格库塔法是自洽的,如果

image

如果要求方法的精度为p阶,即截断误差为O(h)的,则还有相应的条件。这些可以从截断误差本身的定义中导出。例如,一个2级2阶方法要求b1+b2= 1,b2c2= 1/2, 以及b2a21= 1/2。

例子

RK4法处于这个框架之内。其表为:

0
1/2 1/2
1/2 0 1/2
1 0 0 1
_ 1/6 1/3 1/3 1/6

然而,最简单的龙格-库塔法是(更早发现的)欧拉方法,其公式为

image

。这是唯一自洽的一级显式龙格库塔方法。相应的表为:

0
_ 1

隐式方法

以上提及的显式龙格库塔法一般来讲不适用于求解刚性方程。这是因为显式龙格库塔方法的稳定区域被局限在一个特定的区域里。显式龙格库塔方法的这种缺陷使得人们开始研究隐式龙格库塔方法,一般而言,隐式龙格库塔方法具有以下形式:

image

其中

image

在显式龙格库塔方法的框架里,定义参数

image

的矩阵是一个下三角矩阵,而隐式龙格库塔方法并没有这个性质,这是两个方法最直观的区别:

龙格——库塔(Rungekutta)法求解常微分方程_第1张图片
image

需要注意的是,与显式龙格库塔方法不同,隐式龙格库塔方法在每一步的计算里需要求解一个线性方程组,这相应的增加了计算的成本。


龙格-库塔法的C语言实现

#include "stdio.h"
#include "stdlib.h" 
void RKT(t,y,n,h,k,z)
int n;              /*微分方程组中方程的个数,也是未知函数的个数*/
int k;              /*积分的步数(包括起始点这一步)*/
double t;           /*积分的起始点t0*/
double h;           /*积分的步长*/
double y[];         /*存放n个未知函数在起始点t处的函数值,返回时,其初值在二维数组z的第零列中*/
double z[];         /*二维数组,体积为n x k.返回k个积分点上的n个未知函数值*/
{    
    extern void Func();             /*声明要求解的微分方程组*/   
    int i,j,l;
    double a[4],*b,*d;
    b=malloc(n*sizeof(double));     /*分配存储空间*/
    if(b == NULL){ 
       printf("内存分配失败\n");
        exit(1);
    }
    d=malloc(n*sizeof(double));     /*分配存储空间*/
    if(d == NULL){
        printf("内存分配失败\n");
        exit(1);
    }   
   /*后面应用RK4公式中用到的系数*/    
   a[0]=h/2.0;
   a[1]=h/2.0;
   a[2]=h; 
   a[3]=h;
   for(i=0; i<=n-1; i++)
         z[i*k]=y[i];                /*将初值赋给数组z的相应位置*/
   for(l=1; l<=k-1; l++){
        Func(y,d);        
        for (i=0; i<=n-1; i++)
            b[i]=y[i];
        for (j=0; j<=2; j++){ 
           for (i=0; i<=n-1; i++){ 
               y[i]=z[i*k+l-1]+a[j]*d[i];
               b[i]=b[i]+a[j+1]*d[i]/3.0;
           }           
           Func(y,d);
        }
        for(i=0; i<=n-1; i++)
          y[i]=b[i]+h*d[i]/6.0;
        for(i=0; i<=n-1; i++)
          z[i*k+l]=y[i];
          t=t+h;
    } 
   free(b);/*释放存储空间*/
   free(d);            /*释放存储空间*/
   return;
}
main(){
    int i,j;
    double t,h,y[3],z[3][11];
    y[0]=-1.0;
    y[1]=0.0;
    y[2]=1.0;
    t=0.0;
    h=0.01;
    RKT(t,y,3,h,11,z);
    printf("\n");
    for (i=0; i<=10; i++){/*打印输出结果*/
        t=i*h;
        printf("t=%5.2f\t   ",t);
        for (j=0; j<=2; j++) 
         printf("y(%d)=%e  ",j,z[j][i]);        printf("\n");
    }
} 
void Func(y,d)double y[],d[];{    
  d[0]=y[1];      /*y0'=y1*/
  d[1]=-y[0];     /*y1'=y0*/
  d[2]=-y[2];     /*y2'=y2*/
  return;
} 

你可能感兴趣的:(龙格——库塔(Rungekutta)法求解常微分方程)