退火算法大致意义上就是在贪心的基础上加入随机量,以一定的概率来接受一个比当前解要差的解,使其有机会跳出一个局部的最优解,达到全局的最优解。
之所以叫做退火算法就是在这个概率的计算上参考了金属冶炼的退火过程。P(dE) = exp( dE/(kT) )←公式。k是一个常数,exp表示自然指数,且dE<0,随着温度的降低,几率随之降低。
TSP挺常见到用这个的,典型的就是旅行商问题,不过作为一个过程简单,通用,鲁棒性强,适用于并行处理非线性优化问题的算法,退火算法在其他的地方在其它地方使用时,退火算法的时间问题经常很难处理。
因为退火算法的特性决定(1)如果降温过程越缓慢,得到的解就会越优化,与此相对的是减慢收敛速度;
在经典退火算法上的优化方式倒是不少,例如(1)增加记忆功能。(2)在进程的适当时机增加升温过程。(3) 额外做一个补充搜索过程(4)以多次搜索策略代替标准的SA的单次比较等。但仍然要在退火平衡条件上来回权衡,因为决大多数时候在同一题目里不同组数据的数据量也有着巨大的差距。很多时候强行使全部数据都能在1000毫秒内跑完都会牺牲精度,不强求跑完大数据直接就过不了。
试着采用过加入一个计时函数强行卡着时间限制终止运算输出结果,但实际使用上具体时间点很难拿捏。就以最简单的一个01背包问题为例。
<span style="font-size:12px;">#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define T0 1000
#define TF 0.01
#define T 0.8
#define N 1000
#define M 50
int weight[M],profit[M],contain;
int premaxp = 0, bestmaxp = 0;
int a[M] = {0}, preseq[M] = {0}, bestseq[M]={0};
int calprofit(int a[M])
{
int i = 0;
int p = 0;
for (i = 0; i < M; i++)
p = p + (a[i] * profit[i]);
return p;
}
int calweight(int a[M])
{
int i = 0;
int w = 0;
for (i = 0; i < M; i++)
{
w = w + (weight[i] * a[i]);
}
return w;
}
void initialize(void)
{
int i = 0;
for (i = 0; i < M; i++)
{
a[i] = 0;
preseq[i] = 0;
bestseq[i] = 0;
}
premaxp = calprofit(a);
bestmaxp = premaxp;
}
void getrand(int *i, int *j)
{
*i = 0;
*j = 0;
while (*i == *j)
{
*i = rand()%50;
*j = rand()%50;
}
}
void change(void)
{
int r1 = 0, r2 = 0;
getrand(&r1, &r2);
a[r1] = 1;
a[r2] = 1;
if (calweight(a) > contain)
{
a[r2] = 0;
}
if (calweight(a) > contain)
{
a[r1] = 0;
}
}
void SA(void)
{
double t = T0;
int i = 0, j = 0;
int dif1 = 0, dif2 = 0;
double p = 0.0;
while(t > TF)
{
for (i = 0; i < N; i++)
{
change();
dif1 = calprofit(a) - bestmaxp;
if (dif1 > 0)
{
for (j = 0 ; j < M; j++)
{
preseq[j] = a[j];
bestseq[j] = a[j];
}
premaxp = calprofit(a);
bestmaxp = premaxp;
}
else
{
dif2 = calprofit(a) - premaxp;
if (dif2 > 0)
{
for (j = 0; j < M; j++)
{
preseq[j] = a[j];
}
premaxp = calprofit(a);
}
else
{
p = rand()%20001/20000.0;
if (exp((dif2)/ t) > p)
{
for (j = 0; j < M; j++)
{
preseq[j] = a[j];
}
premaxp = calprofit(a);
}
else
{
for (j = 0; j < M; j++){
a[j] = preseq[j];
}
}
}
}
}
t = t * T;
}</span>
<span style="font-size:12px;">}
int main(void)
{
int i,n;
scanf("%d%d",&contain,&n);
for(i=1;i<=n;i++){
scanf("%d%d",&weight[i],&profit[i]);
}
srand((unsigned)time(NULL));
initialize();
SA();
printf("%d\n", bestmaxp);
return 1;
}
</span>
我没做优化但就以一个(背包容量,M<=200)和N(物品数量,N<=30)这样的数据量居然是过不完的,多测几次上下也有个波动。虽然这里面肯定有很大一部分程序写的太蠢的原因但也可以体现一下退火算法拿来做题时的局限。
何况有的比赛记得是禁用随机函数的,山穷水尽的时候写一个还是比写个运气好才能得10分的没有算法的算法强的。