关于模拟退火(以下来自百度百科)
模拟退火算法(Simulate Anneal,SA)是一种通用概率演算法,用来在一个大的搜寻空间内找寻命题的最优解。模拟退火是由S.Kirkpatrick, C.D.Gelatt和M.P.Vecchi在1983年所发明的。V.Černý在1985年也独立发明此演算法。模拟退火算法是解决TSP问题的有效方法之一。
模拟退火的出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。模拟退火算法是一种通用的优化算法,其物理退火过程由加温过程、等温过程、冷却过程这三部分组成。
所以要学OI先学好物理
模拟退火的具体流程
先了解爬山算法吧!
爬山算法就是对于一个函数随机选一个起始点,然后向着峰爬(最大or最小)。
但容易陷入局部最优解,如多峰函数。
而模拟退火则是用了一种较为科(玄)学的方法避免陷入局部最优解。
怎么做呢?
就是对于一个较差的解,我们也有几率接受,假设此新解与最优解的差为$\Delta E$,此时温度为$T$,则概率为$e^{\frac{\Delta E}{kT}}$。($k$为一随机数,也可将$k$置为一常数)。
每次Rand这个$\Delta E$,一般为$(rand()\times 2-RANDMAX)\times Temperature$
$RANDMAX$为你这个种子的$Rand$最大值。这样方便$Rand$到正数和负数。
然后就判断是否接受后降温即可(一般取$0.9到0.99$之间,$Temperature*Down$)
当然,这是个玄学算法,需要有玄学的方法加强它的准确性。
以下推荐几种
- 分块退火(对每块退火后合并答案)
- 多退火几次
- 选多个起始位置
- 温度高点
- 降温系数慢点
- 种子选好点(比如
某个八位质数$srand(time(0))$) - 烧香祈福,提交前不要抽卡,洗把脸等等
例题
在此给出$平衡点/吊打XXX$的标程,$均分数据$由于最近没时间以后在更。
#include<bits/stdc++.h>
#define Down 0.997
#define MAXN 1005
using namespace std;
int N;
double Ans_x=0,Ans_y=0,Ans_w;
struct parm{int x,y,w;}Val[MAXN];
struct node
{
inline void Init(int &x)
{
int f=1;x=0;char ch=getchar();
while ('9'<ch||ch<'0') {if (ch=='-') f=-1;ch=getchar();}
while ('0'<=ch&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
inline void Outit(int x)
{
if (x<0) putchar('-'),x*=-1;
if (x>9) Outit(x/10);
putchar(x%10+'0');
}
}Put;
struct Get_Ans
{
double Get_Energy(double x,double y)//系统能量
{
double Sum=0;
for (int i=1;i<=N;i++)
{
double dx=x-Val[i].x,dy=y-Val[i].y;
Sum+=(double)sqrt(dx*dx+dy*dy)*Val[i].w;
}
return Sum;
}
void SA()
{
double Tep=6000;
while (Tep>1e-15)
{
double New_x=Ans_x+(rand()*2-RAND_MAX)*Tep;
double New_y=Ans_y+(rand()*2-RAND_MAX)*Tep;
double New_w=Get_Energy(New_x,New_y);
double Delta=New_w-Ans_w;
if (Delta<0) Ans_x=New_x,Ans_y=New_y,Ans_w=New_w;
else if (exp(-Delta/Tep)*RAND_MAX>rand()) Ans_x=New_x,Ans_y=New_y;
Tep*=Down;
}
}
}T;
int main()
{
srand(time(0));srand(rand());srand(rand());srand(rand());srand(rand());srand(rand());srand(rand());
Put.Init(N);
for (int i=1;i<=N;i++) Put.Init(Val[i].x),Put.Init(Val[i].y),Put.Init(Val[i].w),Ans_x+=Val[i].x,Ans_y+=Val[i].y;
//for (int i=1;i<=N;i++) cout<<Val[i].x<<" "<<Val[i].y<<" "<<Val[i].w<<endl;
Ans_x=(double)Ans_x/N;Ans_y=(double)Ans_y/N;
Ans_w=T.Get_Energy(Ans_x,Ans_y);
T.SA(); T.SA(); T.SA(); T.SA(); T.SA();
printf("%.3lf %.3lf\n",Ans_x,Ans_y);
return 0;
}