從一條方程到許多條:問題的形狀
這一級到目前為止的一切都活在一條線上:一個未知數 x、一條方程 f(x) = 0、一條曲線穿過一根軸。但真實世界很少只遞給你一條方程。機械手臂的兩個關節角度必須同時滿足兩條方程,手才會落在目標上。電路的各節點電壓必須一起滿足克希荷夫定律。一鍋化學混合物的各個濃度必須同時平衡好幾個反應。在每一種情形裡,你都有 n 個未知數疊成一個向量 x = (x_1, ..., x_n),以及 n 條方程疊成一個向量函數 F(x) = (f_1(x), ..., f_n(x)),而你要找的是讓它們一起歸零的那個 x:F(x) = 0。這就是本篇要談的非線性方程組。
立刻注意到我們失去了什麼。在一維裡,二分法靠的是一次變號——f 在這裡落在軸下、在那裡升到軸上,於是根被困在中間。在 n 維裡,沒有一根軸讓你分處兩側,沒有「跨區間異號」這個乾淨的概念,也根本沒有多維的二分法。那些給過我們鐵打保證的括住法,根本無法推廣。能推廣的,是牛頓法背後的那個想法:在你當前的猜測處,把彎曲的函數換成它最好的直線近似,把那個簡單的線性版本精確解出來,然後踏過去。在多維裡,「直線近似」變成一個平坦的平面——一個線性映射——而把它解出來,就變成解一個線性系統。
雅可比矩陣:一個身為矩陣的導數
在一維裡,牛頓步來自切線,其斜率是單一個數 f'(x)。當有 n 個函數、各自依賴 n 個變數時,「斜率」不再是一個數——它是一整格的偏導數。雅可比矩陣 J(x) 把它們收集起來:它第 i 列、第 j 行的元素,是第 i 個函數 f_i 對第 j 個變數 x_j 的偏導數。第 i 列是 f_i 的梯度,告訴你當你各別輕推每個變數時、那一條方程如何反應;第 j 行則告訴你當你只輕推變數 x_j 時、所有方程如何反應。對 n = 2,它是一個 2 乘 2、含四個偏導數的格子;對 n 個未知數,它是一個 n 乘 n 的矩陣。
雅可比矩陣扮演的,正是先前 f'(x) 扮演的角色。在某點 x_n 附近,函數被它的線性化很好地近似:F(x) 約等於 F(x_n) + J(x_n) * (x - x_n)。這就是多維的切線——一個在 x_n 處貼著彎曲曲面的平坦平面。要找這個平坦近似在哪裡歸零,把右邊設為零、解出步長 s = x - x_n。這給出牛頓步的定義方程:J(x_n) * s = -F(x_n),一個喬裝過的線性系統 A x = b,其中雅可比矩陣當 A、函數值取負當 b、步長 s 是未知數。
one dimension: x_{n+1} = x_n - f(x_n) / f'(x_n)
n dimensions: solve J(x_n) * s = -F(x_n) for the step s
then x_{n+1} = x_n + s
(division by f'(x_n) becomes: SOLVE a linear system with J(x_n))為什麼每一步都是一次線性求解——以及為什麼這很要緊
美妙的統一來了。純量牛頓更新除以 f'(x_n);向量版本沒法除以一個矩陣,所以「除法」變成解 J(x_n) * s = -F(x_n)。突然之間,你在線性系統那一級學的一切,全都湧回求根這一級。你不去計算矩陣的逆——你解那個系統,幾乎總是用 LU 分解(高斯消去),它把 J = L U 分解一次,然後靠一次快速的前代與回代算出 s。如果雅可比矩陣在迭代之間幾乎不變,你甚至可以連用一個過時的分解好幾步,用一點點收斂速度,換取每一步少得多的工作量。
而線性系統那一級的警告也一併回來了——它們不是可有可無的細則。牛頓步可信的程度,頂多就是產生它的那次線性求解可信的程度,而那次求解繼承了雅可比矩陣的條件數。如果 J(x_n) 接近奇異——這恰恰發生在退化根或重根附近,正是一維裡 f'(x) 接近零的多維表親——那一步就會爆掉、或指向一個離譜的錯誤方向,就像一維牛頓步在 f'(x_n) 極小時爆炸那樣。準確度等於條件數乘以穩定度:當雅可比矩陣的條件數達到 10^8 時,一次穩定的 LU 求解仍會丟掉你約十六位雙精度數字裡的大約八位,再聰明的程式碼也買不回那些數字。每一步的答案都是浮點數網格上的一個近似;雅可比矩陣的條件數決定了它最好能近似到什麼程度。
演算法,一步一步走
剝到只剩骨架,方程組牛頓法就是那個純量迴圈,只是把「除法」換成「解一個線性系統」。從一個起始猜測出發,在那裡建出函數值與雅可比矩陣,解出步長,踏出去,重複直到 F(x) 夠小。唯一真正全新的機件,是每次迭代要算出那個 n 乘 n 的雅可比矩陣、並做一次線性求解——這兩樣你在前面的階梯都已經見過。
- 選一個起始猜測 x_0。一如一維情形,牛頓法只是局部收斂——一個好的起點極其重要,一個壞的起點可能發散。
- 在當前的 x_n 處,求出函數向量 F(x_n),並組出由偏導數構成的雅可比矩陣 J(x_n)。
- 解線性系統 J(x_n) * s = -F(x_n) 求出步長 s——用 LU 分解(高斯消去),絕不去組逆矩陣。
- 更新 x_{n+1} = x_n + s。
- 檢查停止測試:||F(x_{n+1})|| 是否低於容忍度、步長 ||s|| 是否夠小?是就停;否就回到步驟 2。設一個迭代上限,使發散的執行不會永遠繞下去。
把雅可比矩陣弄對,是人們最常絆倒的地方。手工推導 n^2 個偏導數容易出錯,而單單一個錯誤元素,會悄悄毀掉二次收斂、卻不會讓程式報錯——它只是改以線性速度蹣跚前行、或乾脆卡住。所以實務上,雅可比矩陣盡可能由自動微分提供,或用有限差分近似:把每個變數輕推一個小小的 h,觀察 F 如何變化。但要當心——浮點數那一篇的捨入下限在這裡同樣適用。h 太大會帶來截斷誤差;h 太小則讓差值淹沒在捨入誤差裡,因為你正在相減兩個幾乎相等的向量。最佳點落在機器 epsilon 的平方根附近,再往小走並不會持續有幫助。
速度、失敗,以及更便宜的表親
這一切機件的回報,是牛頓法在一維裡展現過的同一種驚人速度:在一個乖巧的根附近——一個雅可比矩陣非奇異的單根——收斂是二次的,每次迭代大致讓正確數字的位數加倍。幾步就能把你從一個粗糙的猜測帶到機器精度。這正是為什麼方程組牛頓法,是幾乎每一個認真的非線性方程求解器內部的預設引擎,從電路模擬器、到天氣模型、到僵硬微分方程逼我們使用的隱式時間推進器。
但純量那一篇的每一種失敗模式,都熬過了向多維的跳躍,還冒出幾種新的。從遠處出發,牛頓法可能發散、衝過頭、或晃進一個雅可比矩陣變奇異、步長爆炸的區域。誠實的補救,是純量補救的多維版本:阻尼或線搜尋牛頓法,它沿著牛頓方向、但縮短步長(x_{n+1} = x_n + t*s,其中 0 < t <= 1),直到 F 真的變小,用一點二次速度,換來一個大得多、能收斂的起點盆地。像這樣的全域化牛頓法,正是讓那個二次的局部速率,能從一個現實、不完美的起始猜測派上用場的東西。二次收斂是關於根的鄰域的承諾,從來不是「從任何地方都成立」的承諾。
牛頓法還逼你面對一個成本問題。每一步都需要一個新鮮的雅可比矩陣(n^2 個偏導數)與一次線性求解(對稠密系統,用高斯消去約 O(n^3) 的工作量)。當雅可比矩陣昂貴或拿不到時,割線法的多維對應就接手了:像 Broyden 法這樣的擬牛頓法根本不去計算 J。它們從一個粗略的雅可比矩陣出發,只用你本來就在收集的函數值、便宜地更新它,正如割線法用穿過兩個最近點的斜率取代了 f'(x_n)。你放棄精確的二次收斂,換來某種快速、而且每一步便宜得多的東西——當每個雅可比矩陣的代價超過它省下的迭代時,這往往是更划算的交易。
懂得何時該停,以及把整級收進一個畫面
停止比一維裡更微妙,因為沒有一個括住區間、能讓它的寬度誠實地界定誤差。常見的停止準則同時盯著兩樣東西:殘差 ||F(x_n)||(各方程離平衡有多近)與步長 ||x_{n+1} - x_n||(猜測還在動得多小),各自相對於尺度來量,只有兩者都小時才停。只盯殘差,可能被一個病態的雅可比矩陣騙到,那裡一個微小的殘差藏著一個沒那麼微小的誤差;只盯步長,可能在方法不過是放慢時就提早停下。而那個下限,跟這整個階梯各處相同:你無法把 ||F|| 壓到浮點捨入誤差與問題條件數所設下的水平之下,所以越過那個點去追額外的數字,只是在燒迭代。
退一步,整個一級就喀地合進一個畫面。二分法給了保證,卻只有線性速度、而且只在一維。不動點迭代揭示了簡單迭代到底在什麼收縮條件下才會收斂。牛頓法靠著信任切線買到了二次速度,而那篇「失敗與割線法」則展示了那份信任如何會破、以及如何軟化或繞過它。這最後一篇把完全相同的牛頓想法帶進 n 維,那裡切線斜率變成雅可比矩陣、除法變成一次線性求解——這正是為什麼線性代數各級裡的條件數與穩定度課題,並不是一個獨立的主題,而是非線性求根那顆跳動的心臟。把 A x = b 解好,你就能把 F(x) = 0 解好;數值計算的兩半,恰恰在這裡相遇。