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]
令初值問題表述如下。
則,對于該問題的RK4由如下方程給出:
其中
這樣,下一個值(yn+1)由現在的值(yn)加上時間間隔(h)和一個估算的斜率的乘積所決定。該斜率是以下斜率的加權平均:
k1是時間段開始時的斜率;
k2是時間段中點的斜率,通過歐拉法采用斜率k1來決定y在點tn+h/2的值;
k3也是中點的斜率,但是這次采用斜率k2決定y值;
k4是時間段終點的斜率,其y值用k3決定。
當四個斜率取平均時,中點的斜率有更大的權值:
RK4法是四階方法,也就是說每步的誤差是h階,而總積累誤差為h階。
注意上述公式對于標量或者向量函數(y可以是向量)都適用。
顯式法
顯式龍格-庫塔法是上述RK4法的一個推廣。它由下式給出 [1]
其中
(注意:上述方程在不同著述中有不同但卻等價的定義)。
要給定一個特定的方法,必須提供整數s(級數),以及系數aij(對于1 ≤j<i≤s),bi(對于i= 1, 2, ...,s)和ci(對于i= 2, 3, ...,s)。
龍格庫塔法是自洽的,如果
如果要求方法的精度為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
然而,最簡單的龍格-庫塔法是(更早發現的)歐拉方法,其公式為
。這是唯一自洽的一級顯式龍格庫塔方法。相應的表為:
0
_ 1
隱式方法
以上提及的顯式龍格庫塔法一般來講不適用于求解剛性方程。這是因為顯式龍格庫塔方法的穩定區域被局限在一個特定的區域里。顯式龍格庫塔方法的這種缺陷使得人們開始研究隱式龍格庫塔方法,一般而言,隱式龍格庫塔方法具有以下形式:
其中
在顯式龍格庫塔方法的框架里,定義參數
的矩陣是一個下三角矩陣,而隱式龍格庫塔方法并沒有這個性質,這是兩個方法最直觀的區別:
需要注意的是,與顯式龍格庫塔方法不同,隱式龍格庫塔方法在每一步的計算里需要求解一個線性方程組,這相應的增加了計算的成本。
龍格-庫塔法的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;
}