1、算法简介


模拟退火算法来源于固体退火原理,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。需要注意的是一定要徐徐冷却,也就是物体和外界温度相差越低,退火时间越长退火的效果就越好。这也就是这算法名字的由来。

2、基本步骤

(1)生成初始解T0

(2)生成新解Tn+1

(3)使用评价函数评价新解Tn+1,

(4)如果新解Tn+1更优秀则接受新解。

(5)否则以一定概率p接受Tn+1

(6)重复2-5步骤,直到解足够优秀。

3、关键点

算法中有4个部分是对算法质量有显著影响的。初始解,产生新解的方法,接受差解的概率p,以及评价函数。

通常要求初始解比较差,也就是被加热的温度很高。如果初始解就比较好,很容易坠入局部最优解。

产生的新解与原来的解差距比较要,也就是要徐徐的降温,降温过快就会成淬火了。

接受差解的概率p,概率不能过高也不能过低,过低会导致很容易坠入局部最优解,过高会导致收敛速度大幅下降。

通常取p=exp(-Δt′/T)。

评价函数,显而易见的评价函数能否有效表示解的优秀程度直接关系解的质量。

4、举个例子

下面以求非线性方程组的解为例,展示如何使用模拟退火算法(虽然通常解非线性方程通常不用模拟退火算法,但是用这个举例比较适合,毕竟简单)

(1)方程



funs.Add((double i,double j) =>
                {
                    return (Math.Sin(i) * j);
                });
            funs.Add((double i, double j) =>
            {
                return (Math.Exp(i)-j);
            });



  这里我用一个List保存方程组的各个函数。这里有两个函数,分别是

f1(x,y)=ysinx=0;

f2(x,y)=e^x-y=0;

(2)产生初始解,这里为-50 ~ 50中的随机数。



double a, b;
Random rd = new Random();
a = rd.NextDouble()*100-50;
b = rd.NextDouble()*100-50;



(3)迭代过程。



while(true)
{
    t++;
    doublet1=funs[0](a,b);
    doublet2=funs[1](a,b);
    //接受新解
    if(Ack(t1,t2)<ak)
    {
        ba=a;
        bb=b;
        ak=Ack(t1,t2);
    }
    //以一定概率接受差解
    else
    {
        if(p(t,Ack(t1,t2))<rd.NextDouble())
        {
            ba=a;
            bb=b;
            ak=Ack(t1,t2);
        }

    }
    //迭代结束,输出
    if(Ack(t1,t2)<=0.0001)
    {
        Console.WriteLine("t:{0}a:{1}b:{2}ak:{3}",t,ba,bb,ak);
        return;
    }
    //产生新解
    a=ba+(rd.NextDouble()-0.5)*step;
    b=bb+(rd.NextDouble()-0.5)*step;
}



其中评价函数直接去了两个函数计算结果的模长。

5、总结

模拟退火算法是一种通过随机搜索寻求最优解的方法,所以其不一定能够收敛的最优解,是存在一定概率收敛在局部最优解中的。并且解的质量与计算时间有关,不过只要参数设置得当,会取得高质量解,并且速度十分迅速。