研究了一下模拟退火

在搜索时候经常想不到正确算法,或者一些提交答案的题目不需要最标准的答案,于是让非最优解方法有了用武之地。


很多有标准答案的题目用模拟退火等随机算法也能完美解决。


普通的贪心,爬山会陷入局部最优解,而模拟退火则不会



算法流程:


计算初始解 F(1 )

设置一个T (也就是温度~)的初始值,和T的最终值。

设置一个T的变化率TP比如0.99  0.98

T每一次会T*=TP


想出一个能快速变换状态,并且计算值的方法。

比如以下一个题目:

有n个格子,每个格子有一个初始值a[i]

给n个数字,分别是b[i]


分别数字填入格子,使得乘积的和 (sum(a[i]*b[i]) ) 最大。


正确算法很显然,这里不做讨论。


 我们可以先随便的给b[i]填入一些格子里,

然后随便交换其中2个格子,我们可以在O(1)的时间得到新的状态的答案。

但是如果重新重头计算就会比较慢了~  一般要选择高效的方法。 


de = - abs( f[i + 1] - f[i])

这个也就是2个状态产生的差值,同时取一个负号,为什么用负号一会儿讨论。


显然,如果新的状态f[i + 1] 比老状态f[i]更优的话,那么我们可以转移到新的状态。


但是很多情况下新状态比较差,但是从比较差的状态却可以得到更好的其他新状态~  


所以我们需要用一定概率接受新的答案。


在很多情况下,当然abs(de) 也就是2个状态产生的值相差很大, 而且新状态又不优,我们我们通常的想法就是用比较低的概率接受, 新状态和老状态比虽然不优但是差

的不多,那么我们可以用比较高的概率接受~


这个概率就是exp(de/T)   T是正数,de是负数。 de越小,exp(de/T)越大~概率越高。


当然随着我们的不断调整,我们认为接受新解的概率应该降低, 所以在不停的运行中,T的数值在不断降低,直到降低到T_min的时候停止。



整个过程的T就是退火中的温度,温度高的时候活跃,接受新解的概率高,温度低的时候接受新解的概率就比较低了~


比如用上述例子的代码如下 :

T = 100 初始温度
T_min = 中止温度 0.001
Tp = 温度的变化量 0.99
计算一个初始解 old_ans 
   while (T > T_min)
   {
         for (int i = 1; i <= 循环次数; ++ i)       //执行很多次操作
         {
             int x = 随机数;    
             int y = 随机数;
             if (x == y) -- i; continue;   
             b[x] b[y]交换位置,计算新的解   new_ans; 
             if (new_ans 比 old_ans优 )
             {
                old_ans = new_ans;
                continue;            //更新答案
             }
             
             de =  - abs(old_ans - new_ans) ;
             if (exp(de/T) > random(0, 1)) //以一定概率接受新的解
             {
                old_ans = new_ans;
                continue;              
             }
             
             swap(b[x], b[y]) //重新恢复到初始状态
         }
         
         T *= Tp; //  降温
   }
	输出ans


其中的random()可能会用到  L,R可以是负数


inline double random(double L, double R)
{
        return (double)rand() / RAND_MAX * (R - L) + L;
}



模拟退火可能用到的其他代码段


const double pi = 3.1415926535897932384626433;
const double max_pi = pi * 2;
const double min_pi = 0;
inline double getpi()  //返回一个0 ~ 2pi之间的值
{
        return (double)rand() / RAND_MAX * (max_pi - min_pi) + min_pi;
}

struct dot //这是一个坐标
{
       double x,y;       
       dot():x(0),y(0){} //初始化(0,0)
       dot(double X, double Y):x(X),y(Y){}  //初始化(x,y)
       dot(double k, dot A)  //  初始化为距离点A单位长度为K的点 
       {
            double tmp = getpi();
            x = k*sin(tmp) + A.x;
            y = k*cos(tmp) + A.y;
       }
};


你可能感兴趣的:(模拟退火)