最优化方法Python计算:一元函数搜索算法——二次插值法

已知连续函数 f ( x ) f(x) f(x) x ∗ x^* x近旁存在最优解 x 0 x_0 x0。对博文《最优化方法Python计算:连续函数的单峰区间计算》讨论的 f ( x ) f(x) f(x)单峰区间的包围算法稍加修改,可算得 f ( x ) f(x) f(x)包含 x 0 x_0 x0的单峰区间 [ a , c ] [a,c] [a,c]及含于 ( a , c ) (a,c) (a,c)内的满足 f ( a ) > f ( b ) < f ( c ) f(a)>f(b)<f(c) f(a)>f(b)<f(c)的点 b b b,如下图所示。
在这里插入图片描述
相应地修改实现该算法的myBracket函数如下:

def myBracket(f,xstar,s=1e-4,lamd=2):
   a=xstar						#a初始化为xstar
   ya=f(a)
   b=a+s						#c初始化为a+s
   yb=f(b)
   if yb>ya:					#s为上升方向
      a,b=b,a					#交换a,b
      ya,yb=yb,ya
      s=-s						#调整s为下降方向
   c=b+s						#c置为b+s
   yc=f(c)
   while yc<=yb:				#c同a、b处于同一下降区间
      a,ya=b,yb					#a置为b
      b,yb=c,yc					#b置为c
      s*=lamd					#扩大步长s
      c=b+s						#重置c为b+s
      yb=f(b)
   if a>c:						#若a大c小
      a,c=c,a					#交换a,c
   return a,b,c

y a = f ( a ) y_a=f(a) ya=f(a) y b = f ( b ) y_b=f(b) yb=f(b) y c = f ( c ) y_c=f(c) yc=f(c)。构造通过点 ( a , y a ) (a,y_a) (a,ya) ( b , y b ) (b,y_b) (b,yb) ( c , y c ) (c,y_c) (c,yc)的二次函数 q ( x ) = p 0 + p 1 x + p 2 x 2 q(x)=p_0+p_1x+p_2x^2 q(x)=p0+p1x+p2x2,即
{ y a = p 0 + p 1 a + p 2 a 2 y b = p 0 + p 1 b + p 2 b 2 y c = p 0 + p 1 c + p 2 c 2 . \begin{cases} y_a=p_0+p_1a+p_2a^2\\ y_b=p_0+p_1b+p_2b^2\\ y_c=p_0+p_1c+p_2c^2 \end{cases}. ya=p0+p1a+p2a2yb=p0+p1b+p2b2yc=p0+p1c+p2c2.
解出 p 0 p_0 p0 p 1 p_1 p1 p 2 p_2 p2:
{ p 0 = b c y a ( b − a ) ( c − a ) − a c y b ( b − a ) ( c − b ) + a b y c ( c − a ) ( c − b ) p 1 = − ( b + c ) y a ( b − a ) ( c − a ) + ( a + c ) y c ( b − a ) ( c − b ) − ( a + b ) y c ( c − a ) ( c − b ) p 2 = y a ( b − a ) ( c − a ) − y b ( b − a ) ( c − b ) + y c ( c − a ) ( c − b ) . \begin{cases} p_0=\frac{bcy_a}{(b-a)(c-a)}-\frac{acy_b}{(b-a)(c-b)}+\frac{aby_c}{(c-a)(c-b)}\\ p_1=-\frac{(b+c)y_a}{(b-a)(c-a)}+\frac{(a+c)y_c}{(b-a)(c-b)}-\frac{(a+b)y_c}{(c-a)(c-b)}\\ p_2=\frac{y_a}{(b-a)(c-a)}-\frac{y_b}{(b-a)(c-b)}+\frac{y_c}{(c-a)(c-b)}\end{cases}. p0=(ba)(ca)bcya(ba)(cb)acyb+(ca)(cb)abycp1=(ba)(ca)(b+c)ya+(ba)(cb)(a+c)yc(ca)(cb)(a+b)ycp2=(ba)(ca)ya(ba)(cb)yb+(ca)(cb)yc.
二次式
q ( x ) = p 0 + p 1 x + p 2 x 2 q(x)=p_0+p_1x+p_2x^2 q(x)=p0+p1x+p2x2
称为 f ( x ) f(x) f(x) a , b , c a,b,c a,b,c处的二次插值多项式。由于 q ( a ) = f ( a ) q(a)=f(a) q(a)=f(a) q ( b ) = f ( b ) q(b)=f(b) q(b)=f(b) q ( c ) = f ( c ) q(c)=f(c) q(c)=f(c),故 q ( a ) > q ( b ) < q ( c ) q(a)>q(b)<q(c) q(a)>q(b)<q(c)。即 ( a , b ) (a,b) (a,b) q ( x ) q(x) q(x)的一个单峰区间。而二次函数仅有一个最小值,故 q ( x ) q(x) q(x)的最优解 x 0 ′ ∈ ( a , c ) x_0^{'}\in(a,c) x0(a,c),且为其驻点。即
x 0 ′ = − 1 2 ⋅ p 1 p 2 = 1 2 [ ( b + c ) y a ( b − a ) ( c − a ) − ( a + c ) y c ( b − a ) ( c − b ) + ( a + b ) y c ( c − a ) ( c − b ) y a ( b − a ) ( c − a ) − y b ( b − a ) ( c − b ) + y c ( c − a ) ( c − b ) ] . x_0^{'}=-\frac{1}{2}\cdot\frac{p_1}{p_2}=\frac{1}{2}\left[\frac{\frac{(b+c)y_a}{(b-a)(c-a)}-\frac{(a+c)y_c}{(b-a)(c-b)}+\frac{(a+b)y_c}{(c-a)(c-b)}}{\frac{y_a}{(b-a)(c-a)}-\frac{y_b}{(b-a)(c-b)}+\frac{y_c}{(c-a)(c-b)}}\right]. x0=21p2p1=21 (ba)(ca)ya(ba)(cb)yb+(ca)(cb)yc(ba)(ca)(b+c)ya(ba)(cb)(a+c)yc+(ca)(cb)(a+b)yc .
直观地看,从 f ( x ) f(x) f(x)的极小值点 x 0 x_0 x0的近旁 x ∗ x^* x出发,运用包围算法计算 f ( x ) f(x) f(x)的单峰区间 ( a , c ) (a,c) (a,c),及 b ∈ ( a , c ) b\in(a,c) b(a,c),满足 f ( x ) > f ( b ) < f ( c ) f(x)>f(b)<f(c) f(x)>f(b)<f(c),过三点 ( a , f ( a ) ) (a,f(a)) (a,f(a)) ( b , f ( b ) ) (b,f(b)) (b,f(b)) ( c , f ( c ) ) (c,f(c)) (c,f(c))的插值二次函数 q ( x ) q(x) q(x)的极小值点 x 0 ′ ∈ ( a , c ) x_0^{'}\in(a,c) x0(a,c)与出发点 x ∗ x^* x相比,离 x 0 x_0 x0更近,如下图所示。
在这里插入图片描述
于是,对给定的容错误差 ε \varepsilon ε,从邻近 f ( x ) f(x) f(x)的极小值点 x 0 x_0 x0的点 x ∗ x^* x出发,用包围算法算得含点 x 0 x_0 x0 f ( x ) f(x) f(x)的单峰区间 ( a , c ) (a,c) (a,c) b ∈ ( a , c ) b\in(a,c) b(a,c),若其长度 ∣ c − a ∣ < ε |c-a|<\varepsilon ca<ε,则即以二次插函数 q ( x ) q(x) q(x)的极小值点 x 0 ′ x_0^{'} x0作为 x 0 x_0 x0的近似值。否则,以 x 0 ′ x_0^{'} x0作为包围算法新的出发点 x ∗ x^* x计算 f ( x ) f(x) f(x)的单峰区间 ( a , c ) (a,c) (a,c)及含于其内的点 b b b,重复上述计算二次插值函数 q ( x ) q(x) q(x)的最小值点 x 0 ′ x_0^{'} x0。循环往复,直至 ∣ c − a ∣ < ε |c-a|<\varepsilon ca<ε。此时, x 0 ′ x_0^{'} x0即为 x 0 x_0 x0的近似值。这一方法称为二次插值法。实现为如下的Python函数

from scipy.optimize import OptimizeResult
def interpolation(fun,xstar,eps=0.0002,**options):
    k=1
    a,b,c=myBracket(fun,xstar)
    ya,yb,yc=fun(a),fun(b),fun(c)
    p1=-ya*(b+c)/((b-a)*(c-a))+yb*(a+c)/((b-a)*(c-b))-yc*(a+b)/((c-a)*(c-b))
    p2=ya/((b-a)*(c-a))-yb/((b-a)*(c-b))+yc/((c-a)*(c-b))
    x01=-0.5*p1/p2
    while c-a>eps:
        xstar=x01
        a,b,c=myBracket(fun,xstar)
        ya,yb,yc=fun(a),fun(b),fun(c)
        p1=-ya*(b+c)/((b-a)*(c-a))+yb*(a+c)/((b-a)*(c-b))-yc*(a+b)/((c-a)*(c-b))
        p2=ya/((b-a)*(c-a))-yb/((b-a)*(c-b))+yc/((c-a)*(c-b))
        x01=-0.5*p1/p2
        k+=1
    bestx=x01
    besty=fun(bestx)
    return OptimizeResult(fun=besty, x=bestx, nit=k)

程序的第2~19行定义函数interpolation,实现二次插值算法。参数fun、xstar和eps分别表示目标函数 f ( x ) f(x) f(x),起点 x ∗ x^* x和容错误差 ε \varepsilon ε。options用来实现minimize_scalar向interpolation传递xstar和eps等实际参数的机制。
第3行将迭代次数k初始化为1。
第4~8行执行第一次迭代:第4行调用myBracket函数,从 x ∗ x^* x起计算 f ( x ) f(x) f(x)的单峰区间 ( a , c ) (a,c) (a,c) b ∈ ( a , c ) b\in(a,c) b(a,c),满足 f ( a ) > f ( b ) < f ( c ) f(a)>f(b)<f(c) f(a)>f(b)<f(c)。第5行计算 f ( x ) f(x) f(x) a , b , c a,b,c a,b,c处的函数值为ya,yb,yc。第6、7行分别计算二次插值函数 q ( x ) q(x) q(x)的一次项系数和二次项系数p1,p2。第8行计算 q ( x ) q(x) q(x)的驻点 x 0 ′ = − 1 2 p 1 p 2 x_0^{'}=-\frac{1}{2}\frac{p_1}{p_2} x0=21p2p1赋予x01。
第9~16行的while循环执行余下的迭代操作:第10行将x01赋予xstar,以更新起点 x ∗ x^* x。第11~15行执行与第4~8行相同的操作。第16行将迭代次数k自增1。循环往复,直至区间 ( a , c ) (a,c) (a,c)的长度 c − a c-a ca不超过 ε \varepsilon ε
需要注意的是,之所以要对表示容错误差 ε \varepsilon ε的参数eps设置缺省值0.0002,是因为myBracket中我们将步长s的缺省值设置为0.0001,使得所计算的单峰区间 ( a , b ) (a,b) (a,b)的长度最小为0.0002。这样,才不至于使得此处的{\bf{while}}语句陷入死循环。读者在使用interpolation时需注意eps参数值不要小于myBracket的s参数的初始值。
例1 用上述程序定义的interpolation函数,计算函数 f ( x ) = x 2 + 4 cos ⁡ x f(x)=x^2+4\cos{x} f(x)=x2+4cosx x 1 = 1.5 x_1=1.5 x1=1.5近旁的极小值点。
:下列代码完成计算。

from scipy.optimize import minimize_scalar
f=lambda x:x**2+4*np.cos(x)
xstar=1.5
res=minimize_scalar(f, method=interpolation, options={'xstar':1.5,'eps':0.001})
print(res)

程序的第2行设置目标函数 f ( x ) = x 2 + 4 cos ⁡ x f(x)=x^2+4\cos{x} f(x)=x2+4cosx赋予f,第3行设置起始点 x ∗ x^{*} x赋予xstar,第4行调用minimize_scalar函数(第1行导入),传递f给参数fun、interpolation给method并通过options向interpolation传递参数1.5给xstar,0.001给eps。运行程序,输出

 fun: 2.316808419788213
 nit: 3
   x: 1.895494265404134

与博文《一元函数搜索算法——牛顿法》中例1的计算结果相比较,对同一函数 f ( x ) f(x) f(x),相同起点 x ∗ = 1.5 x^*=1.5 x=1.5,牛顿方法在容错误差 ε = 1.4 ⋅ 1 0 − 8 \varepsilon=1.4\cdot10^{-8} ε=1.4108下迭代7次的结果( x 0 x_0 x0的近似值为1.8954942711282465),用二次插值法在容错误差 ε = 0.001 \varepsilon=0.001 ε=0.001下仅迭代3次就可算得与前者直至千万分位( x 0 x_0 x0的近似值为1.895494265404134)均一致的结果。
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值