M2.4 在 30 秒的中點多看一眼:高階方法
本篇重用M2.3導航每 30 秒更新一次:Euler 法與它的誤差·M0.2導航的三十秒:Taylor 展開與「假設直線」
同一條彎道,多一次讀取
上一篇(M2.3)的導航每 30 秒讀一次方向、中間直走,追一條半徑 2 公里的四分之一圓,終點偏了 486 公尺。要更準,最直接的辦法是讀得更頻繁:每 10 秒讀一次,誤差降到 161 公尺,但讀取次數變成三倍。
工程師提出另一個想法:還是每 30 秒走一步,但在每步的中點多讀一次方向,用「中點的方向」而不是「起點的方向」來直走這 30 秒。讀取次數變成兩倍(不是三倍)。你猜誤差會降到多少?比 161 公尺好還是差?
先寫下你的答案和依據。常見的直覺是「多讀一次,大概好一倍吧,所以 240 公尺左右——比每 10 秒讀一次差」。這個直覺低估得很嚴重。實際跑出來是 7 公尺。為什麼差這麼多?以及什麼時候這個好處會消失?
課堂提問Q1
翻成數學。「在中點多看一眼」寫成更新式是什麼?它和 Euler 的差別在哪一項?我們要比較的量該怎麼定才公平?
先想一想,再展開看整理後的答案
課堂上會先寫出「中點的方向」:先用 Euler 走半步找到中點的大概位置,在那裡讀方向,再用那個方向從起點走整步:
這叫 midpoint 法。同一個想法的另一個版本是 Heun 法:用 Euler 先走到終點的大概位置,在那裡讀方向,然後用起點與終點方向的平均走整步:
兩者與 Euler 的差別都只在「用哪個方向走這一步」:Euler 用起點的方向;midpoint 用中點的;Heun 用兩端的平均。
公平的比較不是「同樣的 」,而是「同樣的讀取次數」,因為讀取(評估 )才是成本。這裡的量叫 number of function evaluations(NFE)。Euler 一步一次,Heun 與 midpoint 一步兩次。所以要比的是:Euler 用 秒(12 次讀取)對上 midpoint 用 秒(同樣 12 次)。
先看畫面:切線 vs 弦
回到那段圓弧。Euler 沿著起點的切線走,一步就離開圓;步長越長、離得越遠,離開的量是 的量級。
中點的方向是什麼?圓弧上中點的切線,方向剛好與連接起點和終點的弦平行。所以「用中點方向走整步」幾乎就是沿著弦走——弦的兩端都在圓上,走完一步幾乎回到圓弧上。剩下的誤差不是「切線離開圓」那種一次項的偏差,而是「弦的長度與弧長不完全相等」這種更高一階的小量。這就是為什麼多讀一次不是「好一倍」,而是好一個數量級:它消掉的不是誤差的一半,而是誤差的最低階項。
寫成數學:把 那一項配掉
以 autonomous 的 為例(有 的情形只是多幾項,結論相同)。真實解的 Taylor 展開到二階(M0.2):
Midpoint 的局部誤差。 把 也展開:。於是
與真實解前三項完全一致,差距是 。
Heun 同理:,,同樣配掉二階項。
全域誤差。 接著照 M2.3 的 Grönwall 論證走一遍,只是局部誤差從 換成 : 步累積後是 。所以 Heun 與 midpoint 是二階方法: 縮一半,誤差縮四倍;Euler 是一階,縮一半只縮一半。
一般地,一個方法若局部誤差是 、全域誤差就是 ,叫 order 。Euler ,Heun 與 midpoint ,經典的 Runge–Kutta 四階法 (每步四次讀取)。這一族方法的系統設計見 [1, 2]。
展開細節從數值積分看同一件事(u 只依賴 t 的情形)
若 u = u(t) 不依賴 x,ODE 的解就是積分 x(t+h) = x(t) + ∫ₜ^(t+h) u(s) ds,三種方法對應三種數值積分規則:
- Euler:h·u(t),左端點法則。誤差 (h²/2)u′ + …
- Midpoint:h·u(t+h/2),中點法則。把 u 在中點 m 展開,奇數次項對稱相消,誤差 = ∫ ½u″(m)(s−m)² ds = (h³/24)u″(m)。
- Heun:(h/2)[u(t)+u(t+h)],梯形法則。u(t)+u(t+h) = 2u(m) + u″(m)(h/2)² + …,所以誤差 = (h³/8 − h³/24)u″ = (h³/12)u″(m)。 兩者都是 h³,係數分別是 1/24 與 1/12——midpoint 在這個度量下常數小一半,與上面 7 m 對 23 m 的實測一致。有 x 依賴時多出 (∇u)u 的交叉項,但配掉 h² 項的機制不變。
回到導航:贏在哪、什麼時候不贏
回答起點問題。中點多讀一次, 秒的 midpoint 誤差是 7 公尺,Heun 是 23 公尺——都遠好於「每 10 秒讀一次」的 Euler(161 公尺,而且那還多用了 50% 的讀取)。直覺的「好一倍」錯在把方法的改進想成線性的:多一次讀取不是多修正一半,而是把誤差的主項整個消掉、換成下一階。
但要說清楚這個好處依賴什麼:
- 路要夠 smooth。 配掉 項用到 的二階 Taylor 展開;若路上有尖角( 在某處不可微),展開在那一步失效,所有方法在那一步都退回一階,高階方法的優勢消失。
- 預算要夠多。 二階方法的誤差 、一階的 ( 是總讀取次數,二階方法每步用兩次所以步長是 )。 大時 必勝 ;但 很小(例如全程只讀兩三次)時, 未必小於 ——高階方法在極粗的步長下不保證比較好。
- 穩定性不會免費變好。 步長大到 M2.3 那種發散區時,二階方法的穩定區只比 Euler 大一點,不是質的改變。
回到導航:同樣的讀取次數比一次
import numpy as np
R, v = 2000.0, 60/3.6; T = (np.pi/2)*R/v
def u(x): r = np.hypot(*x); return v*np.array([-x[1], x[0]])/r
def run(method, nfe):
steps = nfe if method == 'euler' else nfe//2
h = T/steps; x = np.array([R, 0.0])
for _ in range(steps):
k1 = u(x)
if method == 'euler': x = x + h*k1
else: x = x + h*u(x + 0.5*h*k1) # midpoint
return np.linalg.norm(x - np.array([0.0, R]))
for nfe in [12, 38, 126]:
print(nfe, round(run('euler', nfe)), round(run('midpoint', nfe), 1))
# 12 次:Euler 約 252 m、midpoint 7 m;38 次:82 m 對 0.8 m;126 次:25 m 對 0.08 m
同樣的讀取次數,二階方法的誤差每次都少一到兩個數量級,而且讀取越多差距越大(一階縮三倍時二階縮九倍)。接著違反假設:把圓弧路換成一條有 90 度尖角的折線路( 在角上不連續)。兩種方法在跨過尖角的那一步都會偏,偏的量都是 ;跑一次會看到 midpoint 的優勢從兩個數量級掉到不到一倍。
先消化一下
參考文獻
- Hairer, E., Nørsett, S. P., Wanner, G. Solving Ordinary Differential Equations I: Nonstiff Problems. Springer, 2nd ed., 1993.(第 II.1 節:Runge–Kutta 方法的起源、Heun 與 midpoint、order 條件。)
- Butcher, J. C. Numerical Methods for Ordinary Differential Equations. Wiley, 3rd ed., 2016.(第 3 章:Runge–Kutta 方法的階數理論與成本比較。)