浅谈模拟退火
Posted h-lka
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈模拟退火相关的知识,希望对你有一定的参考价值。
模拟退火,很有力的一个偏分武器。
对于一类搜索,或者是有明确答案空间的题目,且不会写正解,爆搜过不去,剪枝减不了的时候,我们可以考虑用模拟退火偏分。
模拟退火模拟的是一个物理过程,即一个物体由高温逐步降温的过程。也可以理解为,在我们寻找答案的过程中,答案所存在的范围越来越小,以此类比降温。
也就是说,我们先开辟一个温度,或者叫搜索空间,在这个空间中,我们随机一个答案,判断这个答案和当前已知答案的优劣。
若优于当前解,显然我们必然要更新的。如果比当前解劣呢?
注意到,我们随机的答案是和上一个答案有关联的,有时候我们随机的将会是一个与原答案的变化幅度。也就是说,一个答案,虽然它劣,但是它的周围是可能有更优解的。
有一个选择的准则:如果当前解优则更新;否则:
定义(delta) ( ext{t'})为当前答案与新答案的变化幅度。则,我们以(exp(frac{-delta t'}{T}))的概率来接受这个答案。(T)表示当前的“温度”。
我不会证这东西qwq
然而有很多题没有这个准则,只更新更优解也可以过
于是,我们得到一个基本算法模型:
首先,确定温度(T).
然后,确定降温系数。
使得(T)在不断降温的情况下,根据当前解随机出下一个解。
判断下一个解的优劣。并选择是否接受。
降温。
这个算法完成了。
值得注意的是,如果考试要用,一定要暴力多对拍几次,今天我做题对拍了半天没有问题,然而过了一会还是出现了差别。
如果代码过不了,注意调整初始温度和降温参数。程序的效率与它们是紧密相关的。精度不够的话可以试试( ext{long double.})
面对不会的题也可以得分,何乐而不为呢?
下面放一个(P1337)平衡点的代码。
题目大意就是使得距离乘以重量的和最小,这样才平衡。
我们退火找一个点,判断它与当前答案的差。这里的差别指的是我们求的(sum dist*wight)的差别。注意不同题目比较的标准不同,计算函数也不同。
这份代码我没有选择概率放弃解的写法,但依旧跑的挺快。
#include<bits/stdc++.h>
using namespace std;
typedef long double db;
const db h=0.96;
int n;
struct point{
int x,y,w;
}p[5000];
db ansx,ansy,answ;
inline db calc(db x,db y){
db sum=0;
for(int i=1;i<=n;++i){
db dx=x-p[i].x;
db dy=y-p[i].y;
sum+=(sqrt(dx*dx+dy*dy))*p[i].w;
}
return sum;
}
inline void solve(){
db T=3000;
db x=ansx,y=ansy;
answ=min(answ,calc(x,y));
while(T>1e-14){
db qx=ansx+T*(rand()*2-RAND_MAX),qy=ansy+T*(rand()*2-RAND_MAX);
db Ew=calc(qx,qy);
if(Ew<answ){
answ=Ew;
ansx=qx;
ansy=qy;
}
T*=h;
}
}
void work(){solve();}
int main(){
srand(time(0));
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].w);
answ=9999999999;work();
printf("%.3Lf %.3Lf
",ansx,ansy);
return 0;
}
以上是关于浅谈模拟退火的主要内容,如果未能解决你的问题,请参考以下文章