当我们求关于的方程
的解时,如果,
是像
这样的线性形方程(1次方程)的话,其解为,
这里的。但是,
是非线性方程的时候解法要复杂的多。比如,像下面这样的
次代数方程(algebraic equation)的情况,
的2次方程我们很容易求解,3次或4次方程可以通过Cardano公式或者Ferrari公式求解,然而5次以上却无法直接求解。
更不用说,还有像超越方程式(transcendental equation)这样的无穷次的代数方程()。例如,
用无穷幂级数表示的形式便是超越方程。超越方程一般无法直接求解,只能求近似解。
本文,我们对如下的非线性方程的数值求解方法进行逐一介绍。
假设是在区间
上的函数。
和
符号相反的时候(
),区间
内至少存在一个解。我们先求区间
的中点的函数值
,如果这个值的符号与
同号, 则在
内存在解。 这样的话,解的存在区间便变成了原来的
。同样,再使用
的中点的函数值,解的存在区间就变成了
。把上述步骤反复处理求得方程式的解的方法就叫做2分法(bisection method)。
图1为2分法求近似解的说明图。图中解的存在区间为。用
对
进行初始赋值。 这时的中点为
。 由于图1
,解在
内存在, 令
,这时的中点为
, 由图1
,解在
内存在。上述步骤反复迭代,求
。
2分法的处理顺序如下:
迭代终止的收敛条件如下:
下面为2分法的代码示例。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
用2分法虽然可以求出指定区间内的解,但是收敛太慢。如果是区间内连续可微的函数的话,用函数的梯度值可以更快收敛。
解的初期值设为。在
的函数
的切线为,
这条切线与相交(
)的点为,
把作为下一次的近似值,用同样方法逐渐向解趋近。上述处理顺序如图2所示。这种方法便称作牛顿-拉弗森法(Newton-Raphson method),或者牛顿法(Newton method)。牛顿法的公式如下:
迭代次数足够多的话会无限趋近的解。 迭代终止的收敛条件为,
牛顿法只指定一个初始值,并且收敛速度比2分法快很多。然而,这种方法需要求及其微分值
,而且根据设定的初始值,也存在解无法收敛的情况。
牛顿法代码示例如下:
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 26 27 28 29 |
|
把牛顿法表示成联立非线性方程的一般化形式:
向量表示为,
这里的,
第步的近似值表示成
,把上式用
的附近点进行泰勒展开:
这里的是
的雅可比行列。 忽略掉2次以上的项,联立非线性方程变为:
设,可得
这个式子是以为未知数的线性联立方程,通过LU分解等解法可以得到
。 然后再按照下式计算
。
然后,
由此,与相关的公式如下:
求解,得:
然后再更新。
首先我们来计算一下用2分法或牛顿法解下述代数方程的运算次数,
计算的值时,乘法运算
次,加法运算
次,乘法运算次数呈
的2次增加,比如,
时55次,
时5050次。
为了减少乘法运算次数,对原式做如下变形:
此时,从最里面的括号内开始计算的话,可知
最后。 此时的乘法运算次数是
次,加法运算也是
次。当
很大时,计算次数可以大幅减小。例如当乘法运算次数
时为10次,
时为100次。这种计算方法便被称作霍纳(Horner)。
用霍纳法求代数方程的值及导函数的代码示例如下。
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 26 27 28 29 30 31 32 |
|
示例函数使用了复数计算,可以对应各种类型的方程。
代数方程包括重根在内有复数解(实数也是复数的一部分)。以上的方法,通过赋予合适的初始值和范围求得了一个解。当求代数方程的复数解时不得不重新设定初始值和检索范围。以下对求
个复数解的方法进行讲解。
首先,有复数解的代数方程用未知数的表示如下:
用牛顿法分析代数方程。上述方程的解表示成,方程发生如下变形:
将其微分,
当等于
时,由于
,上式只剩下第一项。同理
时左边仅剩第
项。用公式表示,即:
这里的。由牛顿法公式,
用
的各近似值逼近,
这里的。这个式子就是Weierstrass逼近法,或者,称作Durand-Kerner法[1]。以下把上式简称DK公式。
虽然我们可以通过DK公式计算个复数解,由于使用了牛顿法,有必要选择一个合适的
的初始值
。这里,我们使用下面的(Aberth)的初始值。
这里的,
是复数平面上包含这
个解的圆的半径。这里的
为
。
我们把使用Aberth的初始值,通过DK公式的迭代计算,获得个根的方法叫做DKA(Durand-Kerner-Aberth)法。
关于Aberth初始值半径的求法有很多种,这里介绍的是最初Aberth提议的方法[2]。 令得到以下方程。
这里的是通过霍纳法获得的系数。
且所有系数
非零时,
的解在,求
的方程时以解为半径的圆内存在。由此,
在
内有解。使用计算
的初始值的半径。 由于
有单一的实数解,所以使用牛顿法求解。
使用DKA法计算多个根的代码如下。首先是计算Aberth初始值的函数。
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 26 27 28 29 30 31 32 33 34 |
|
这里由于涉及到了复数计算,所以使用C++中的complex库。
#include// 复数 typedef complex complexf;
计算半径时,的系数计算用到的霍纳函数如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
得到初始值后,利用以下的Weierstrass函数求解。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
下面推导一下代数方程的解与系数之间的关系。首先,以2次方程为例。
使用解把方程改写成如下形式。
与之前的系数做对比,得到下面的关系式。
同理,n次方程表示如下,
于是,n次方程的解与系数的关系为,
这里的。如,当方程为3次方程时(
),
由时的关系式,
Aberth的初始值中,把解所在复数平面的重心:
当作中心的半径为的圆周上等间距的配置
。也就是说,
由欧拉公式(),可得:
这里的为
。另外,角度值加上
是为了防止,初始值
在实轴上对称分布(防止成为共轭复数)。
[1] http://en.wikipedia.org/wiki/Durand%E2%80%93Kerner_method
[2] O. Aberth, ``Iteration methods for finding all zeros of a polynomial simultaneously'', Math. Comput. 27, pp.339-344, 1973.