现在的位置: 首页 > 综合 > 正文

模拟退火算法

2013年02月20日 ⁄ 综合 ⁄ 共 2702字 ⁄ 字号 评论关闭

模拟退火算法的理论讲解:

《模拟退火与遗传算法》

《模拟退火算法》



POJ: 2069   Super star

求一个半径最小的球体,包含所有的点。模拟退火,不断缩小半径,搜索……

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define N 33
#define eps 1e-7

struct POINT {
    double x, y, z;
} p[N], s;
int n;

inline double dist(const POINT &a, const POINT &b) {
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}
void solve() {
    s.x = s.y = s.z = 0;
    double ans = 1e20, delta = 100;
    while (delta > eps) {
        int d = 0;
        for (int i=1; i<n; i++)
            if (dist(s, p[i]) > dist(s, p[d]))
                d = i;
        double md = dist(s, p[d]);
        if (ans > md) ans = md;
        s.x += (p[d].x-s.x)/md*delta;
        s.y += (p[d].y-s.y)/md*delta;
        s.z += (p[d].z-s.z)/md*delta;
        delta *= 0.98;
    }
    printf("%.5lf\n", ans);
}
int main() {
    while (scanf("%d", &n) == 1 && n) {
        for (int i=0; i<n; i++)
            scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z);
        solve();
    }
    return 0;
}



POJ:1379 Run Away

基础模拟退火题目。

在平面内部随机取NUM个点,然后对每一个点进行“退火”(随机向各个方向移动T次,寻求最优结果,不断增大圆的半径)。

从NUM个结果中找到半径最大的点即可。


附上一篇国家集训队论文:浅谈随机化思想在几何问题中的应用

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstdlib>
using namespace std;
#define N 1005
#define NUM 30
#define eps 1e-2

struct POINT {
    double x, y, d;
    POINT() {}
    POINT(double _x, double _y): x(_x), y(_y) {}
} s, p[N], rp[NUM];

int x, y, n;
double dis(POINT a) {
    double ret = 1e20;
    double tmp;
    for (int i=0; i<n; i++) {
        tmp = sqrt((a.x-p[i].x)*(a.x-p[i].x) + (a.y-p[i].y)*(a.y-p[i].y));
        if (tmp < ret) ret = tmp;
    }
    return ret;
}
void Init() {
    scanf("%d%d%d", &x, &y, &n);
    for (int i=0; i<n; i++)
        scanf("%lf %lf", &p[i].x, &p[i].y);
    double d;
    for (int i=0; i<NUM; i++) {
        rp[i].x = rand()%x + 1;
        rp[i].y = rand()%y + 1;
        rp[i].d = dis(rp[i]);
    }
}
void solve() {
    double delta = (double)max(x, y)/sqrt((double)n)+1;
    double theta;
    POINT t;
    while (delta > eps) {
        for (int i=0; i<NUM; i++) {
            for (int k=0; k<NUM; k++) {
                theta = rand();
                t.x = rp[i].x + cos(theta)*delta;
                t.y = rp[i].y + sin(theta)*delta;
                t.d = dis(t);
                if (0<=t.x && t.x<=x && 0<=t.y && t.y<=y) {
                    if (t.d > rp[i].d) rp[i] = t;
                }
            }
        }
        delta *= 0.8;
    }
    int k = 0;
    for (int i=1; i<NUM; i++)
        if (rp[k].d < rp[i].d) k = i;
    printf("The safest point is (%.1lf, %.1lf).\n", rp[k].x, rp[k].y);
}
int main() {
    int cas;
    scanf("%d", &cas);
    while (cas--) {
        Init();
        solve();
    }
    return 0;
}

POJ:2420 A Star not a Tree

求费马点:到所有点距离和最短的点称之为费马点。然后输出最短距离和。简单的模拟退火。

上代码:里面向不同方向走的时候,才用了随机化的方法,我取了10这个常数,这个理论上没有要求必须上下左右四个方向或者八个方向什么的,满足随机即可,这个常数的选择应该是比较凑巧可以过掉题目的。Just so so 了哈。。。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 110
#define eps 1e-2
struct POINT {
    double x, y;
} p[N], s, c;
int n;
double dist(POINT &a) {
    double ret = 0;
    for (int i=0; i<n; i++)
        ret += sqrt((p[i].x-a.x)*(p[i].x-a.x)+(p[i].y-a.y)*(p[i].y-a.y));
    return ret;
}
void solve() {
    s.x = s.y = 0;

    for (double delta = 10000.0; delta>eps; delta*=0.9) {
        for (int i=0; i<10; i++) {
            double t = rand();
            c.x = s.x + cos(t)*delta;
            c.y = s.y + sin(t)*delta;
            if (dist(c) < dist(s)) s = c;
        }
    }
    printf("%.0lf\n", dist(s));
}
int main() {
    scanf("%d", &n);
    for (int i=0; i<n; i++)
        scanf("%lf%lf", &p[i].x, &p[i].y);
    solve();
    return 0;
}

抱歉!评论已关闭.