ホイン法#

ホイン法(Heun’s method) または 改良オイラー法(Improved Euler’s method) はオイラー法で利用する始点 \(t\) での関数の傾きの代わりに,始点 \(t\) と終点 \(t+h\) での傾きの平均を用いて近似を行う.つまり,

\[ \begin{aligned} x(t+h) = x(t) + \frac{h}{2} \left \{ f\left (x \left (t \right),t\right)+ f\left (x + hf\left(x,t\right),t + h\right)\right \} \end{aligned} \]

の近似式を利用する.

heun

オイラー法と同様にこの近似式の幾何的解釈を図に示す.ホイン法では始点 \(t\) での傾き(図の灰色の点線)に加えて,終点 \(t+h\) での傾き(図の青色の点線)の情報を利用する.近似式からもわかるように,これらの傾きを平均した傾きを使って次の時刻 \(t+h\) での関数の値を外挿する(図の赤色の点線).これを数値列の近似として書くと,

\[\begin{split} \begin{aligned} \tilde{x}_{i+1} &= x_i + hf\left(x_i,t_i \right) \\ x_{i+1} &= x_i + \frac{h}{2} \left \{ f\left(x_i,t_i \right) + f\left(\tilde{x}_{i+1},t_{i+1} \right)\right \} \end{aligned} \end{split}\]

となる.ホイン法は二次の精度をもつ近似法として知られている.このことは次のようにオイラー法から修正した導関数,つまり,次の時刻の関数の値 \(f(x+hf(x,t),t+h)\) のTaylor展開をすることで確認できる.二変数関数であることに注意すると

\[ \begin{aligned} f(x+hf(x,t), t+h) = f(x,t)+h \frac{\partial f}{\partial t} + h\frac{\partial f}{\partial x} \frac{dx}{dt} +O(h^2) \end{aligned} \]

となる.続いて,これをホイン法で用いた近似式に代入する.

\[\begin{split} \begin{aligned} x(t+h) &= x(t) + \frac{h}{2} \left \{ f\left (x \left (t \right),t\right)+ f\left (x\left(t\right) + hf\left(x,t\right),t + h\right)\right \} \\ &= x(t) + \frac{h}{2} \left \{ f\left (x \left (t \right),t\right)+ f(x(t),t)+h \frac{\partial f}{\partial t} + h\frac{\partial f}{\partial x} \frac{dx}{dt}+O(h^2)\right \} \\ &= x(t) + h f\left (x \left (t \right),t\right) + \frac{h^2}{2} \left \{ \frac{\partial f}{\partial t} + \frac{\partial f}{\partial x} \frac{dx}{dt} \right \} + O(h^3) \\ &= x(t) + h x'(t) + \frac{h^2}{2} x''(t) + O(h^3) \end{aligned} \end{split}\]

ここで \(f(x(t),t)=x'(t)\) であり,\(x(t)\) の二階導関数が

\[ \begin{aligned} x''(t)=\frac{d^2 x}{d t^2}=\frac{\partial f}{\partial t}+\frac{\partial f}{\partial x} \frac{d x}{d t} \end{aligned} \]

であることを利用した.式からもわかるように右辺第3項まで \(x(t+h)\) のTaylor展開と一致しており,ホイン法が二階の導関数までを含む二次近似をしており,オイラー法より優れたアルゴリズムであることがわかる.

ホイン法の実装#

これをPythonで実装する.今回は次の初期値問題を解く

\[ x'=x(1-x), x(0)=\frac{1}{2} \]

解答:解析解は \(x=\frac{1}{1+e^{-t}}\) であり,以下のプログラムで解析解と近似解の違いをプロットする.

import numpy as np
import matplotlib.pyplot as plt

def heun_method(f, x0, t0, tn, h):
    """
    ホイン法(修正オイラー法)を用いて常微分方程式を数値的に解く関数
    :param f: 常微分方程式の右辺の関数 f(t, x) 
    :param x0: 初期値 x(t0)
    :param t0: 初期時刻
    :param tn: 最終時刻
    :param h: 刻み幅
    :return: 時刻と近似解のリスト
    """
    t_values = [t0] # t_0の値を格納したリストを作成
    x_values = [x0] # x_0の値(初期値)を格納したリストを作成
    
    t = t0
    x = x0
    
    while t < tn: # 最終時刻になるまで繰り返す
        # 時刻と式()に基づく値の更新
        k1 = h * f(t, x)
        k2 = h * f(t + h, x + k1)
        x = x + 0.5 * (k1 + k2)
        t = t + h
        
        # 計算された値をリストに追加
        t_values.append(t)
        x_values.append(x)
        
    return t_values, x_values

# 微分方程式の右辺
def f(t, x):
    return x * (1 - x)

# 解析解
def analytical_solution(t):
    return 1. / (1 + np.exp(-t))

# 初期値とパラメータ
x0 = 0.5 # 初期値
t0 = 0 # 開始時刻
tn = 5 # 終了時刻
h = 0.001 # 刻み幅

plt.figure()
t_analytical = np.linspace(t0, tn, 500) # 区間[開始時刻,終了時刻]で時刻を500点サンプリング
x_analytical = analytical_solution(t_analytical) # 解析解の計算
plt.plot(t_analytical, x_analytical, label='Analytical solution') # 解析解のプロット 

t_values, x_values = heun_method(f, x0, t0, tn, h) # ホイン法の計算
plt.plot(t_values, x_values, label="Heun's method") # ホイン法の結果のプロット
plt.title(r"Heun's Method for $x' = x(1-x)$")
plt.xlabel(r'$t$')
plt.ylabel(r'$x(t)$')
plt.grid(True)
plt.legend()
plt.show()
../../_images/1a322f25f456111361077e2ec66889bdef805a5b11952ee26e34147faf3a83b3.png