模拟退火
Posted 中二病没有蛀牙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟退火相关的知识,希望对你有一定的参考价值。
玄学算法get√
模拟退火是一种通用的优化算法,其实学一下完全是因为数学建模也能用,在icpc中可以用于求一个毫无规律的函数最优解。当一个问题的方案数量极大(甚至是无穷的)而且不是一个单峰函数时,我们常使用模拟退火求解。
简单地说,就是将热力学的理论套用到统计学上,将搜寻空间内每一点想像成空气内的分子;分子的能量,就是它本身的动能;而搜寻空间内的每一点,也像空气分子一样带有“能量”,以表示该点对命题的合适程度。演算法先以搜寻空间内一个任意点作起始:每一步先选择一个“邻居”,然后再计算从现有位置到达“邻居”的概率。
包括状态空间与状态产生函数
(1).搜索空间(又叫状态空间)。一般范围比较大,事我们自定义的可行解的集合。
(2).状态函数。状态函数将决定你是否要选用当前的解,对于一个好的退火来说,状态函数的搜索空间应该足够大。
(3).候选解。一般采用随机数来在一定密度内随机选取。
(4).概率分布。大多采取均匀分布或指数分布。
状态转移概率
(1).状态转移概率是指从一个状态向另一个状态的转移概率。
(2).通俗的理解是接受一个新解为当前解的概率。
(3).它与当前的温度参数T有关,随温度下降而减小。
(4).一般采用Metropolis准则。
过程
模拟退火的过程中维护T,另包含三个参数:初始温度
T
0
T_0
T0,降温系数
Δ
\\Delta
Δ,中止温度
T
k
T_k
Tk。
T 0 T_0 T0 是一个比较大的数, Δ \\Delta Δ是一个略小于1的正数, T k T_k Tk是一个略大于 0 的正数。
先让温度 T = T 0 T=T_0 T=T0,然后每次降温时 T = T ⋅ Δ T=T·\\Delta T=T⋅Δ,直到 T ≤ T k T≤T_k T≤Tk为止。此时当前接为最优解。
注意事项
温度T的初始值设置问题。 初始温度高,则搜索到全局最优解的可能性大,但因此要花费大量的计算时间;反之,则可节约计算时间,但全局搜索性能可能受到影响。
退火速度问题。 模拟退火算法的全局搜索性能也与退火速度密切相关。同一温度下的“充分”搜索(退火)是相当必要的,但这需要计算时间。
例题
https://www.luogu.com.cn/problem/P1337
这个题目就是求解重力势能最小的时候的点
显然模拟退火并不适合运气不好的人,提交了25发才ac罢了
加粗样式
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;
#define down 0.996
int n;
double ansx,ansy,answ;
struct node {
int xi,yi,wi;
}a[maxn];
double cal(double x,double y)
{
double ans = 0.0,dx,dy;
for(int i = 1;i <= n;++i){
dx = x - a[i].xi;
dy = y - a[i].yi;
ans += sqrt(dx * dx + dy * dy) * a[i].wi;
}
return ans;
}
void sa()
{
double t =2000;
while(t > 1e-17)
{
double ex = ansx + (rand() * 2 - RAND_MAX ) * t;
double ey = ansy + (rand() * 2 - RAND_MAX ) * t;
double ew = cal(ex,ey);
double de = ew -answ;
if(ew - answ < 0){
ansx = ex;
ansy = ey;
answ = ew;
}
else if(exp(-de / t) * RAND_MAX > rand()){
ansx = ex;
ansy = ey;
}
t *= down;
}
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n; i++){
scanf("%d%d%d",&a[i].xi,&a[i].yi,&a[i].wi);
ansx += a[i].xi;
ansy += a[i].yi;
}
ansx /= n;
ansy /= n;
answ = cal(ansx,ansy);
sa();
sa();
sa();
sa();
sa();
printf("%.3lf %.3lf\\n",ansx,ansy);
return 0;
}
以上是关于模拟退火的主要内容,如果未能解决你的问题,请参考以下文章