二分法是一种基本的数值方法,主要用于求解单变量函数的零点或者方程的根。然而,在一定的条件下,它也可以被扩展到多元方程求解中。在说算法以前我们先看一个简单的例子。


例: 使用二分法数值解方程 的近似根。首先令 ,画出 的图形:

In[]:=
1
2
3
f[x_, y_] = Exp[x - 2] - y;
g[x_, y_] = y^2 - x;
Plot3D[{f[x, y], g[x, y], 0}, {x, 0, 2}, {y, 0, 2}, AxesLabel -> {"x", "y", "z"}]
Out[]:=

三维图很难看出这两个面与 的交点,因此有必要画出 的等高图。此时当 ,则

In[]:=
1
ContourPlot[f[x, y]^2 + g[x, y]^2, {x, -1, 1}, {y, -1, 1}, Contours -> {0.01, 0.1}, ContourLabels -> True, AxesLabel -> {"x", "y"}]
Out[]:=

从图中可以发现方程的解范围为 ,于是有下面的 Mathematica 代码:

In[]:=
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fc = 0.1; gc = 0.1;

While[f[fc, gc] > 0.000001 || g[fc, gc] > 0.000001,
(*固定 gc,求解 fc*)
fa = -0.5; fb = 0.5; fc = (fa + fb)/2;
fn = 0;
While[Abs[g[fc, gc] ] > 0.000001 && fn < 1000,
If[g[fc, gc] == 0, Break[],
If[g[fa, gc] g[fc, gc] > 0, fa = fc, fb = fc]];
fc = (fa + fb)/2;
fn++
];

(*固定 fc,求解 gc*)
ga = -0.5; gb = 0.5; gc = (ga + gb)/2;
gn = 0;
While[Abs[f[fc, gc] ] > 0.000001 && gn < 1000,
If[f[fc, gc] == 0, Break[],
If[f[fc, ga] f[fc, gc] > 0, ga = gc, gb = gc]];
gc = (ga + gb)/2;
gn++
]
(*反复迭代一直到 f[fc,gc] 和 g[fc,gc] 都满足精度要求*)
]
{fc, gc}
Out[]:=


对比 Mathematica 自带函数 FindRoot[] 解的结果:

In[]:=
1
FindRoot[{f[x, y] == 0, g[x, y] == 0}, {{x, 1}, {y, 1}}]
Out[]:=

从上面的简单的例子可以看出,利用二分法数值求解二元方程组的时候,先固定其中一个未知数,假设先固定 ,求解另一个未知数 ;再固定 ,求解 ;然后依次交替求解,直到满足精度要求。


当然该方法可以应用到更多元方程组的情况。但它也存在一些局限性和缺点,二分法的收敛速度是线性的,每次迭代只能将区间长度减半,因此在接近根时,需要较多的迭代次数才能达到所需的精度。对于多元方程组来说,二分法的计算效率是非常低的。因此在实际应用中,一般采用其他数值解法,如牛顿法、拟牛顿法等。二分法的收敛性与初始区间的选择有很大关系,如果初始区间选择不当,可能会导致算法收敛缓慢或者无法收敛,对于多元方程组来说,初始区间的选择是很困难的一件事情。

因此,尽管二分法在某些情况下非常有效,但在实际应用中,需要根据具体问题特点和需求,选择更合适的方法,或者将二分法与其他数值方法结合使用(以后会讲解实际例子),以克服其缺点。