最优化方法Python计算:解一元方程

我们知道,若 f ( x ) f(x) f(x) R \text{ℝ} R上连续,则 f ( x ) f(x) f(x)有原函数 F ( x ) , x ∈ R F(x),x\in\text{ℝ} F(x),xR。因此,解方程 f ( x ) = 0 f(x)=0 f(x)=0,等价于计算 F ( x ) F(x) F(x)的局部最小(大)值。譬如,设 f ( x ) f(x) f(x) [ a 0 , b 0 ] [a_0,b_0] [a0,b0]上连续且 f ( a 0 ) ⋅ f ( b 0 ) < 0 f(a_0)\cdot f(b_0)<0 f(a0)f(b0)<0,我们对解决局部优化问题的二分算法稍加修改即可用于在 ( a 0 , b 0 ) (a_0,b_0) (a0,b0)内解方程 f ( x ) = 0 f(x)=0 f(x)=0

def bisect_solv(fun,bracket,eps=1e-10):
    a0,b0=bracket							#初始区间端点
    a1=(a0+b0)/2							#区间中点
    f=fun(a1)								#当前函数值
    k=1										#迭代次数
    while (b0-a0>=eps) and (abs(f)>=eps):	#循环迭代
       if f*fun(a0)>0:						#当前点函数值与左端点同号
            a0=a1							#修改左端点
        else:								#否则
            b0=a1							#修改右端点
        a1=(a0+b0)/2						#当前中点
        f=fun(a1)							#当前点函数值
        k+=1								#迭代次数
   return a1,k

程序的第1~14行定义的函数bisect_solv实现解一元方程 f ( x ) = 0 f(x)=0 f(x)=0的二分算法。函数含有三个参数:fun表示连续函数 f ( x ) f(x) f(x)。bracket表示初始区间 [ a 0 , b 0 ] [a_0,b_0] [a0,b0],这是一个二元组。要求 f ( a 0 ) ⋅ f ( b 0 ) < 0 f(a_0)\cdot f(b_0)<0 f(a0)f(b0)<0。eps表示容错误差 ε \varepsilon ε,缺省值为 1 0 − 10 10^{-10} 1010。2~ 5行作初始化操作,第6~13行的while循环进行迭代,取区间中点 a 1 = a 0 + b 0 2 a_1=\frac{a_0+b_0}{2} a1=2a0+b0,计算 f 1 = f ( a 1 ) f_1=f(a_1) f1=f(a1),检测 f 1 f_1 f1是否与 f ( a 0 ) f(a_0) f(a0)同号,若是则修改 a 0 a_0 a0 a 1 a_1 a1,否则修改 b 0 b_0 b0 a 1 a_1 a1,以保持 f ( a 0 ) f(a_0) f(a0) f ( b 0 ) f(b_0) f(b0)异号。循环往复,直至 ∣ f ( a 1 ) ∣ < ε |f(a_1)|<\varepsilon f(a1)<ε ∣ b 0 − a 0 ∣ < ε |b_0-a_0|<\varepsilon b0a0<ε为止。第14行返回方程解的近似值a1及迭代次数k。
例1 ln ⁡ 2 \ln{2} ln2可视为方程 e x − 2 = 0 e^x-2=0 ex2=0的解。给定初始区间 [ a 0 , b 0 ] = [ 0 , 1 ] [a_0,b_0]=[0,1] [a0,b0]=[0,1],容错误差 ε = 1 0 − 10 \varepsilon=10^{-10} ε=1010,用二分法解方程 e x − 2 = 0 e^x-2=0 ex2=0,以算得 ln ⁡ 2 \ln{2} ln2的近似值。
:下列代码完成计算

f=lambda x: np.exp(x)-2			#设置函数f(x)
solution=bisect_solv(f,(0,1))	#求解f(x)=0在区间(0,1)内的根
print(solution)

程序的第1行设置函数 f ( x ) = e x − 2 f(x)=e^x-2 f(x)=ex2,第2行调用bisect_solv计算 f ( x ) = 0 f(x)=0 f(x)=0在区间 ( 0 , 1 ) (0,1) (0,1)内的根。运行程序,输出:

(0.6931471806019545, 29)

这意味着用二分法在 ε = 1 0 − 10 \varepsilon=10^{-10} ε=1010的容错误差下,迭代29次,算得 e x − 2 = 0 e^x-2=0 ex2=0 ( 0 , 1 ) (0,1) (0,1)内的根为0.6931,即 ln ⁡ 2 \ln2 ln2的近似值为0.6931。
scipy的optimize模块提供了函数
root_ scalar(f, method, ...) \text{root\_{}scalar(f, method, ...)} root_scalar(f, method, ...)
用于一元方程的求根。参数f表示方程 f ( x ) = 0 f(x)=0 f(x)=0中的函数 f ( x ) f(x) f(x)。method表示所用求根方法。返回值是包含收敛信息、函数调用次数、迭代次数及近似根等信息的元组。scipy.minimize为root_{}scalar的method参数提供了如下的可选方法

方法f的定义域初始区间1阶导数2阶导数收敛收敛率 p p p
bisectR必需不限不限保证1(线性)
brentqR必需不限不限保证 1 ≤ p ≤ 1.62 1\leq p\leq1.62 1p1.62
brenthR必需不限不限保证 1 ≤ p ≤ 1.62 1\leq p\leq1.62 1p1.62
ridderR必需不限不限保证 1.41 ≤ p ≤ 2.0 1.41\leq p\leq2.0 1.41p2.0
toms748R必需不限不限保证 1.65 ≤ p ≤ 2.7 1.65\leq p\leq2.7 1.65p2.7
secantR/C需近似解x0,x1无需无需不保证1.62
newtonR/C需近似解x0必需无需不保证 1.41 ≤ p ≤ 2.0 1.41\leq p\leq2.0 1.41p2.0
halleyR/C需近似解x0必需必需不保证 1.44 ≤ p ≤ 3.0 1.44\leq p\leq3.0 1.44p3.0

method参数的缺省值是系统按所提供的参数使用适用于所呈现情况的最佳方法。如果提供了包含根的区间,它可能会使用区间方法之一(bisect、brentq、brenth、rider、toms748)。如果指定了导数和初始值,它可以选择基于导数的方法之一(secant、newton、halley)。如果没有方法被判定为适用,它将引发异常。
例2:计算函数 f ( x ) = 1 + ( 2 − x ) 2 1 + x 2 f(x)=\frac{1+(2-x)^2}{1+x^2} f(x)=1+x21+(2x)2的最大值点 x 0 x_0 x0
:由于 f ( x ) f(x) f(x)二阶连续可微,为计算 f ( x ) f(x) f(x)的最大值点,先求得 f ( x ) f(x) f(x)驻点。令
f ′ ( x ) = 4 ( ( x − 1 ) 2 − 2 ) ( 1 + x 2 ) 2 = 0 f'(x)=\frac{4((x-1)^2-2)}{(1+x^2)^2}=0 f(x)=(1+x2)24((x1)22)=0
解此方程,得驻点。按二阶可微函数极值点的必要条件,计算 f ( x ) f(x) f(x)的二阶导数 f ′ ′ ( x ) f''(x) f′′(x)在驻点处的值,通过二阶导数值的正负以判断是极大值点或是极小值点。下列代码完成计算过程。

from scipy.optimize import root_scalar				  	#导入root_scalar
answer={True:'极小点',False:'极大点'}				  		#设置驻点标签字典
f=lambda x: (1+(2-x)**2)/(1+x**2)				  		#设置目标函数
f1=lambda x:4*((x-1)**2-2)/(1+x**2)**2				  	#设置导函数
x0=root_scalar(f1,bracket=(-2,0)).root				  	#计算区间(-2,0)内驻点
_,f2=der_scalar(f,x0)									#计算驻点处二阶导数
print('x0=%.4f为%s,f(x0)=%.4f'%(x0,answer[f2>0],f(x0)))	#驻点标签
x0=root_scalar(f1,bracket=(2,3)).root				  	#计算区间(2,3)内驻点
_,f2=der_scalar(f,x0)									#计算驻点处二阶导数
print('x0=%.4f为%s,f(x0)=%.4f'%(x0,answer[f2>0],f(x0)))	#驻点标签

程序的第2行设置驻点的极值属性标签字典answer:True对应“极小点”,False对应“极大点。第3、4行分别设置目标函数表达式f及其1阶导函数f1。第5行调用root_scalar(第1行导入),传递区间(-2,0)给参数bracket,method参数使用缺省值自动求解方程 f ′ ( x ) = 0 f'(x)=0 f(x)=0赋予x0。第6行调用der_scalar函数(详见博文《最优化方法Python计算:一元函数导数的数值计算》),计算 f ( x ) f(x) f(x)在驻点x0处的二阶导数赋予f2,第7行以f2是否大于零而确定x0的极值点属性。相仿地,第8~10行计算 f ( x ) f(x) f(x)在区间 ( 2 , 3 ) (2,3) (2,3)内的驻点,并确定其极值点属性。运行程序,输出

x0=-0.4142为极大点,f(x0)=5.8284
x0=2.4142为极小点,f(x0)=0.1716

这意味着驻点-0.4142,极大值为5.8284。驻点2.4142是函数 f ( x ) f(x) f(x)的极小值点,极小值为0.1716。所以,最大值点为-0.4142。
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值