M2.4 13 分鐘閱讀 2026年9月

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 走半步找到中點的大概位置,在那裡讀方向,再用那個方向從起點走整步:

k1=u(xn,tn),k2=u ⁣(xn+h2k1,  tn+h2),xn+1=xn+hk2.k_1=u(x_n,t_n),\qquad k_2=u\!\big(x_n+\tfrac{h}{2}k_1,\;t_n+\tfrac h2\big),\qquad x_{n+1}=x_n+h\,k_2 .

這叫 midpoint 法。同一個想法的另一個版本是 Heun 法:用 Euler 先走到終點的大概位置,在那裡讀方向,然後用起點與終點方向的平均走整步:

k1=u(xn,tn),k2=u(xn+hk1,  tn+h),xn+1=xn+h2(k1+k2).k_1=u(x_n,t_n),\qquad k_2=u(x_n+h\,k_1,\;t_n+h),\qquad x_{n+1}=x_n+\tfrac h2(k_1+k_2).

兩者與 Euler 的差別都只在「用哪個方向走這一步」:Euler 用起點的方向;midpoint 用中點的;Heun 用兩端的平均。

公平的比較不是「同樣的 hh」,而是「同樣的讀取次數」,因為讀取(評估 uu)才是成本。這裡的量叫 number of function evaluations(NFE)。Euler 一步一次,Heun 與 midpoint 一步兩次。所以要比的是:Euler 用 h=15h=15 秒(12 次讀取)對上 midpoint 用 h=30h=30 秒(同樣 12 次)。

先看畫面:切線 vs 弦

回到那段圓弧。Euler 沿著起點的切線走,一步就離開圓;步長越長、離得越遠,離開的量是 h2h^2 的量級。

中點的方向是什麼?圓弧上中點的切線,方向剛好與連接起點和終點的平行。所以「用中點方向走整步」幾乎就是沿著弦走——弦的兩端都在圓上,走完一步幾乎回到圓弧上。剩下的誤差不是「切線離開圓」那種一次項的偏差,而是「弦的長度與弧長不完全相等」這種更高一階的小量。這就是為什麼多讀一次不是「好一倍」,而是好一個數量級:它消掉的不是誤差的一半,而是誤差的最低階項

寫成數學:把 h2h^2 那一項配掉

以 autonomous 的 u(x)u(x) 為例(有 tt 的情形只是多幾項,結論相同)。真實解的 Taylor 展開到二階(M0.2):

x(t+h)=x+hx˙+h22x¨+O(h3),x˙=u(x),x¨=ddtu(x(t))=(u)u.x(t+h)=x+h\,\dot x+\tfrac{h^2}{2}\ddot x+O(h^3),\qquad \dot x=u(x),\quad \ddot x=\tfrac{d}{dt}u(x(t))=(\nabla u)\,u .

Midpoint 的局部誤差。k2k_2 也展開:k2=u(x+h2u)=u+h2(u)u+O(h2)k_2=u\big(x+\tfrac h2u\big)=u+\tfrac h2(\nabla u)\,u+O(h^2)。於是

x+hk2=x+hu+h22(u)u+O(h3),x+h\,k_2=x+h\,u+\tfrac{h^2}{2}(\nabla u)\,u+O(h^3),

與真實解前三項完全一致,差距是 O(h3)O(h^3)\square

Heun 同理k2=u(x+hu)=u+h(u)u+O(h2)k_2=u(x+hu)=u+h(\nabla u)u+O(h^2)h2(k1+k2)=hu+h22(u)u+O(h3)\tfrac h2(k_1+k_2)=h\,u+\tfrac{h^2}{2}(\nabla u)u+O(h^3),同樣配掉二階項。

全域誤差。 接著照 M2.3 的 Grönwall 論證走一遍,只是局部誤差從 Ch2Ch^2 換成 Ch3Ch^3N=T/hN=T/h 步累積後是 O(h2)O(h^2)。所以 Heun 與 midpoint 是二階方法hh 縮一半,誤差縮四倍;Euler 是一階,縮一半只縮一半。

一般地,一個方法若局部誤差是 O(hp+1)O(h^{p+1})、全域誤差就是 O(hp)O(h^p),叫 order pp。Euler p=1p=1,Heun 與 midpoint p=2p=2,經典的 Runge–Kutta 四階法 p=4p=4(每步四次讀取)。這一族方法的系統設計見 [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² 項的機制不變。

回到導航:贏在哪、什麼時候不贏

回答起點問題。中點多讀一次,h=30h=30 秒的 midpoint 誤差是 7 公尺,Heun 是 23 公尺——都遠好於「每 10 秒讀一次」的 Euler(161 公尺,而且那還多用了 50% 的讀取)。直覺的「好一倍」錯在把方法的改進想成線性的:多一次讀取不是多修正一半,而是把誤差的主項整個消掉、換成下一階。

但要說清楚這個好處依賴什麼:

  • 路要夠 smooth。 配掉 h2h^2 項用到 uu 的二階 Taylor 展開;若路上有尖角uu 在某處不可微),展開在那一步失效,所有方法在那一步都退回一階,高階方法的優勢消失。
  • 預算要夠多。 二階方法的誤差 C2(2T/E)2\approx C_2(2T/E)^2、一階的 C1(T/E)\approx C_1(T/E)EE 是總讀取次數,二階方法每步用兩次所以步長是 2T/E2T/E)。EE 大時 1/E21/E^2 必勝 1/E1/E;但 EE 很小(例如全程只讀兩三次)時,C24T2/E2C_2\cdot4T^2/E^2 未必小於 C1T/EC_1T/E——高階方法在極粗的步長下不保證比較好
  • 穩定性不會免費變好。 步長大到 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 度尖角的折線路(uu 在角上不連續)。兩種方法在跨過尖角的那一步都會偏,偏的量都是 O(h)O(h);跑一次會看到 midpoint 的優勢從兩個數量級掉到不到一倍。

先消化一下

想一想

一個天氣模擬每一步都要跑一次極貴的物理計算(一次讀取 uu 要一分鐘)。在總計算時間固定為一小時的前提下,下面哪個說法最合理?

想一想

你把導航從 Euler 換成 midpoint(同樣每 30 秒一步),發現在市區裡效果遠不如在高速公路上。最可能的原因是:

想一想

Heun 法用 h2(k1+k2)\tfrac h2(k_1+k_2),midpoint 用 hk2h\,k_2k2k_2 在半步處)。兩者都是二階,是因為:

參考文獻

  1. 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 條件。)
  2. Butcher, J. C. Numerical Methods for Ordinary Differential Equations. Wiley, 3rd ed., 2016.(第 3 章:Runge–Kutta 方法的階數理論與成本比較。)