Python调用二分法和牛顿法求方程的根
文章目录
- 二分法
- 牛顿法
- 函数定义
二分法
二分法是最简单的求根算法。
对于方程 f ( x ) = 0 f(x)=0 f(x)=0,如果 f ( a ) ⋅ f ( b ) < 0 f(a)\cdot f(b)<0 f(a)⋅f(b)<0,则说明 a , b a,b a,b之间必有根,如果按照某个步长对其进行搜索,那么最终一定能够不断缩小根的取值范围,不断精确化。
二分法就是基于这种思想的一种算法,其实质是对区间进行快速选取。如果已知 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)之间,然后不断迭代下去。
scipy
提供了二分求根算法bisect
,测试如下
from scipy.optimize import bisect
def func(x):
return x*x - 2
bisect(func, 0, 2)
# 1.4142135623715149
其中,func
为待求函数,0,2
为下限和上限。
牛顿法
如果 f ( x ) f(x) f(x)=0可以写成 x = φ ( x ) x=\varphi(x) x=φ(x)的形式,则可以将这个方程的根理解为 y = φ ( x ) y=\varphi(x) y=φ(x)与 y = x y=x y=x的交点。现给定一个初值 x 0 x_0 x0,将其代入 φ ( x ) \varphi(x) φ(x)中,则 φ ( x 0 ) \varphi(x_0) φ(x0)对应一个新的 x x x,记为 x 1 x_1 x1。
如果数列 { x n } \{x_n\} {xn}收敛,那么必有 x = lim k → ∞ x k x=\lim_{k\to\infty}x_k x=limk→∞xk即为方程的根。所以,问题的关键在构造合适的 φ ( x ) \varphi(x) φ(x)使得数列能够快速收敛。
其最简单的形式莫过于 x k + 1 = x k + f ( x k ) x_{k+1}=x_k+f(x_k) xk+1=xk+f(xk),不过这种迭代公式看起来十分危险,如果$f(x_k)是一个大于0的单调递增函数,那么结果就爆炸了。
所以,最好为 f ( x k ) f(x_k) f(xk)乘以一个系数,当 f ( x k ) f(x_k) f(xk)增加时使之变小,当其递减时使之变大。所以一个比较好的形式为
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)
此即Newton公式。
除了迭代形式,Newton公式也可以作为二分法的一个升级版本, x k − f ( x k ) f ′ ( x k ) x_k-\frac{f(x_k)}{f'(x_k)} xk−f′(xk)f(xk)可以表示过 ( x k , f ( x k ) ) (x_k,f(x_k)) (xk,f(xk))做切线与 x x x轴的交点,通过切线选取新的 x x x值取代了二分法的盲动性,示例如下
from scipy.optimize import newton
newton(func, 0)
# 1.4142135623715149
函数定义
二分法和牛顿法的参数定义如下
bisect(f, a, b, args=(), xtol=2e-12, rtol=8.881784197001252e-16, maxiter=100, full_output=False, disp=True)
newton(func, x0, fprime=None, args=(), tol=1.48e-08, maxiter=50, fprime2=None, x1=None, rtol=0.0, full_output=False, disp=True)
其中,f, a, b
以及func, x0
已经在演示的时候调用了,分别是求根函数和初值;args
为待求根函数中可能包含的其他参数;maxiter
为最大迭代步长。