【codevs 1344】线型网络&模拟退火详解

来源:互联网 发布:arnavi软件下载 编辑:程序博客网 时间:2024/05/29 18:49

以下都是些玄学算法,之后慢慢搞。

引入

现在假设给定一个函数,求最大值。
穷举法:直接搜索,但是效率低下。
爬山算法:每次朝着更高的地方走,走到最高点为止。但是会出现走到极大值但走不到最大值的情况。
模拟退火算法:每次朝着更高的地方走,但是“有一定概率朝着更低的地方走”,相比较爬山算法而言,模拟退火算法的改进就是尽量跳出极大值的局限。
遗传算法:和生物学中的遗传很像,初始给定一些个体,就是初始情况,个体之间的基因交换理解为一个新的方案。随着时间的推移,那些较差的个体(较差的解)逐渐被淘汰,剩下的就是较优的个体(较优的解)。
蚁群算法:一开始所有的搜索方案都是平等的,随着搜索的进行,有些路径会更优,这时候优先搜索这些较优的路径。

下面的题就是用模拟退火算法来解决。

金属冶炼的时候金属温度越高,金属分子间运动越剧烈。同样在模拟退火中,一开始的时候温度最高,接受较差解的概率越大;随着搜索的进行,温度不断降低,接受较差解的概率越小。可以这样理解,爬山算法是直接朝着高处走,而模拟退火是一开始乱跳,跳了几次之后就不再乱跳,而是也朝着最高处走,这样就能走出极大值的局限了。

就拿本题开刀了。
首先,我们要判断这个“接受较差解的概率”到底有多大。
P(dE)=edET
设当前方案和之前方案的能量变化量为dE(也就是本题中,两种方案的差)。如果dE小于等于0,说明是一个较优解,则一定接受;如果dE大于0,说明是一个较差解,所以这个概率P就能从0取到1。其中T表示当前的温度,温度越高,概率也越高。

bool accept(double delta,double tmp){    if (delta <= 0) return true;    return rand() % max_rand <= exp((-delta)/tmp) * max_rand;}

下面是搜索的过程,将这个函数多运行几次,找出的最优解就是答案了。

double solve(){    int i; double dis1,dis2;    double res = 100000000.0;    const double max_tmp = 100000;//初始温度    const double dec = 0.999;//每次降低温度的比率    double tmp = max_tmp;    fo(i,1,n) p[i] = i; calc(p,dis1);    while (tmp > 0.01)        {            drunk(); calc(pp,dis2);//随机交换两个点,表示一种新的情况            if (accept(dis2-dis1,tmp))                {fo(i,1,n) p[i] = pp[i]; dis1 = dis2;}            tmp *= dec;//降温            if (dis1 < res) res = dis1;        }    return res;}

下面是完整程序,一般来说:
1、T大10倍,时间复杂度也大10倍;
2、dec后面多一个9,时间复杂度大10倍;
3、初温大10倍,时间复杂度大20%左右。

#include<cmath>#include<cstdio>#include<vector>#include<queue>#include<cstring>#include<iomanip>#include<stdlib.h>#include<iostream>#include<algorithm>#define ll long long#define inf 1000000000#define mod 1000000007#define N 30#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;int n,i,j,T;int cnt;int pp[N],p[N],x[N],y[N];double dis[N][N];const int max_rand = 2333333;void drunk(){    int i,x,y;    fo(i,1,n) pp[i] = p[i];    x = rand() % n + 1; y = rand() % n + 1;    swap(pp[x],pp[y]);}void calc(int *p,double &s){    int i; s = 0;    fo(i,1,n-1) s += dis[p[i]][p[i+1]];}bool accept(double delta,double tmp){    if (delta <= 0) return true;    return rand() % max_rand <= exp((-delta)/tmp) * max_rand;}double solve(){    int i; double dis1,dis2;    double res = 100000000.0;    const double max_tmp = 100000;    const double dec = 0.999;    double tmp = max_tmp;    fo(i,1,n) p[i] = i; calc(p,dis1);    while (tmp > 0.01)        {            drunk(); calc(pp,dis2);            if (accept(dis2-dis1,tmp))                {fo(i,1,n) p[i] = pp[i]; dis1 = dis2;}            tmp *= dec;            cnt++;            if (dis1 < res) res = dis1;        }    return res;}int main(){    cnt = 0;    srand(233333);    scanf("%d",&n);    fo(i,1,n) scanf("%d%d",&x[i],&y[i]);    fo(i,1,n) fo(j,1,n) dis[i][j] = dis[j][i] = hypot(x[i]-x[j],y[i]-y[j]);    double ans = 1000000000.0; T = 150;    while (T--) ans = min(ans,solve());    printf("%.2lf\n",ans);    return 0;}
0 0
原创粉丝点击