境界値問題
Note
本節のポイント
- 初期値問題(IVP)と境界値問題(BVP)の違いを理解する。
- 境界値問題を初期値問題に帰着させる「シューティング法」の原理を学ぶ。
- 二分法とニュートン法で学んだ「求根アルゴリズム」との密接な関連性を理解する。
これまで扱ってきた微分方程式は、ある時刻 での状態(位置や速度)がすべて分かっていて、そこから未来を予測する初期値問題 (Initial Value Problem, IVP) でした。
しかし、物理学では「始点と終点の位置が決まっている」ような問題も頻繁に現れます。これを境界値問題 (Boundary Value Problem, BVP) と呼びます。
境界値問題の例
- 静的な弦のたわみ: 両端が固定された弦の形状。
- 熱伝導: 棒の両端の温度が固定されている場合の内部温度分布。
- 量子力学の束縛状態: 波動関数が無限遠でゼロになる(または境界でゼロになる)条件。
シューティング法 (Shooting Method)
境界値問題を解くための最も直感的な手法がシューティング法(射撃法) です。これは、境界値問題を「未知の初期条件を探す初期値問題」として解く手法です。
原理
2階微分方程式 において、以下の境界条件が与えられているとします。
この問題を初期値問題として解くには、初期速度 が必要ですが、これは与えられていません。
- 初期速度の値を と仮定し、初期値問題 を まで解きます。
- における計算結果を とします。
- 目的の境界条件 との差(誤差)を計算します:
- この となるような を、二分法とニュートン法で学んだ求根アルゴリズム(二分法やニュートン法)を用いて探索します。
まさに「標的(境界条件)に当たるように、大砲の角度(初期速度)を調整しながら試射を繰り返す」イメージです。
Rustによる実装例
例として、単振動の方程式 において、境界条件 を満たす解をシューティング法で求めてみましょう。解析解は であり、初期速度は となるはずです。
use ndarray::{Array1, arr1};
use std::f64::consts::PI;
/// 4次のルンゲ=クッタ法による1ステップの更新
fn rk4_step<F>(state: &Array1<f64>, t: f64, h: f64, f: F) -> Array1<f64>
where
F: Fn(f64, &Array1<f64>) -> Array1<f64>,
{
let k1 = f(t, state);
let k2 = f(t + h * 0.5, &(state + &k1 * (h * 0.5)));
let k3 = f(t + h * 0.5, &(state + &k2 * (h * 0.5)));
let k4 = f(t + h, &(state + &k3 * h));
state + (&k1 + &k2 * 2.0 + &k3 * 2.0 + &k4) * (h / 6.0)
}
/// 初期値問題 (IVP) として t0 から t1 まで積分し、終端の状態を返す
fn solve_ivp(v0: f64) -> f64 {
let system = |_t: f64, state: &Array1<f64>| arr1(&[state[1], -state[0]]);
let mut state = arr1(&[0.0, v0]); // x(0)=0, v(0)=v0
let mut t = 0.0;
let t1 = PI / 2.0;
let h = 0.01;
while t < t1 {
let step_h = if t + h > t1 { t1 - t } else { h };
state = rk4_step(&state, t, step_h, system);
t += step_h;
}
state[0] // 終端位置 x(t1) を返す
}
fn main() {
let target_x1 = 1.0; // 目標: x(pi/2) = 1
let tolerance = 1e-8;
// 二分法による初期速度 v0 の探索
let mut low = 0.0;
let mut high = 2.0;
let mut v0 = (low + high) / 2.0;
println!("{:<5} {:<15} {:<15}", "Iter", "v0 (Guess)", "x(pi/2) Error");
println!("{}", "-".repeat(40));
for i in 0..50 {
let x_final = solve_ivp(v0);
let error = x_final - target_x1;
println!("{:<5} {:<15.8} {:<15.2e}", i, v0, error);
if error.abs() < tolerance {
break;
}
if error < 0.0 {
low = v0;
} else {
high = v0;
}
v0 = (low + high) / 2.0;
}
println!("\n結果: 求める初期速度 v(0) = {:.8}", v0);
}
シューティング法の限界
シューティング法は直感的で実装も比較的容易ですが、以下のような課題があります。
- 不安定性: 微分方程式が「硬い(stiff)」場合、初期値 の極めて微小な変化が、終端 で爆発的な差となって現れることがあり、求根アルゴリズムが収束しなくなります。
- 多変数の困難さ: 高次元の境界値問題では、探索すべき初期パラメータが増え、求根が非常に困難になります。
このような場合、領域全体を格子に分割して一気に解く有限差分法 (Finite Difference Method) や、変分法に基づいた手法が用いられます。これらについては、偏微分方程式で詳しく扱います。
参考リンク
常微分方程式では、物理現象の記述に欠かせない数値解法を学びました。偏微分方程式からは、空間的な広がりを持つ現象へと進んでいきます。