最优化算法

最优化算法

1. 一元函数最优化算法

1.1 三分法

普通的三分法是比较常用的求一元函数最值的方法

以求函数 f ( x ) f(x) f(x) 的最小值为例,假设函数 f ( x ) f(x) f(x) 在区间 [ a , b ] [a,b] [a,b] 上先严格单调递减,后严格单调递增,那么可以通过迭代缩小最值点可能在的区间 [ l , r ] [l,r] [l,r]

  1. l = a , r = b l=a,r=b l=a,r=b
  2. m l = 2 l + r 3 , m r = l + 2 r 3 m_l=\frac{2l+r}{3},m_r=\frac{l+2r}{3} ml=32l+r,mr=3l+2r ,即取 [ l , r ] [l,r] [l,r] 的三等分点
  3. 如果 f ( m l ) > f ( m r ) f(m_l)>f(m_r) f(ml)>f(mr) ,那么 l = m l l=m_l l=ml ,否则 r = m r r=m_r r=mr
  4. 如果达到最大迭代次数,那么停止,否则转2

每经过一次迭代,区间 [ l , r ] [l,r] [l,r] 的长度变为原来的 2 3 \frac{2}{3} 32 ,因此其精度是一阶收敛的

/*三分法

    f:目标函数
    l:区间下界
    r:区间上界
    max_step:最大迭代次数

*///2019.2.23
template <class fun>
double oneThirdIterate(fun f,double l,double r,int max_step){
    double ml,mr;
    rep(it,1,max_step){
        ml=(2*l+r)/3,mr=(l+2*r)/3;
        if(f(ml)>f(mr))l=ml;
        else r=mr;
    }
    return l;
}

1.2 黄金分割三分法

黄金分割三分法是在三分法的基础上改进得到的算法,在三分法中,我们可以把 m l , m r m_l,m_r ml,mr 的位置选得更接近 l + r 2 \frac{l+r}{2} 2l+r ,这样收敛速度会更快,在此基础上,还可以减少目标函数的调用来提高效率

m l = g l + ( 1 − g ) r , m r = ( 1 − g ) l + g r m_l=gl+(1-g)r,m_r=(1-g)l+gr ml=gl+(1g)r,mr=(1g)l+gr ,其中 g = 5 − 1 2 g=\frac{\sqrt 5-1}{2} g=25 1

在这里插入图片描述

如图所示,这样选取可以沿用前一次迭代的 f ( m l ) f(m_l) f(ml) 值作为新的 f ( m r ) f(m_r) f(mr) 值(或沿用前一次迭代的 f ( m r ) f(m_r) f(mr) 值作为新的 f ( m l ) f(m_l) f(ml) 值),避免了目标函数的重复调用

这样可以提高算法效率,但会带来较大的舍入误差

/*黄金分割迭代

    f:目标函数
    l:区间下界
    r:区间上界
    max_step:最大迭代次数

*///2019.2.20
template <class fun>
double GoldenIterate(fun f,double l,double r,int max_step){
    double g=(sqrt(5.0)-1)/2,ml=g*l+(1-g)*r,mr=(1-g)*l+g*r,lval=f(ml),rval=f(mr);
    rep(it,1,max_step){
        if(lval<rval)r=mr,mr=ml,ml=g*l+(1-g)*r,rval=lval,lval=f(ml);
        else l=ml,ml=mr,mr=(1-g)*l+g*r,lval=rval,rval=f(mr);
    }
    return l;
}

2. 多元函数最优化算法

2.1 模拟退火(SAA)

模拟退火算法是来自冶金的算法,当温度过高时,粒子会剧烈运动,随着时间延长,温度逐渐降低,粒子运动速度下降。在算法中,随机确定下一步的位置,如果下一步更优,那就移动到该位置,否则根据当前温度计算粒子运动的概率,以此概率随机确定是否移动到该位置

  1. 随机确定起点 x = x 0 x=x_0 x=x0 ,确定初始温度 T = T 0 T=T_0 T=T0
  2. 温度 T = T η ( 0 &lt; η &lt; 1 ) T=T\eta(0&lt;\eta&lt;1) T=Tη(0<η<1)
  3. 随机选取 x x x 附近的点 x ′ x&#x27; x d ( x , x ′ ) d(x,x&#x27;) d(x,x) 需要收敛到0)
  4. f ( x ′ ) &lt; f ( x ) f(x&#x27;)&lt;f(x) f(x)<f(x) ,那么 x = x ′ x=x&#x27; x=x ,否则若 P ( f ( x ) , f ( x ′ ) , T ) &gt; r a n d o m ( 0 , 1 ) P(f(x),f(x&#x27;),T)&gt;random(0,1) P(f(x),f(x),T)>random(0,1) ,那么 x = x ′ x=x&#x27; x=x
  5. 如果达到最大迭代次数,那么停止,否则转2
/*SAA模拟退火算法

    f:目标函数
    x:初始点
    d:初始步长
    mi:变量下界
    ma:变量上界
    max_step:最大迭代次数
    T:初始温度

*///2019.2.15
double uniformRand(double l,double r){return rand()/32767.0*(r-l)+l;}
ll uniformRandInt(int l,int r){return rand()%(r-l+1)+l;}
template <class fun>
tensor SAA(fun f,tensor x,tensor d,tensor mi,tensor ma,int max_step,double T){
    int n=x.size();
    double yita=0.97,fval=f(x),next_fval;
    tensor next;
    rep(it,1,max_step){
        next=x;
        rep(i,0,n-1)next[i]=(rand()&1)?min(next[i]+d[i],ma[i]):max(next[i]-d[i],mi[i]);
        next_fval=f(next);
        if(next_fval<fval || uniformRand(0,1)<exp((fval-next_fval)/T))x=next,fval=next_fval;
        rep(i,0,n-1)d[i]*=yita;
        T*=yita;
    }
    return x;
}

2.2 粒子群算法(PSO)

粒子群算法是由鸟群的运动启发得到的算法,想象一群鸟在寻找温暖的地方,每只鸟有自己的位置 P i P_i Pi ,有自己的速度 v i v_i vi ,同时记录了自己飞过的最温暖的位置 p b e s t i pbest_i pbesti ,鸟群间有信息交流,共享所有鸟飞过的位置中最温暖的位置 g b e s t gbest gbest 。每只鸟在飞行时,有沿自身速度方向飞的趋势,有向 p b e s t i pbest_i pbesti 飞的趋势,有向 g b e s t i gbest_i gbesti 飞的趋势,因此,其位置由以上三个量确定

  1. 随机确定 m m m 个粒子的位置 P i P_i Pi
  2. 更新每个粒子的 p b e s t i pbest_i pbesti ,更新全局最优解 g b e s t gbest gbest
  3. 对于每个粒子,其下一步位置 P i ′ = P i + w v i + c 1 r 1 ( p b e s t i − P i ) + c 2 r 2 ( g b e s t − P i ) P_i&#x27;=P_i+wv_i+c_1r_1(pbest_i-P_i)+c_2r_2(gbest-P_i) Pi=Pi+wvi+c1r1(pbestiPi)+c2r2(gbestPi) ,其中 w w w 是惯性系数, c 1 c_1 c1 是认知加速常数, c 2 c_2 c2 是社会加速常数, r 1 r_1 r1 r 2 r_2 r2 [ 0 , 1 ] [0,1] [0,1] 范围内的随机数
  4. 如果达到最大迭代次数,那么停止,否则转2
/*PSO粒子群算法

    f:目标函数
    vmax:最大飞行速度(参考值:变量变化范围的10%)
    mi:变量下界
    ma:变量上界
    max_step:最大迭代次数
    m:粒子群规模(参考值:50)
    w:惯性系数(参考值:0.6)
    c1:"认知"加速常数(参考值:1.7)
    c2:"社会"加速常数(参考值:1.7)

*///2019.2.17
template <class fun>
tensor PSO(fun f,tensor vmax,tensor mi,tensor ma,int max_step,int m,double w,double c1,double c2){
    int n=vmax.size();
    tensor p[m],g,x[m],v[m];
    double pbest[m],gbest,now;
    rep(i,0,m-1){
        x[i].resize(n),v[i].resize(n),p[i].resize(n);
        rep(j,0,n-1){
            x[i][j]=uniformRand(mi[j],ma[j]);
            v[i][j]=uniformRand(-vmax[j],vmax[j]);
        }
        p[i]=x[i],pbest[i]=f(x[i]);
        if(i==0 || pbest[i]<gbest)gbest=pbest[i],g=x[i];
    }
    rep(it,1,max_step){
        rep(i,0,m-1){
            v[i]=w*v[i]+c1*uniformRand(0,1)*(p[i]-x[i])+c2*(g-x[i]);
            rep(j,0,n-1)v[i][j]=max(min(v[i][j],vmax[j]),-vmax[j]);
            x[i]+=v[i];
            now=f(x[i]);
            if(now<pbest[i])pbest[i]=now,p[i]=x[i];
            if(now<gbest)gbest=now,g=x[i];
        }
    }
    return g;
}

2.3 梯度下降法(GD)

梯度下降法是机器学习中常用的算法,但是需要依赖函数的导数。函数的梯度反方向是函数值下降最快的方向,因此可以以公式
x n + 1 = x n − γ  gradient ( f ( x ) ) x_{n+1}=x_n-\gamma\ \text{gradient}\left(f(x)\right) xn+1=xnγ gradient(f(x))
迭代若干次,逼近最优解

(函数的梯度即为函数对每个自变量的偏导数组成的向量)

/*GD梯度下降法

    f_:目标函数的梯度函数
    x:起始点
    gamma:学习率(参考值:0.4)
    max_step:最大迭代次数

*///2019.2.21
template <class fun>
tensor GD(fun f_,tensor x,double gamma,int max_step){
    rep(it,1,max_step)x-=gamma*f_(x);
    return x;
}

2.4 坐标下降法(CD)

坐标下降法是依赖一元函数最优化算法的多元函数最优化算法,设目标函数的维数为 n n n ,在每一次迭代中,固定自变量中的 n − 1 n-1 n1 维,调整剩下的一维达到局部最优,重复上述过程即可

注意,某些情况下,坐标下降法的收敛速度可能较慢

/*CD坐标下降法

    f:目标函数
    x:初始点
    mi:变量下界
    ma:变量上界
    max_step:最大迭代次数
    max_step2:一维搜索的最大迭代次数

*///2019.2.23
template <class fun>
tensor CD(fun f,tensor x,tensor mi,tensor ma,int max_step,int max_step2){
    ll d=mi.size(),xl,xr;
    static fun _f=f;
    class Fun{
    public:
        int idx;
        tensor x;
        double operator () (double y){
            x[idx]=y;
            return _f(x);
        }
    }F;
    F.x=x;
    rep(it,1,max_step){
        rep(i,0,d-1){
            F.idx=i;
            double l=GoldenIterate(F,mi[i],x[i],max_step2);
            double r=GoldenIterate(F,x[i],ma[i],max_step2);
            x[i]=F(l)<F(r)?l:r;
            F.x[i]=x[i];
        }
    }
    return x;
}

2.5 Nelder–Mead方法(NM)

Nelder–Mead方法又名下山单纯形法,维护高维空间中的多胞体包裹住最优解,通过迭代减小多胞体的测度,确定最优解

  1. 随机确定 x 1 , x 2 , … , x n + 1 x_1,x_2,\dots,x_{n+1} x1,x2,,xn+1
  2. 根据 f ( x i ) f(x_i) f(xi) 对其从小到大进行排序
  3. 计算 x 1 , x 2 , … , x n x_1,x_2,\dots,x_n x1,x2,,xn 的中心 x o x_o xo
  4. Reflection 计算Reflection点 x r = x o + α ( x o − x n + 1 ) , α &gt; 0 x_r=x_o+\alpha(x_o-x_{n+1}),\alpha&gt;0 xr=xo+α(xoxn+1),α>0 ,如果 f ( x 1 ) ≤ f ( x r ) &lt; f ( x n ) f(x_1)\le f(x_r)&lt;f(x_n) f(x1)f(xr)<f(xn) ,那么将 x n + 1 x_{n+1} xn+1 替换为 x r x_r xr 并转2
  5. Expansion 如果 f ( x r ) &lt; f ( x 1 ) f(x_r)&lt;f(x_1) f(xr)<f(x1) ,那么计算 x e = x o + γ ( x r − x o ) , γ &gt; 1 x_e=x_o+\gamma(x_r-x_o),\gamma&gt;1 xe=xo+γ(xrxo),γ>1 ,如果 f ( x e ) &lt; f ( x r ) f(x_e)&lt;f(x_r) f(xe)<f(xr) ,那么将 x n + 1 x_{n+1} xn+1 替换为 x e x_e xe 并转2 ,否则将 x n + 1 x_{n+1} xn+1 替换为 x r x_r xr 并转2
  6. Contraction 计算contracted点 x c = x o + ρ ( x n + 1 − x o ) , 0 &lt; ρ ≤ 0.5 x_c=x_o+\rho(x_{n+1}-x_o),0&lt;\rho \le0.5 xc=xo+ρ(xn+1xo),0<ρ0.5 ,如果 f ( x c ) &lt; f ( x n + 1 ) f(x_c)&lt;f(x_{n+1}) f(xc)<f(xn+1) ,那么将 x n + 1 x_{n+1} xn+1 替换为 x c x_c xc 并转2
  7. Shrink x i x_i xi 替换为 x 1 + γ ( x i − x 1 ) , i = 2 , 3 , … , n + 1 x_1+\gamma(x_i-x_1),i=2,3,\dots,n+1 x1+γ(xix1),i=2,3,,n+1
  8. 如果达到最大迭代次数,那么停止,否则转2
/*Nelder–Mead method

    f:目标函数
    mi:变量下界
    ma:变量上界
    max_step:最大迭代次数
    n:多胞体顶点数
    alpha(参考值:1)
    gamma(参考值:2)
    rho(参考值:0.5)
    sigma(参考值:0.5)

*///2019.2.22
template <class fun>
tensor NM(fun f,tensor mi,tensor ma,int max_step,int n,double alpha,double gamma,double rho,double sigma){
    tensor xr,xe,xc,x[n+2];
    /*init*/
    ll d=mi.size();
    rep(i,1,n+1){
        x[i].clear();
        rep(j,0,d-1)x[i].push_back(uniformRand(mi[j],ma[j]));
    }
    rep(it,1,max_step){
        /*sort*/
        if(it==1){
            rep(i,1,n+1)rep(j,1,n)if(f(x[j])>f(x[j+1]))swap(x[j],x[j+1]);
        }
        else{
            int pos=-1;
            double fx=f(x[n+1]);
            rep(i,1,n)if(fx<f(x[i])){
                pos=i;
                break;
            }
            if(pos!=-1){
                tensor temp=x[n+1];
                for(ll i=n+1;i>pos;i--)x[i]=x[i-1];
                x[pos]=temp;
            }
        }
        /*calculate centroid*/
        x[0]=tensor(d,0);
        rep(i,1,n)x[0]+=x[i]/n;
        /*Reflection*/
        xr=x[0]+alpha*(x[0]-x[n+1]);
        if(f(x[1])<=f(xr) && f(xr)<f(x[n])){
            x[n+1]=xr;
            continue;
        }
        /*Expansion*/
        if(f(xr)<f(x[1])){
            xe=x[0]+gamma*(xr-x[0]);
            x[n+1]=f(xe)<f(xr)?xe:xr;
            continue;
        }
        /*Contraction*/
        xc=x[0]+rho*(x[n+1]-x[0]);
        if(f(xc)<f(x[n+1])){
            x[n+1]=xc;
            continue;
        }
        /*Shrink*/
        rep(i,2,n+1)x[i]=x[1]+sigma*(x[i]-x[1]);
    }
    return x[1];
}

2.6 遗传算法(GA)

遗传算法是将自变量转化为遗传信息序列,模拟生物进化的算法

笔者能力有限,无法给出通用性的模板代码,请读者参考其他资料

https://en.wikipedia.org/wiki/Genetic_algorithm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值