はじめに
本稿では、クレローの微分方程式(Clairaut's equation)を題材とし、その一般解と特異解の関係を概観したのち、ニュートン法およびオイラー法を用いた数値計算によって解を求める方法を簡潔に紹介する。具体的には、クレローの微分方程式
y = x y' + f(y')
における$y'$をニュートン法を用いて数値解析的に解く。
その後、オイラー法でステップを進める。
クレローの微分方程式
クレローの微分方程式は、次の形で与えられる1階常微分方程式である。
y = x y' + f(y')
$p = y'$ とおくと、上式は
y = xp + f(p)
と書ける。本稿では例として $f(p) = p^2$ の場合を扱う。
一般解と特異解
$p$ を任意定数 $c$ とみなすと、
y = cx + f(c)
は方程式を満たす。これは傾き $c$ の直線群であり、これがクレローの微分方程式の一般解である。
一方、この直線群は $c$ を動かすと1本の曲線に接しながら包絡線を形成する。この包絡線は、上式を $c$ について微分した
0 = x + f'(c)
と元の式から $c$ を消去することで得られ、特異解と呼ばれる。$f(p)=p^2$ の場合、$f'(c)=2c$ より $c = -x/2$ となり、これを $y=cx+c^2$ に代入すると
y = -\frac{x^2}{4}
という放物線が得られる。すなわち、放物線が特異解であり、それに接する一般解は直線群となる。
数値計算による解法
解析的に求まる本問題を、あえて数値計算によって解く。用いる手法は次の2つである。
ニュートン法(概要)
方程式 $g(p)=0$ の解を、初期値 $p_0$ から反復式
p_{n+1} = p_n - \frac{g(p_n)}{g'(p_n)}
によって近似的に求める手法である。本稿では $g'(p_n)$ を数値微分(前進差分)で近似し、陰関数
g(x,y,p) = y - xp - f(p) = 0
を $p$ について解くために用いる。
オイラー法
$dy/dx = p$ という関係のもと、微小刻み幅 $\Delta x$ ごとに
y_{n+1} = y_n + p_n \, \Delta x, \quad x_{n+1} = x_n + \Delta x
として解曲線を逐次的に求める、最も基本的な数値積分法である。
2つの手法の組み合わせ
各 $x$ において、まずニュートン法で陰関数 $g(x,y,p)=0$ を満たす $p$ を求め(直線の傾きを特定し)、その $p$ を使ってオイラー法で $x,y$ を1ステップ進める。この手順を繰り返すことで、一般解である直線を数値的にトレースする。
プログラム
その1
import numpy as np
import matplotlib.pyplot as plt
def f(p):
return p**2
def g(x, y, p):
return y - x*p - f(p)
def solve_p_newton(x, y, p0, h=1e-6, iters=20):
p = p0
for _ in range(iters):
dg = (g(x, y, p+h) - g(x, y, p)) / h
p -= g(x, y, p) / dg
return p
# (x, y) = (-5, -4) は直線 y = x + 1 上の点
x, y, p = -5.0, -4.0, 0.5
dx = 1e-3
xs, ys = [x], [y]
for _ in range(10000):
p = solve_p_newton(x, y, p) # ニュートン法: 陰関数から傾き p を求める
y = y + p * dx # オイラー法: dy/dx = p で y を更新
x = x + dx
xs.append(x)
ys.append(y)
plt.plot(xs, ys, label="numerical (Newton + Euler)")
plt.plot(xs, [-(xx**2)/4 for xx in xs], "--", label="singular solution: y = -x^2/4")
plt.legend()
plt.show()
初期値 $(x,y)=(-5,-4)$ は一般解 $y=x+1$($c=1$)上の点である。ニュートン法によって各ステップで $p=1$ が求まり、オイラー法によってこの直線が忠実にトレースされる。
結果
得られた数値解は、解析解である直線 $y=x+1$ と一致し(誤差 $10^{-13}$ 程度)、かつ特異解の放物線 $y=-x^2/4$ に $x=-2$ の点で接することが確認できる。これは、一般解の直線群が特異解の放物線を包絡線として持つという理論的性質と整合する結果である。
その2
さらに、$p$の値を変化させたときの一般解の推移を示すプログラムも以下のように作成した。
# Clairautの微分方程式 y = xp + f(p) の特異解(包絡線)を
# ニュートン法を用いて数値計算で解く。任意の f(p) に対応する。
#
# 一般解は y = cx + f(c) という直線族。特異解(包絡線)は、
# この直線族から x, y を残して媒介変数 p(=c) を消去することで得られ、
# y = xp + f(p) ... (1) 直線群の式
# x + f'(p) = 0 ... (2) 包絡線条件 (∂/∂p = 0)
# の連立から求まる。(2) を p についてニュートン法で解き、(1) から y を計算する。
#
# f が線形 (f''(p)=0) の場合など、(2) が p に依存しない/収束しない x では
# 包絡線が定義できない(直線群が1点を通るだけ等)ので、その点は NaN にして
# プロット上でも自然に欠落させる。
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
import matplotlib.cm as cm
n = 50 # ニュートン法の最大反復回数
h = 1e-6 # 数値微分のステップ幅
step_tol = 1e-9 # 収束判定の閾値(ステップ幅 |Δp| がこれ未満なら収束とみなす)
p_max = 1e8 # これを超えて発散したら解なしと判断する
retries = 6 # 停留点(f'(p)の傾きが0)に当たった場合の初期値ずらし回数
x_min, x_max = -1, 1
num_points = 2000
# ここを差し替えれば任意の f(p) の Clairaut 方程式 y = xp + f(p) を扱える
def func(p):
return p**2
def func_diff(p):
return (func(p+h) - func(p)) / h
def envelope_eq(x, p):
# 包絡線条件: x + f'(p) = 0
return x + func_diff(p)
def solve_p(x, p0):
for attempt in range(retries):
# 停留点(f'(p)の傾きが0)を避けるため、失敗するたびに初期値を少しずらす
p = p0 + attempt * 0.1737
for i in range(n):
e = envelope_eq(x, p)
e_diff = (envelope_eq(x, p+h) - e) / h
if abs(e_diff) < 1e-9:
break # この初期値では停留点に来てしまった。次の初期値で再試行
step = e / e_diff
p = p - step
if not np.isfinite(p) or abs(p) > p_max:
break # 発散した。次の初期値で再試行
if abs(step) < step_tol:
return p # 収束
return None # 実数解が存在しない(あるいは見つからなかった)
x_ary = np.linspace(x_min, x_max, num_points)
y_ary = []
p = 0.0 # 初期推定値。収束すれば前ステップの解を引き継ぎ、探索を安定させる
for x in x_ary:
p_sol = solve_p(x, p)
if p_sol is None:
y_ary.append(np.nan) # この x では特異解が存在しない
else:
p = p_sol
y_ary.append(x*p + func(p))
plt.plot(x_ary, y_ary, label="特殊解")
num2=10
x2_ary=np.linspace(x_min,x_max,num_points)
p_ary=np.linspace(x_min/5,x_max/5,num2)
for i in range(num2):
y2_ary=np.zeros(num_points)
for k in range(num_points):
y2=x2_ary[k]*p_ary[i]+func(p_ary[i])
y2_ary[k]=y2
plt.plot(x2_ary,y2_ary,color=cm.jet(i/p_ary[i]))
plt.legend()
plt.savefig("クレローの微分方程式2.png")
plt.show()
特殊解である放物線に接する包絡線としての一般解を図示目することができた。ニュートン法は、シンプルなアルゴリズムながら、収束が早いため計算時間が比較的短く済んだ。
見ての通り、特殊解に接する直線としての一般解の描写は、一般解のパラメータを動かし描写することで美しいグラフを描写することが可能となる。
まとめ
クレローの微分方程式を題材に、一般解(直線群)と特異解(放物線)の関係を概観し、ニュートン法とオイラー法を組み合わせた数値計算によってこれを検証した。今回は $f(p)=p^2$ の場合を扱ったが、$f(p)$ を差し替えることで他のクレローの微分方程式にも同様の手法を適用できる。

