为什么我的眼中常含泪水。因为我还有一个算法不会……为了少流点泪,还是多码点题……
今天学了模拟退火算法,听起来很高大上,实现起来代码很少。先贴一个模拟退火的物理解释(来源于百度百科)
模拟退火算法来源于固体退火原理,是一种基于概率的算法,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。
额,应该看不懂吧……在博客上看到了一个很有意思的解释:
想象把一群喝醉的兔子随机放到山群中,兔子要往山顶走,一开始兔子摇摇晃晃的,可能往上走,可能往下走,后来兔子们清醒了,坚定的往山顶走。这样,在很高的概率下,总有一只兔子能走到最高的山顶。
这样理解起来就很容易了吧。概括来说就是,一开始兔子的脚步很大,也容易往下走,随着时间的推移,兔子的脚步越来越小,逐渐的不接受往下走的更新,这就是退火的原理。
这和爬山算法的区别是什么呢?就是以(随时间推移而减小的)概率接受往下走的状态更新。
hdu1109是一道基本的退火题,题目如下:
Run Away
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 1619 Accepted Submission(s): 669
题意分析:求全局最优点,使得这个最优点到所有点的最小值(欧式距离)最大
直接贴代码啦:
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<stdlib.h>
#include<math.h>
#include<time.h>
using namespace std;
int n,m,k;
const int iniNum=50;
const int maxn=1005;
const float pai=acos(-1);
struct node{
float x,y,val;
}pos[maxn],initSeq[iniNum+5];
float Rand(float L,float R)//产生L到R之间的随机数
{
return (rand()%10000)/10000.0*(R-L)+L;
}
float dis(node a)
{
float ans=1000000;
for(int i=0;i<k;i++)
ans=min(ans,(float)sqrt((a.x-pos[i].x)*(a.x-pos[i].x)+(a.y-pos[i].y)*(a.y-pos[i].y)));
return ans;
}
void init()//产生初始点位置,val是距离所有点中的最小值
{
//printf("initial place:\n");
for(int i=0;i<iniNum;i++)
{
float x=Rand(0,n);
float y=Rand(0,m);
initSeq[i].x=x;
initSeq[i].y=y;
initSeq[i].val=dis(initSeq[i]);
//printf("x: %.2f y:%.2f\n",x,y);
}
}
node nextSolution(node cur,float step)//更新后的点
{
node next;
float theta=Rand(0,2*pai);//以任意角度更新
next.x=cur.x+cos(theta)*step;
next.y=cur.y+sin(theta)*step;
while(next.x<0||next.x>n)
{
theta=Rand(0,2*pai);
next.x=cur.x+cos(theta)*step;
}
while(next.y<0||next.y>m)
{
theta=Rand(0,2*pai);
next.y=cur.y+sin(theta)*step;
}
return next;
}
int sa()
{
float T=1000,delta=0.98,t_min=0.0001;//初始温度和退火的速率
float t=T;
while(t>t_min)
{
for(int i=0;i<iniNum;i++)
{
node next=nextSolution(initSeq[i],t);
if(dis(next)>dis(initSeq[i]))//往高处走,直接替换
initSeq[i]=next;
else//往低处走,以概率替换
{
float p,r,q;
r=dis(next)-dis(initSeq[i]);//<0
p=1/(1+exp(r/T));
//printf("p: %.2f\n",p);
q=Rand(0.5,1);//因为p的范围是在(0.5,1)内
if(p>q)//接受
initSeq[i]=next;
}
}
t*=delta;
}
float ans=dis(initSeq[0]),ans_i=0;
for(int i=1;i<iniNum;i++)
{
//printf("test:\n");
//printf("x:%.2f y:%.2f\n",initSeq[i].x,initSeq[i].y);
if(ans<dis(initSeq[i]))
{
ans_i=i;
ans=dis(initSeq[i]);
}
}
return ans_i;
}
int main()
{
int T;
cin>>T;
srand((unsigned)(time(NULL)));
while(T--)
{
cin>>n>>m>>k;
for(int i=0;i<k;i++)
scanf("%f%f",&pos[i].x,&pos[i].y);
init();//初始化随机点
int ans_i=sa();
printf("The safest point is (%.1f, %.1f).\n",initSeq[ans_i].x,initSeq[ans_i].y);
}
return 0;
}