4. 二分法

二分法(Bisection Method)是求解单变量非线性方程 f(x)=0 最古老、最稳健的数值方法之一。它不依赖导数信息,也不需要良好的初值猜测,只要能在某个区间上确认函数值异号,就能以可预测的速度把根“夹”出来。正因如此,二分法常被用作其他快速迭代法的“保底”手段,也是工程中区间搜索与根的初步定位的首选工具。

一、原理:连续函数介值定理的直接应用

设函数 f(x) 在闭区间 [a, b] 上连续,且满足 f(a)·f(b) < 0,即两端点函数值异号。由连续函数的介值定理,区间内至少存在一点 x* 使 f(x*)=0。二分法的做法非常朴素:取中点 m=(a+b)/2,计算 f(m),然后判断根落在哪一半:

  • 若 f(a)·f(m) < 0,则根在 [a, m],令 b ← m;
  • 若 f(m)·f(b) < 0,则根在 [m, b],令 a ← m;
  • 若 f(m)=0(或 |f(m)| 小于容差),则 m 即为根,直接终止。

每迭代一次,包含根的区间长度减半。这种“每次砍掉一半”的确定性收缩,是二分法收敛性可严格证明的根本原因。

二、算法流程与实现要点

下面给出一个可直接运行的 Python 实现,包含区间合法性检查、迭代上限保护与误差记录。示例中求解 f(x)=x³−x−2,在区间 [1, 2] 上 f(1)=−2、f(2)=4,满足异号条件。

def bisection(f, a, b, tol=1e-6, max_iter=100):
    fa, fb = f(a), f(b)
    if fa * fb > 0:
        raise ValueError("区间端点函数值同号,无法保证有根")
    history = []
    for k in range(1, max_iter + 1):
        m = 0.5 * (a + b)
        fm = f(m)
        history.append((k, a, b, m, fm, (b - a) / 2))
        if abs(fm) < tol or (b - a) / 2 < tol:
            return m, k, history
        if fa * fm < 0:
            b, fb = m, fm
        else:
            a, fa = m, fm
    return 0.5 * (a + b), max_iter, history

f = lambda x: x**3 - x - 2
root, iters, hist = bisection(f, 1.0, 2.0, tol=1e-6)
print(root, iters)

实现时有几个容易踩坑的细节:其一,判断分支应使用 fa·fm < 0 而不是 fm·fb < 0 的随意组合,必须与更新 a 还是 b 保持一致;其二,终止条件建议同时检查 |f(m)| 与区间半长,前者防止函数值恰好过小造成误判,后者保证根的定位精度;其三,务必设置 max_iter,避免因容差过小或浮点舍入导致死循环。

三、收敛性证明与误差估计

设初始区间长度为 L₀=b₀−a₀。第 k 次迭代后区间长度为 Lₖ=L₀/2ᵏ。由于根 x* 始终被包含在区间 [aₖ, bₖ] 内,取中点 mₖ 作为近似值,则误差满足:

|mₖ − x*| ≤ Lₖ/2 = L₀ / 2^(k+1)

这说明二分法是线性收敛的,收敛比恒为 1/2,与函数形态无关,只与初始区间长度有关。由此可反解所需迭代次数:若要求误差不超过 ε,则需

k ≥ log₂(L₀/ε) − 1

例如 L₀=1、ε=10⁻⁶ 时,约需 k≈19 次迭代。这一“先验估计”是二分法最实用的性质之一:在动手计算前就能预知计算量,便于安排计算资源与设定容差。

二分法:区间逐次减半与误差上界 迭代次数 k 区间长度 1 1/2 1/4 1/8 1/16 1/32 1/64 k=0 k=1 k=2 k=3 k=4 k=5 k=6 误差上界 L₀/2^(k+1) 每步区间减半 收敛比恒为 1/2 与函数形态无关

四、误差控制策略

实际工程中,误差控制不能只看区间长度。常见做法是设置双重判据:|f(m)| < ε_f 与 (b−a)/2 < ε_x,任一满足即停止。前者对应“函数值足够接近零”,后者对应“根的定位足够精确”。当函数在根附近非常平坦时,|f(m)| 可能很早就很小,但 m 与真实根仍有距离,此时应以 ε_x 为主;反之,当函数陡峭时,ε_f 更敏感。此外,浮点数在区间极小时会出现 a 与 b 相等或中点无法再细分的情况,应把 (b−a) 与机器精度比较作为兜底终止条件。

容差选择建议:若问题对根的绝对位置要求为 10⁻⁶,则 ε_x 取 10⁻⁶ 即可;若函数值量级很大(如 10⁶),ε_f 应相应放大,否则会因浮点舍入提前终止。

五、在区间搜索中的实际应用

二分法最典型的工程用法不是直接求根,而是“先扫描、再精化”。例如在电路工作点分析中,需要找出使某支路电流为零的偏置电压;在热平衡计算中,需要找到使净热流为零的温度。做法是:先在物理合理范围内以固定步长扫描,记录所有函数值变号的小区间,再对每个变号区间调用二分法精化。这样既避免了漏根,又保证了每个根的定位精度。

以一个示例说明:设 f(x)=x³−x−2,在 [1,2] 上扫描步长 0.5,得到 f(1)=−2、f(1.5)=−0.125、f(2)=4,变号发生在 [1.5,2]。对该区间执行二分,约 20 次迭代后可得根约 1.52138(示例结果,实际值随容差略有差异)。若扫描步长过大,可能跨过两个相邻根而漏检;步长过小则计算量上升。经验上,步长应小于预估根间距的一半。

二分法要求区间端点严格异号。若 f(a)·f(b)=0,说明端点本身就是根,应直接返回;若函数在区间内不连续(如含分母零点),介值定理不成立,二分法可能收敛到伪根,使用前必须确认连续性。

综上,二分法的价值在于“确定性”:收敛速度虽慢于牛顿法,但每一步都可预测、可证明、可兜底。在区间搜索、根的初步定位以及作为混合算法的安全网时,它仍然是数值求根工具箱中不可替代的基础方法。