跳轉到

6.1 數值微分#

概念說明#

導數的定義 f′(x) = lim_{h→0} [f(x+h) − f(x)]/h 在電腦上不能直接實作,因為「極限」需要無限小,而浮點數的精度有限。數值微分用有限差分(finite difference)取代極限,常用的三個公式:

  • 前向差分:(f(x+h) − f(x))/h,泰勒展開後誤差為 −f″(ξ)h/2,是 O(h)。
  • 中央差分:(f(x+h) − f(x−h))/(2h),誤差為 f⁗(ξ)h²/6,是 O(h²)。
  • 五點中央差分:(−f(x+2h) + 8f(x+h) − 8f(x−h) + f(x−2h))/(12h),誤差 O(h⁴)。

中央差分之所以更準,是因為泰勒展開中 f 的偶數階項正負相消——這是「對稱性換精度」的第一課。

但數值微分有一個與眾不同的陷阱:誤差不是單調下降的。捨入誤差大致與 ε/h 成正比(h 越小,相減的兩個數越接近,相對誤差越大,即「災難性抵銷」,見 1.2 節),而截斷誤差與 hᵖ 成正比。總誤差 E(h) ≈ C₁hᵖ + C₂ε/h 有一個最佳點,例如前向差分的最佳步長約 h* ≈ √ε ≈ 10⁻⁸,中央差分約 h* ≈ ε^(1/3) ≈ 10⁻⁵~10⁻⁶。盲目把 h 設成 1e-15 反而會得到全是雜訊的導數。

演算法步驟#

  1. 選定差分格式(建議中央差分)與步長 h。
  2. 計算 f 在 x ± h(及更遠節點)的值。
  3. 套加權公式得近似導數。
  4. 若需要更高可信度,用兩個不同 h 各算一次,比較差異(Richardson 外推見 7.3 節)。

Python 程式碼範例#

import math

def forward_diff(f, x, h):
    return (f(x + h) - f(x)) / h

def central_diff(f, x, h):
    return (f(x + h) - f(x - h)) / (2 * h)

def five_point_diff(f, x, h):
    return (-f(x+2*h) + 8*f(x+h) - 8*f(x-h) + f(x-2*h)) / (12*h)

def auto_central_diff(f, x):
    """自動選擇接近最佳的步長"""
    h = (abs(x) + 1) * 6e-6        # 經驗上接近 eps**(1/3)
    return central_diff(f, x, h)

f = math.sin
x = 1.0
exact = math.cos(x)
print(f"真值 cos(1) = {exact:.15f}\n")
print(f"{'h':>8} {'前向誤差':>12} {'中央誤差':>12} {'五點誤差':>12}")
for h in [1e-1, 1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12]:
    ef = abs(forward_diff(f, x, h) - exact)
    ec = abs(central_diff(f, x, h) - exact)
    e5 = abs(five_point_diff(f, x, h) - exact)
    print(f"{h:8.0e} {ef:12.3e} {ec:12.3e} {e5:12.3e}")

print("\n自動步長中央差分:", f"{auto_central_diff(f, x):.15f}")

# 高階與符號微分對照
import sympy as sp
X = sp.symbols("x")
g = sp.exp(sp.sin(X))
print("sympy 精確二階導在 x=1:", float(sp.diff(g, X, 2).subs(X, 1)))

執行後觀察誤差表:前向差分在 h = 10⁻⁸ 左右觸底後回升,中央差分在 10⁻⁵~10⁻⁶ 觸底——兩難與最佳步長理論完全吻合。

誤差分析#

設 f 足夠光滑。前向差分:E(h) = |f′(x) − Df| = |−f″(ξ)h/2| + O(ε/h),最佳 h* ≈ √(4εM/f″) 時 E ≈ O(√ε) ≈ 10⁻⁸——前向差分最多只有約 8 位精度。中央差分:E(h) = |f⁗(ξ)h²/6| + O(ε/h),最佳 h* ≈ (3εM/f⁗)^(1/3),E ≈ O(ε^(2/3)) ≈ 10⁻¹⁰~10⁻¹¹。五點格式可達 O(ε^(4/5))。這解釋了為什麼高階格式值得學:它把「捨入誤差地板」抬得更低。另一個務實建議:如果函數可以符號化,用自動微分或 sympy 直接算精確導數,數值微分只留給純黑箱函數(例如模擬器輸出);另外注意中央差分對 f 的評估點成對出現,天然適合複數步長技巧(complex-step differentiation),可把誤差壓到機器精度。

練習#

  1. 對 f(x) = e^x 在 x = 0 畫出 log(h) 對 log(誤差) 的曲線,標出前向與中央差分的理論斜率(1 與 2),驗證實測斜率一致。
  2. 實作 complex-step 法:f′(x) ≈ Im[f(x + ih)]/h,h = 1e-20,與中央差分比較可達到的最高精度,並解釋為何它不受災難性抵銷影響。
  3. 寫一個函數 second_derivative(f, x, h) 用中央差分計算 f″(套兩次一階公式),對 f(x) = x⁴ 在 x = 2 驗證,並討論其二階導的誤差階為何只有 O(h²)。