為什麼一步只取一個斜率還不夠
我們仍在解同一個初值問題 y'(t) = f(t, y),已知 y(t_0),並且仍以步長 h 一步步向前推進。前面的指南已揭示了前向 Euler 的毛病:它在這一步的「起點」只讀一次斜率 f(t_n, y_n),然後就沿著直線走完整個區間。但真正的解一路彎曲,所以只在左端取樣的斜率,等你抵達右端時早已過時。正是這一次過時的讀數,使 Euler 只是一階——它每步的局部截斷誤差是 O(h^2),換來令人失望的 O(h) 整體誤差。
顯而易見的補救,就是別盲目信任單一斜率。如果曲線在這一步裡彎了,為什麼不在步內的「好幾個」點取樣斜率再加以平均,讓你最後採取的那個直線動作能反映斜率沿途的變化?這正是 Runge-Kutta 方法 的全部精神:用區間內幾個精心挑選的子點上算出的斜率,以加權混合的方式踏出一步。關鍵在於,這仍是「單步」方法——從 y_n 推進到 y_{n+1} 只用到 y_n,不需要更早各點的記憶——所以它能自我起步,並在你改變 h 時從容應付。
回想上一篇指南:一個方法的「階」——也就是整體誤差 O(h^p) 中的 p——是描述它最重要的單一數字。Euler 是一階。每多一階,就意味著把 h 減半時誤差不是除以 2、而是除以 2^p,所以要達到嚴格的容忍度所需的步數會少得多。Runge-Kutta 的承諾,是不靠縮小步長、而是在每一步「內部」多花幾次函數計算,來買到高階——而 f 的函數計算通常是昂貴的部分,所以這筆交易必須划算才行。
暖身:中點法(RK2)
從最簡單的改良開始,只用兩次斜率讀數。先踏出一個試探性的 Euler 半步,去探測區間「中央」的斜率,而不是它過時的左端。把起點斜率叫做 k1 = f(t_n, y_n);用它跳到一半,到 t_n + h/2 與 y_n + (h/2) k1;然後讀「那裡」的斜率,k2 = f(t_n + h/2, y_n + (h/2) k1)。最後只用中點斜率 k2 踏出真正的整步:y_{n+1} = y_n + h k2。第一次讀數是鷹架,用完即丟;第二次取在弧線中段附近,才是我們真正踩上去的那個。
為什麼這就從一階跳到二階?精確的一步可由 y(t_n + h) 對 h 的泰勒展開捕捉。Euler 只對上 h^1 項,漏掉了 (h^2/2) y'' 這一項,那就是它的主導誤差。中點法的設計,正是要讓中點斜率在它自己的泰勒級數展開後,恰好重現那個漏掉的 h^2 項——兩次讀數被加權成使二階誤差互相抵消。於是它每步的局部誤差降到 O(h^3)、整體誤差降到 O(h^2)。如今把 h 減半,誤差是除以四、而非除以二。兩次斜率計算換一階:公平交易。
RK4:經典的四級主力
這個家族最有名的成員,就是經典的四階方法 RK4。它在這一步內取樣斜率四次,再把它們混成單一的加權平均。其中兩次讀數取在左端與右端,但兩次「中段」讀數——取在半步點、且各自精煉前一次——的權重是端點的兩倍。權重的樣式,1、2、2、1 除以總和 6,正是讓經典數值積分法準確的那條類 Simpson 規則,而這並非巧合:把 y' 在這一步上積分,本來就是一個數值積分問題。
Classical RK4, one step from (t_n, y_n) to (t_n+h, y_{n+1}):
k1 = f(t_n, y_n) # slope at the left edge
k2 = f(t_n + h/2, y_n + (h/2) k1) # slope at midpoint, using k1
k3 = f(t_n + h/2, y_n + (h/2) k2) # slope at midpoint, refined by k2
k4 = f(t_n + h, y_n + h k3) # slope at the right edge, using k3
y_{n+1} = y_n + (h/6) (k1 + 2 k2 + 2 k3 + k4)
Four evaluations of f per step. Local error O(h^5), global error O(h^4).把這份食譜當故事讀。k1 是起點處天真的 Euler 斜率。k2 用 k1 走到一半、在中點重讀斜率——對中央的第一個猜測。k3 重做中點,但這次從 k2 更好的資訊出發,所以是更銳利的中點估計。k4 用 k3 抵達遠端、在那裡讀斜率。最後的動作沿著加權平均前進,最倚重那兩個中點斜率,因為弧線的中段最能代表整一步。四次讀數,除了第一次外各自重用前一次,編織成一個自信的大跨步。
為什麼四階是大家都伸手去拿的甜蜜點?因為它是成本對階曲線上最划算的一筆:四次函數計算買到四階,正好一次換一階,這是你能拿到的最高比率。過了四階,階障就咬人了——五階需要 6 級,於是你付 6 次計算換 5 階,效率下滑。這就是為什麼 RK4 成了教科書的預設、也是無數物理、機器人與軌道模擬內部那具安靜的引擎:寫起來極簡、能自我起步、而且就其成本而言準得驚人。
Butcher 表:把每個 RK 方法寫成一張小表
上面所有的記帳工夫——在哪裡取樣、走多遠、如何加權——都可由一張 Butcher 表 緊湊地捕捉。它是一張小格表,裝著三樣東西:c 值(每個斜率沿著這一步在 h 的哪些分數處取樣)、a 值(每個較早的斜率有多少餵進下一個子步)、以及 b 值(把各斜率混成這一步的最終權重)。給我一張表,我就能機械式地寫出方法;這張表「就是」那個方法。
這張表也在兩個世界之間劃下一條清楚的界線。如果 a 格嚴格地是下三角,每個斜率只依賴已算好的斜率,於是你一個接一個地算它們——這是「顯式」方法,像 RK4。如果 a 格在對角線上或上方有元素,某個斜率就依賴它「自己」,於是你每一步都必須解一個方程式(往往是非線性方程組)才能求出它——這是「隱式」方法,像後向 Euler。隱式 RK 方法每步貴得多,但正如關於剛性的那篇指南將展示的,它買到顯式方法無論把 h 縮得多小都拿不到的穩定性。
這張表觀點一個優雅的回報,是 嵌入式 Runge-Kutta 對。挑一張表,使它的各級可以用「兩組」不同的 b 權重組合——比如一組給出四階、另一組給出五階——而共用完全相同的 k1...k_s 斜率計算。兩個答案之間的差距,就是局部誤差一個便宜、即時的估計,幾乎不費額外工夫,因為昂貴的斜率被重用了。那個誤差估計,正是下一篇指南裡自適應步長控制的燃料——求解器會在解狂野處縮小 h、在解平靜處拉長 h。
一個小小的算例步驟,與誠實的極限
用玩具問題 y' = y、y(0) = 1 把它具體化,它的精確答案是 y = e^t。踏出一個 h = 0.5 的 RK4 步。這裡 f(t, y) = y,所以每個斜率就是當前的 y 值。我們得到 k1 = 1,k2 = 1 + 0.25(1) = 1.25,k3 = 1 + 0.25(1.25) = 1.3125,k4 = 1 + 0.5(1.3125) = 1.65625。混合後,y_1 = 1 + (0.5/6)(1 + 2(1.25) + 2(1.3125) + 1.65625) = 1.6484375。精確值是 e^0.5 = 1.6487212...,所以單單一個半單位的步,RK4 就準到約四位小數。同樣 h 的普通 Euler 會給出 1.5——在「第一位」小數就錯了。品質的差距觸目驚心。
在你到處伸手去拿 RK4 之前,還有三個誠實的提醒。第一,四階精度仍是「近似」的、且活在浮點數裡:在浮點運算中每個斜率與每個加權和都會被捨入,所以一旦 h 夠小,O(h^4) 的截斷誤差就沉到不斷累積的捨入誤差之下,再縮小 h 就不再有幫助了——存在一個捨入地板。第二,採用「固定」步長時,RK4 根本不知道自己有多準;沒有嵌入式誤差估計就無法自適應,這正是為什麼接下來談 RK 對與自適應控制。第三,也最重要,RK4 是「顯式」的,所以只是條件穩定——對剛性問題,它的穩定極限會逼你用一個小到令人痛苦的 h,無論精度需要與否,而你必須改用隱式或 BDF 方法。那道懸崖,就是這一級最後一篇指南的全部主題。