文章目录
- root_scalar
- 参数差异
- 测试
root_scalar
scipy.optimize
提供了统一的一元函数求根方法,其函数定义为
scipy.optimize.root_scalar(f, args=(), method=None, bracket=None, fprime=None, fprime2=None, x0=None, x1=None, xtol=None, rtol=None, maxiter=None, options=None)
其中,f
是待求根的函数,args
为除了待求的自变量之外的其他函数。bracket
为函数的两个异号端点,x0
和x1
为可能用到的根的估计值。
xtol, rtol
与精度有关,越小越精,maxiter
为最大迭代次数。
其中,method
为最关键的参数,用来决定root_scalar
采用的求根方法,其中一部分方法在optimize
中实现了独立的函数,下表列出了这些方法的对应关系
参数 | 估计参数 | 独立方法 | 备注 |
---|---|---|---|
'bisect' | bracket | 'bisect' | 二分求根 |
'brentq' | bracket | 'brentq' | brent方法 |
'brenth' | bracket | 'brenth' | brent方法+双曲外推 |
'ridder' | bracket | 'ridder' | ridder方法 |
'toms748' | bracket | 'toms748' | 我也不知道什么方法 |
'newton' | x0 | 'newton' | 牛顿法 |
'secant' | x0, x1 | - | 弦截法 |
'halley' | x0 | - | 三阶方法 |
root_scalar
的返回值是一个RootResults
对象,里面的.root
属性,即为所求的根。
具有独立函数的求根算法,参数与root_scalar
大同小异,以bisect
为例,其参数为
bisect(f, a, b, args=(), xtol=2e-12, rtol=8.881784197001252e-16, maxiter=100, full_output=False, disp=True)
其中,args, xtol, rtol, maxiter, full_output, disp
为所有求根算法函数共有的参数,除此之外,求根函数的参数列表如下
toms748(f, a, b, k=1)
bisect(f, a, b)
brentq(f, a, b)
brenth(f, a, b)
ridder(f, a, b)
newton(func, x0, fprime=None, fprime2=None, x1=None)
其中,(a,b)
相当于root_scalar
中的bracket
,其他参数与root_scalar
中含义相同。
参数差异
之所以会有(a,b)
或是x0
这种参数差别,可从二分法和牛顿法的原理来一窥一二。
二分法是最简单的求根算法,如果已知 f ( a ) ⋅ f ( b ) < 0 f(a)\cdot f(b)<0 f(a)⋅f(b)<0,则快速选取 c = a + b 2 c=\frac{a+b}{2} c=2a+b,如果 f ( a ) ⋅ f ( c ) < 0 f(a)\cdot f(c)<0 f(a)⋅f(c)<0,则说明根在 ( a , c ) (a,c) (a,c)之间,否则即说明根在 ( a , b ) (a,b) (a,b)之间,然后不断迭代下去。为了完成二分法,则必须输入一对(a,b)
。
牛顿法则利用了导数,其迭代公式可表示为
x k + 1 = x k − f ( x k ) f ′ ( x k ) x_{k+1}=x_k-\frac{f(x_k)}{f'(x_k)} xk+1=xk−f′(xk)f(xk)
为了执行牛顿法,则必须给一个初始的 x 0 x_0 x0。
测试
下面随便写一个函数,来测试这些求根方法
import scipy.optimize as sodef func(x):return x*x - 2so.bisect(func, 0, 2)
# 1.4142135623715149
so.newton(func, 0)
# 1.4142135623715149
这两种方法可以写成等价的root_scalar
的形式
so.root_scalar(func, bracket=(0,2), method='bisect')
'''返回值converged: Trueflag: 'converged'function_calls: 42iterations: 40root: 1.4142135623715149
'''
由于root_scalar
返回了迭代次数,故而可用这个函数,测试一下不同方法的运算效率。
method=['bisect', 'brentq', 'brenth', 'ridder', 'toms748', 'secant']
bk = (0,2)
x0, x1 = 0, 2res = []
for m in method:print(m)res.append(so.root_scalar(func, method=m, bracket=bk, x0=x0, x1=x1))for r,m in zip(res, method):print(m, r.iterations, r.root**2-2)
最后结果如下
bisect 40 -4.469535852535955e-12
brentq 8 1.1723955140041653e-13
brenth 8 3.9968028886505635e-15
ridder 6 -2.8170799026838722e-12
toms748 5 -4.440892098500626e-16
secant 7 -4.440892098500626e-16
效率最低的是二分法,效率最高的是toms748
。