一條漂亮公式底下的附帶條款
上一篇讓你目眩神迷:牛頓法在單根附近每一步都讓正確位數加倍,正是這種二次收斂使它成為數值計算的主力。但那個承諾包裹著三個容易被遺忘的安靜條件,一旦條件成立人們就忘了它們:根必須是單根(f'(根) 不為零)、你必須起步夠靠近、而且 f 在那裡必須光滑。牛頓法是個局部的奇蹟。抽掉其中任何一個條件,奇蹟就可能酸壞成發散、無止盡的繞圈,或一場信心滿滿、卻奔向錯誤答案的行軍。
記住牛頓法在幾何上究竟做了什麼會很有幫助。在當前猜測 x_n 處,它畫出 f 的切線,跳到那條切線與零相交之處:x_{n+1} = x_n - f(x_n)/f'(x_n)。當切線大致指向根時,這一跳精彩無比。但切線是個局部近似——它只知道一點上的斜率,對函數在別處的行為一無所知。一旦那幅局部圖景與 f 的整體形狀不合,切線就能把你拋向某個荒謬之地。底下每一種失靈模式,都只是一個「切線說了謊」的故事。
牛頓法欺騙你的四種方式
課本上牛頓法失靈模式的清單值得具體認識,因為每一種你終究都會踩到。平坦處衝過頭:若 f'(x_n) 極小,你就是在除以一個近乎零的斜率,下一個迭代值被甩到遠方——一條近乎水平的切線會在離你一整個大陸之遙處與零相交。繞圈:對某些函數,牛頓法永遠在兩點之間彈跳,A -> B -> A -> B;經典玩具是 f(x) = x^3 - 2x + 2 從 x_0 = 0 起步,它在 0 與 1 之間振盪,根本完全不收斂。
捕獲錯誤的根:牛頓法對你在意的那個根毫無忠誠。若你的起點落在錯誤的吸引盆裡,它會盡責地收斂——甚至很快——卻收斂到另一個完全不同的根,或者朝某條漸近線飛奔而去。重根:當根是重根、因而 f'(根) 也為零時,切線恰恰在你最需要它的地方變平。牛頓法仍會收斂,但那著名的二次速度會塌縮成僅僅線性,每一步只把距離折半、而非平方,慢吞吞地爬。殘忍的諷刺是:你最渴望速度的情形,正是把速度奪走的那一個。
割線法:不用導數的牛頓法
牛頓法還有一項我們一直忽略的代價:它要求 f'(x),一個你得親手推導或計算出來的真實導數。對一個雜亂的 f——一長串表達式,或一個只回傳數字的黑箱模擬——那個導數可能痛苦難求,或根本無從取得。割線法問了一個妙到極點的懶問題:要是我乾脆用「手上已有的兩點」來估計斜率呢?把真正的切線斜率 f'(x_n) 換成「通過最近兩個迭代值的割線斜率」,(f(x_n) - f(x_{n-1}))/(x_n - x_{n-1})。光是這一個替換,就把牛頓法變成一個根本不需要任何導數的方法。
Newton (needs the derivative f'):
x_{n+1} = x_n - f(x_n) / f'(x_n)
Secant (estimates the slope from two past points):
x_n - x_{n-1}
x_{n+1} = x_n - f(x_n) * -----------------------
f(x_n) - f(x_{n-1})
needs two starting guesses x_0, x_1
keeps only the two most recent points; one new f-evaluation per step幾何上這幅圖溫和而直觀。你不在單點畫切線,而是畫出「通過曲線上兩點的直線」,跳到那條線碰到零之處。隨著迭代值步步逼近,那兩點越靠越近,割線越來越貼合真正的切線,於是這方法開始表現得幾乎與牛頓法一模一樣——但你沒為它付出任何導數。那個幾乎二字,正是它跑多快的全部故事。
每步略慢,整體卻常更快
因為割線法用的是近似斜率,它的收斂階數不是牛頓法的 2,而是黃金比例,約 1.618——超線性,遠遠勝過二分法的線性 1,卻比二次低了一截。所以每一步它的進展比牛頓法略少。但這裡有個常常翻轉判決的轉折:每個牛頓步要花兩次函數呼叫(一次算 f,一次算 f'),而每個割線步只花一次(它重用上一步的 f 值)。若以「每次函數估值」來衡量——當 f 很昂貴時這才是誠實的計價單位——割線法的有效階數約 1.618,而牛頓法的有效階數是 2^(1/2),約 1.414。對一個昂貴的 f,這個懶方法可能率先抵達終點。
割線法繼承了牛頓法的天賦,也繼承了它的大半罪過。它仍只是局部收斂——糟糕的起點照樣能讓它發散或繞圈——而且和牛頓法一樣,它能交給你一個「f 很小」的點,卻從不保證你正落在「圍住根的真正括弧」裡。還有一個它自己獨有的危險:當 x_n 與 x_{n-1} 漂得很近時,分母 f(x_n) - f(x_{n-1}) 變成兩個幾乎相等之數的差,正是浮點裡災難性抵銷的課本場景。於是斜率估計恰恰在你逼近根時變得充滿雜訊,所以一條清醒的停止準則不可或缺,別把公式多跑一步。
把狂野的東西括起來:試位法與布倫特法
注意我們一再繞回的這筆交易。二分法慢,但有保證:它維持一個括弧 [a, b],f 在其兩端變號,所以根永遠被困在裡頭,而區間只能縮小。牛頓法與割線法快,卻無人看守:沒有東西攔得住它們乾脆躍出整個區域。自然的夢想是一個混血兒:像二分法那樣保住括弧,又像割線法那樣快步。第一個嘗試是試位法(false position):用割線公式,但永遠取在「變號括弧」的兩端之間,使根保持被困。它從不發散——但它可能卡住,懶懶地把一端固定不動、另一端一吋吋挪進,在偏斜的函數上退化成線性速度。
成熟的答案,也是你在真實軟體裡(MATLAB 的 fzero、SciPy 的 brentq)會遇到的預設,是布倫特法。它是個謹慎的裁判,全程維持一個有保證的變號括弧,但每一步它都試試那個快步——通過三點的反二次插值,或一個割線步——並且只有當結果安全落在括弧內、且確實在縮小括弧時才接受。若快步亂來,布倫特法就悄悄退回一個有保證的二分步。於是只要函數規矩,你就同時拿到二分法那鐵打的收斂保證與割線般的速度。這正是工程上的理想:永不比安全的方法慢,通常又和大膽的方法一樣快。