1 约束最优化问题
1.1 约束最优化问题的基本结构
在我们讨论完无约束最优化问题后,我们接着讨论约束最优化问题。在无约束最优化问题中,我们默认了可行域为 R n R^n Rn,然而在约束最优化问题中,我们需要为可行域做出一些限制,因此衍生出了一些与无约束最优化问题不同的、独有的性质。
一般约束最优化问题的表达为:
{ m i n f ( x ) g i ( x ) ≥ 0 , i ∈ I = { 1 , 2 , . . . , m 1 } h i ( x ) = 0 , j ∈ E = { m 1 + 1 , . . . , m } \begin{cases}minf(x)\\g_i(x)\geq0,i\in I=\{1,2,...,m_1\}\\h_i(x)=0,j\in E=\{m_1+1,...,m\}\end{cases} ⎩⎪⎨⎪⎧minf(x)gi(x)≥0,i∈I={1,2,...,m1}hi(x)=0,j∈E={m1+1,...,m}
其中 g ( x ) g(x) g(x)被称为不等式约束,而 h ( x ) h(x) h(x)被称为等式约束。这些约束限定了 f ( x ) f(x) f(x)的可行域为 D = { x ∣ g i ( x ) ≥ 0 , i ∈ I ; h j ( x ) = 0 , j ∈ E } D=\{x|g_i(x)\geq0,i\in I;h_j(x)=0,j\in E\} D={x∣gi(x)≥0,i∈I;hj(x)=0,j∈E}
解决约束最优化问题的方法主要有罚函数法和拉格朗日乘子法。其中罚函数法较为简单,基本思想是将约束最优化问题转化为无约束最优化问题再用为无约束最优化问题的方法求解。
1.2 约束最优化问题实例
约束最优化问题比起理想模型,更多的是在实际生活中尤其是经济学领域的应用模型。比如,在每种原料有限的情况下,如果每种材料需要满足一定配比,如何规划生产可以使得开销最小。因此,约束最优化方法也被称为运筹学。
下面提出一个约束最优化问题实例:
- 开销函数(最优化的目标函数): y = ( x 1 − 1 ) 2 + ( x 1 − x 2 ) 2 + ( x 2 − x 3 ) 2 y=(x_1-1)^2+(x_1-x_2)^2+(x_2-x_3)^2 y=(x1−1)2+(x1−x2)2+(x2−x3)2
- 配比限制(等式约束): x 1 ( 1 + x 2 2 ) + x 3 4 − 4 − 3 2 = 0 x_1(1+x_2^2)+x_3^4-4-3\sqrt{2}=0 x1(1+x22)+x34−4−32=0
- 原料限制(不等式约束): − 10 ≤ x i ≤ 10 , i = 1 , 2 , 3 -10\leq x_i\leq 10,i=1,2,3 −10≤xi≤10,i=1,2,3
在接下来提出的方法中,我们将测试方法对以上实例的求解效果。
2 外点罚函数法
2.1 外点罚函数法步骤
对可行域外的点(即违反约束的点)施加惩罚,内部的点不惩罚,从而使迭代点向可行域D逼近。
如构造函数 F ( x ) = { f ( x ) x ∈ D + ∞ x ∉ D F(x)=\begin{cases}f(x)\quad x\in D\\+∞\quad x\notin D\end{cases} F(x)={f(x)x∈D+∞x∈/D,但这只是理想的情况,而且无法用无约束问题的方法求解。
构造辅助函数 F μ ( x ) = f ( x ) + μ S ( x ) F_\mu(x)=f(x)+\mu S(x) Fμ(x)=f(x)+μS(x), μ \mu μ为罚参数或罚因子, S ( x ) S(x) S(x)的值的大小反映x偏离可行域D的程度。
对于约束 g i ( x ) ≥ 0 , i ∈ I g_i(x)\geq 0,i\in I gi(x)≥0,i∈I, h i ( x ) = h j ( x ) , j ∈ E h_i(x)=h_j(x),j\in E hi(x)=hj(x),j∈E,有 g i ( − ) ( x ) = m i n { g i ( x ) , 0 } , i ∈ I g_i^{(-)}(x)=min\{g_i(x),0\},i\in I gi(−)(x)=min{gi(x),0},i∈I, h j ( − ) = h j ( x ) , j ∈ E h_j^{(-)}=h_j(x),j\in E hj(−)=hj(x),j∈E,则 S ( x ) = ∑ i ∈ I [ g i ( − ) ( x ) ] 2 + ∑ j ∈ E [ h j ( − ) ( x ) ] 2 S(x)=\displaystyle\sum_{i\in I}[g_i^{(-)}(x)]^2+\sum_{j\in E}[h_j^{(-)}(x)]^2 S(x)=i∈I∑[gi(−)(x)]2+j∈E∑[hj(−)(x)]2
随着 μ \mu μ不断变大,可行域外的函数值越往可行域边界折叠,像是形成了一堵墙壁,阻止向外迭代。若最优点不在可行域内,最终迭代到的点也会靠近边界。
步骤:
- 选定初始点 x 0 ∈ R n x_0\in R^n x0∈Rn,初始罚因子 μ 0 > 0 \mu_0>0 μ0>0,放大系数 σ > 1 \sigma>1 σ>1,精度 ϵ > 0 \epsilon>0 ϵ>0,置 k = 0 k=0 k=0
- 构造增广目标函数 F μ k ( x ) = f ( x ) + 1 2 μ k S ( x ) F_{\mu_k}(x)=f(x)+\frac{1}{2}\mu_kS(x) Fμk(x)=f(x)+21μkS(x)
- 以 x k x_{k} xk为初始点求解无约束最优化问题 m i n F μ k ( x ) minF_{\mu_k}(x) minFμk(x),得解 x k x_k xk
- 若 μ k S ( x ) < ϵ \mu_kS(x)<\epsilon μkS(x)<ϵ,则得解 x k x_k xk,停止迭代
- 令 μ k + 1 = σ μ k , k = k + 1 \mu_{k+1}=\sigma\mu_k,k=k+1 μk+1=σμk,k=k+1,转步1
2.2 实战测试
对于本节2.1中提出的约束最优化问题, x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3的初值均在 [ 0 , 4 ] [0,4] [0,4]的范围内随机生成,总共生成100组起点。统计迭代成功(在1000步内得到最优解且单次步长搜索迭代次数不超过1000次)的样本的平均迭代步数、平均迭代时间和得到的最优解及开销函数最小值。
迭代步数 | 迭代时间 | 最优解 | 函数最小值 |
---|---|---|---|
1 | 1.95s | x 1 = 1.1048 x 2 = 1.1965 x 3 = 1.5349 x_1=1.1048~x_2=1.1965~x_3=1.5349 x1=1.1048 x2=1.1965 x3=1.5349 | 0.03255 0.03255 0.03255 |
代码实现
使用共轭梯度PRP+法的外点罚函数法
本博客所有代码在https://github.com/HarmoniaLeo/optimization-in-a-nutshell开源,如果帮助到你,请点个star,谢谢这对我真的很重要!
你可以在上面的GitHub链接或本文集的第一篇文章深入浅出最优化(1) 最优化问题概念与基本知识中找到Function.py和lagb.py
import numpy as np
from Function import Function #定义法求导工具
from lagb import * #线性代数工具库
from scipy import linalg
n=3 #x的长度
mu=2 #μ的初值
def func(x): #目标函数,x是一个包含所有参数的列表
return (x[0]-1)**2+(x[0]-x[1])**2+(x[1]-x[2])**2
def hj(x): #构造数组h,第j位是第j+1个等式限制条件计算的值,x是一个包含所有参数的列表
return np.array([x[0]*(1+x[1]**2)+x[2]**4-4-3*np.sqrt(2)])
def gi(x): #构造数组g,第i位是第i+1个不等式限制条件计算的值,x是一个包含所有参数的列表
return np.array([x[0]+10,-x[0]+10])
def S(x):
h=hj(x)
g=gi(x)
return np.sum(np.power(h,2))+np.sum(np.power(np.where(g<0,g,0),2))
def myFunc(x):
return func(x)+S(x)*mu*0.5
sigma2=1.5 #放大因子
e2=0.001
x=np.array([2.0,2.0,2.0]) #初值点
k1=0
while mu*S(x)>=e2:
e=0.001
beta1=1
sigma=0.4
rho=0.55
tar=Function(myFunc)
k=0
d=-tar.grad(x)
while tar.norm(x)>e:
a=1
if not (tar.value(x+a*d)<=tar.value(x)+rho*a*dot(turn(tar.grad(x)),d) and \
np.abs(dot(turn(tar.grad(x+a*d)),d))>=sigma*dot(turn(tar.grad(x)),d)):
a=beta1
while tar.value(x+a*d)>tar.value(x)+rho*a*dot(turn(tar.grad(x)),d):
a*=rho
while np.abs(dot(turn(tar.grad(x+a*d)),d))<sigma*dot(turn(tar.grad(x)),d):
a1=a/rho
da=a1-a
while tar.value(x+(a+da)*d)>tar.value(x)+rho*(a+da)*dot(turn(tar.grad(x)),d):
da*=rho
a+=da
lx=x
x=x+a*d
beta=np.max((dot(turn(tar.grad(x)),tar.grad(x)-tar.grad(lx))/(tar.norm(lx)**2),0)) #PRP+
d=-tar.grad(x)+beta*d
k+=1
print(k1,k)
mu*=sigma2
k1+=1
print(x)