logo

Fourth-order Runge-Kutta method 📂Numerical Analysis

Fourth-order Runge-Kutta method

Method 1

Given the initial value problem {y=f(x,y)y(x0)=Y0\begin{cases} y ' = f(x,y) \\ y( x_{0} ) = Y_{0} \end{cases} for the continuous function ff defined in DR2D \subset \mathbb{R}^2, let’s assume the interval (a,b)(a,b) is divided into nodes as in ax0<x1<<xn<xNba \le x_{0} < x_{1} < \cdots < x_{n} < \cdots x_{N} \le b. Particularly, for sufficiently small h>0h > 0, if xj=x0+jhx_{j} = x_{0} + j h, then for the initial value y0=Y0y_{0} = Y_{0} y_{n+1} = y_{n} + h \sum_{j=1}^{p} \gamma_{j} V_{j} y_{n+1} = y_{n-1} + h \sum_{j=1}^{p} \gamma_{j} V_{j} $$

Explanation

The Runge-Kutta method, like the Adams method, comes in various forms and determines γj\gamma_{j} and VjV_{j} through complex algebraic operations. Among them, the 4th-order Runge-Kutta method, often abbreviated as RK4, is the most popularly used.

4th-order Runge-Kutta method: yn+1=yn+h6[V1+2V2+2V3+V4]V1=f(xn,yn)V2=f(xn+h2,yn+h2V1)V3=f(xn+h2,yn+h2V2)V4=f(xn+h,yn+hV3) \begin{align*} y_{n+1} =& y_{n} + {{h} \over {6}} \left[ V_{1} + 2 V_{2} + 2 V_{3} + V_{4} \right] \\ V_{1} =& f(x_{n} , y_{n}) \\ V_{2} =& f \left( x_{n} + {{h} \over {2}} , y_{n} + {{h} \over {2}} V_{1} \right) \\ V_{3} =& f \left( x_{n} + {{h} \over {2}} , y_{n} + {{h} \over {2}} V_{2} \right) \\ V_{4} =& f \left( x_{n} + h , y_{n} + h V_{3} \right) \end{align*}

From the formula, you can see that to proceed one step hh, data up to the point h2\displaystyle {{h} \over {2}} is used. For a more generalized explanation, refer to See also.

Intuitive Derivation

Let’s break down the terms one by one and think using the simplest Euler method (Assuming xn+12:=xn+h2\displaystyle x_{n+ {{1} \over {2}} } : = x_{n} + {{h} \over {2}}, yn+12Y(xn+h2)y_{n+ {{1} \over {2}} } \approx Y(x_{n + {{h} \over {2}} } ) for convenience.) V1=f(xn,yn) V_{1} = f(x_{n} , y_{n} ) is the differential coefficient yny_{n} ' at the point xnx_{n}. V2=f(xn+h2,yn+h2f(xn,yn))=f(xn+12,yn+12) \begin{align*} V_{2} =& f \left( x_{n} + {{h} \over {2}} , y_{n} + {{h} \over {2}} f \left( x_{n} , y_{n} \right) \right) \\ =& f \left( x_{n + {{1} \over {2}} } , y_{n + {{1} \over {2}} } \right) \end{align*} is the differential coefficient yn+12\displaystyle y_{n + {{1} \over {2}} } ' at the point xn+12x_{n+{{1} \over {2}} }. V3=f(xn+h2,yn+h2f(xn+12,yn+12)) V_{3} = f \left( x_{n} + {{h} \over {2}} , y_{n} + {{h} \over {2}} f \left( x_{n + {{1} \over {2}} } , y_{n + {{1} \over {2}} } \right) \right) is also by the backward Euler method, V3=f(xn+12,yn+12)\displaystyle V_{3} = f \left( x_{n + {{1} \over {2}} } , y_{n + {{1} \over {2}} } \right) , the differential coefficient yn+12\displaystyle y_{n + {{1} \over {2}} } ' at the point xn+12x_{n+{{1} \over {2}} }. V4=f(xn+h,yn+hV3)=f(xn+h2+h2,yn+h2V3+h2V3)=f(xn+h2+h2,yn+h2+h2V3) \begin{align*} V_{4} =& f \left( x_{n} + h , y_{n} + h V_{3} \right) \\ =& f \left( x_{n} + {{h} \over {2}} + {{h} \over {2}} , y_{n} + {{h} \over {2}} V_{3} + {{h} \over {2}} V_{3} \right) \\ =& f \left( x_{n + {{h} \over {2}}} + {{h} \over {2}} , y_{n + {{h} \over {2}}} + {{h} \over {2}} V_{3} \right) \end{align*} Furthermore, by the backward Euler method, it is the differential coefficient yn+1\displaystyle y_{n + 1} ' at the point xn+1x_{n+ 1 }. h6[V1+2V2+2V3+V4] {{h} \over {6}} \left[ V_{1} + 2 V_{2} + 2 V_{3} + V_{4} \right] is especially a weighted average that uses data up to the point xn+12x_{n+{{1} \over {2}} } and gives a weight to the point xn+12x_{n+{{1} \over {2}} } to proceed one step hh.

Although more calculations can be added, from RK4 onwards, the order of convergence doesn’t increase as much as the amount of computation anymore. Even if not increased, if the problem is not stiff, RK4 is often sufficient, and its coding is also comparatively easy, which is why it is widely used.

Implementation

R

The following is the code to implement RK4 in R and draw the Lorenz attractor in 3D.

library(rgl)
 
RK4<-function(ODE,v,h=10^(-2)){
  V1 = ODE(v)
  V2 = ODE(v + (h/2)*V1)
  V3 = ODE(v + (h/2)*V2)
  V4 = ODE(v + h*V3)
  v = v + (h/6)*(V1 + 2*V2 + 2*V3 + V4)
  return(v)
}
 
lorenz<-function(v,rho=28,sigma=10,beta=8/3){
  dvdt = v
  dvdt[1] = sigma*(v[2]-v[1])
  dvdt[2] = v[1]*(rho-v[3])-v[2]
  dvdt[3] = v[1]*v[2] - beta*v[3]
  return(dvdt)
}
 
traj<-numeric(0)
v=c(1,1,1)
for(i in 1:10000){
  traj<-rbind(traj,v)
  v=RK4(lorenz,v)
}
 
plot3d(traj,type='l')
plot(traj[,1],traj[,3],type='l')

Executing the code yields the following 3D plot.

20190226\_173942.png

Julia

Below is the implementation of the same content in Julia.

using Plots
function RK4(ODE::Function,v::Array{Float64,1},h=10^(-2))
    V1 = ODE(v)
    V2 = ODE(v .+ (h/2)*V1)
    V3 = ODE(v .+ (h/2)*V2)
    V4 = ODE(v .+ h*V3)
    return @. v + (h/6)*(V1 + 2*V2 + 2*V3 + V4)
end
function lorenz(v::Array{Float64,1}; ρ=28.,σ=10.,β=8/3)
  dvdt = deepcopy(v)
  dvdt[1] = σ*(v[2]-v[1])
  dvdt[2] = v[1]*(ρ-v[3])-v[2]
  dvdt[3] = v[1]*v[2] - β*v[3]
  return dvdt
end
function  lorenz_attracter(v::Array{Float64,1}, endtime = 10000)
  x,y,z = [v[1]], [v[2]], [v[3]]
  for t in 1:endtime
    newx,newy,newz = RK4(lorenz,[x[end], y[end], z[end]])
    push!(x, newx)
    push!(y, newy)
    push!(z, newz)
  end
  return x,y,z
end
result = lorenz_attracter([1.,1.,1.])
plot(result)

See also


  1. Atkinson. (1989). An Introduction to Numerical Analysis(2nd Edition): p420. ↩︎