# 牛顿法 · 切线的力量，与它翻车的时候

> 牛顿法顺着切线滑向根，每步让正确位数翻倍——二次收敛，快得惊人。这一章手写它、打印「位数翻倍」的收敛表，也诚实展示它翻车的三种方式：导数为零、跑飞、来回震荡。快与险，是它的一体两面。

- 作者：David（道雾轩）
- 专栏：数值计算的艺术（https://daiw.org/manual/numerical-computing.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/numerical-computing/newton
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 牛顿法 · 切线的力量，与它翻车的时候

二分法慢，因为它只看 $f$ 的**符号**。牛顿法（Newton's method）快，因为它还用了 $f$ 的**斜率**——顺着切线滑，一步到位得多。

## 思想：沿切线滑到 x 轴

在当前猜测 $x_n$ 处，画一条 $f$ 的**切线**，看这条切线和 x 轴的交点，把它当作下一个、更好的猜测 $x_{n+1}$。切线方程一解，就得到牛顿法的迭代公式：

$$
x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}
$$

直觉很美：如果 $f$ 在根附近接近一条直线，那么「沿切线滑到 x 轴」几乎就直接命中根了。

## 手写 + 「位数翻倍」的收敛表

还是求 $\sqrt{2}$（$f(x)=x^2-2$，$f'(x)=2x$）：

```python
def newton(f, df, x0, tol=1e-14, maxiter=50):
    x = x0
    for _ in range(maxiter):
        fx = f(x)
        if abs(fx) < tol:
            return x
        x = x - fx / df(x)          # 沿切线滑
    return x

import math
root = newton(lambda x: x*x - 2, lambda x: 2*x, x0=1.0)
print(root, abs(root - math.sqrt(2)))
```

打印每步的误差，和二分法对比，差距惊人：

```text
step   x_n                       误差 |x_n - √2|
0      1.00000000000000          4.1e-01
1      1.50000000000000          8.6e-02
2      1.41666666666667          2.5e-03
3      1.41421568627451          2.1e-06
4      1.41421356237469          1.6e-12
5      1.41421356237310          0.0
```

**看误差：0.41 → 0.086 → 0.0025 → 2e-6 → 1.6e-12——每一步大致是上一步的平方。** 正确位数 $1\to2\to5\to11$ 大致翻倍。这叫**二次收敛（quadratic convergence）**：

$$
e_{n+1} \approx C\, e_n^{2}
$$

二分法砍 40 刀才到的精度，牛顿法**5 步**就到了。一旦进入根附近，它快得没道理。

## 但它会翻车

牛顿法的快，是有代价的——它**不像二分法那样保证收敛**。三种经典翻车方式：

<Callout type="warn">
  **① 导数为零：除以 0。** 公式里除以 $f'(x_n)$，一旦迭代点撞上 $f'(x)=0$（函数的水平切线），就除零崩溃，或切线几乎水平、把下一点甩到十万八千里。

  **② 起点不好：跑飞或跑到别的根。** 牛顿法对初值敏感。起点离根太远、或函数有多个根 / 拐点，切线可能把你**导向发散**，或**收敛到一个你没想要的根**。

  **③ 来回震荡：陷入循环。** 某些函数（如 $f(x)=x^3-2x+2$ 从 $x_0=0$ 出发）会让牛顿迭代在两个点之间**无限来回跳**，永远不收敛。
</Callout>

用代码亲眼看一次发散——对 $f(x)=\arctan(x)$ 从一个稍远的起点出发：

```python
# arctan 的牛顿迭代,起点稍远就发散
f  = lambda x: math.atan(x)
df = lambda x: 1 / (1 + x*x)
x = 2.0
for i in range(6):
    x = x - f(x) / df(x)
    print(f"step {i+1}: x = {x:.4f}")
# step 1: x =  -3.5357
# step 2: x =   13.95
# step 3: x = -279.34    ← 越跑越远,发散
```

切线把 $x$ 一步步甩得越来越远——**牛顿法飞了**。同样的方法，起点 $x_0=1$ 时几步收敛，$x_0=2$ 时直接发散。**快和险，是一体两面。**

## 实用建议：防翻车

<Callout type="info">
  **工程上很少「裸用」牛顿法，而是给它加保险：**

  - **限制步长 / 加阻尼**：如果一步跳得太远（比如 $|x_{n+1}-x_n|$ 反而变大），就把步长砍半，别让它飞。
  - **和二分法混用（safeguarded Newton）**：牛顿步若跳出了「夹住根的区间」，就退回去做一步稳的二分。这样既有牛顿的快、又有二分的**绝不失手**——鱼和熊掌兼得。成熟的库（`scipy.optimize.brentq` 等）内部就是这类**混合方法**。
  - **不想手算导数**：用[下一章](https://daiw.org/manual/numerical-computing/iteration-order)的**割线法**——它用差商近似导数，不需要 $f'$，收敛也几乎和牛顿一样快。

  **口诀：牛顿法快，但要「牵着绳子放」——给它兜底，别让它裸奔。**
</Callout>

## 小结与下一章

牛顿法沿切线滑向根，$x_{n+1}=x_n-f(x_n)/f'(x_n)$，**二次收敛**——每步正确位数翻倍，快得惊人。但它**不保证收敛**：导数为零会崩、起点不好会跑飞、某些函数会震荡。工程上要加保险（限步长、和二分混用、或用割线法免导数）。

牛顿是「二次收敛」，二分是「线性收敛」——这个「收敛阶」到底怎么定义、怎么测？还有一个「不用导数、却快过二分」的割线法，收敛阶是有趣的黄金比 $1.618$。下一章把收敛阶这把尺子讲清。👉 [不动点与割线法：收敛阶怎么量](https://daiw.org/manual/numerical-computing/iteration-order)。

## 参考文献

- [1] Burden & Faires.《Numerical Analysis》, §2.3 —— 牛顿法与收敛性。
- [2] Tim Sauer.《Numerical Analysis》, §1.4 —— 牛顿法的发散与保险策略。
- [3] SciPy 文档.「scipy.optimize.newton.」https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html —— 库实现(含割线回退)。
