龍格——庫塔(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

的矩陣是一個下三角矩陣,而隱式龍格庫塔方法并沒有這個性質,這是兩個方法最直觀的區別:

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;
} 
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容