模拟退火算法

背景:想象整个解空间为一段连续的函数,y轴为温度,求解的过程相当于求一个全局的最优解。

模拟退火有关的几个概念:

温度(步长): 初始温度 T 0 T_0 T0,终止温度 T E T_E TE
衰减系数: T ′ = T ∗ 0.96 T'=T * 0.96 T=T0.96, 这个0.96就是一个衰减系数,取值为(0,1)之间的一个值,一般来说,系数越大,搜到全局最优解的可能最大。
温度差 △ E \bigtriangleup E E: 新点 T 2 T_2 T2和当前点 T 1 T_1 T1的温度差。 △ E = T 2 − T 1 \bigtriangleup E=T_2-T_1 E=T2T1
局部最优解: 记当前函数为 f ( x ) f(x) f(x) x 0 x_0 x0满足 f ′ ( x 0 ) = 0 f'(x_0)=0 f(x0)=0 f ′ ′ ( x 0 ) > 0 f''(x_0)>0 f(x0)>0的所有点。

步骤:

  1. 确定若干个点作为初始温度 T 0 T_0 T0,开始搜索
  2. 从当前温度附近的区间选择一个新点,温度为 T 1 T_1 T1,开始下一步的搜索。
    • △ E < 0 \bigtriangleup E<0 E<0时,往新点搜索
    • △ E > 0 \bigtriangleup E>0 E>0时,以一定概率往新点搜索,这个概率的经验值为 P ( x ) = e − △ E T P(x)=e^{-{\frac{\bigtriangleup E}{T}}} P(x)=eTE。可以看出, △ E \bigtriangleup E E越大时,往这个点搜索的概率越小

题目

  1. AcWing 3167. 星星还是树
#include<iostream>
#include<ctime>
#include<cmath>
using namespace std;

typedef pair<double, double> PDD;
#define x first
#define y second
const int N = 110;
int n;
PDD p[N];
double ans = 1e18;

//产生l,r之间的一个随机数字
double rand(double l, double r) {
    return (double)rand() / RAND_MAX * (r - l) + l;
}

double get_dis(PDD pt) {
    double res = 0;
    for(int i = 0; i < n; i++) {
        double dx = p[i].x - pt.x, dy = p[i].y - pt.y;
        res += sqrt(dx * dx + dy * dy);
    }
    ans = min(res, ans);
    return res;
}

void simulate_anneal() {
    PDD cur(rand(0, 1e4), rand(0, 1e4));
    for(double t = 1e4; t > 1e-4; t *= 0.99) {
        PDD np(rand(cur.x - t, cur.x + t), rand(cur.y - t, cur.y + t));
        double delta = get_dis(np) - get_dis(cur);
        if(exp(-delta / t) > rand(0, 1)) cur = np;
    }
}


int main()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%lf%lf", &p[i].x, &p[i].y);
    
    for(int i = 0; i < 100; i++) {
        simulate_anneal();
    }
    
    printf("%.0lf\n", ans);
    return 0;
}
  1. 1815. 得到新鲜甜甜圈的最多组数
class Solution {
public:
    int w[31], n;
    int m, ans;

    int calc() {
        int res = 0;
        for(int i = 0, s = 0; i < n; i++) {
            if(!s) res++;
            s = (s + w[i]) % m;
        }
        ans = max(ans, res);
        return res;
    }
    void simualate_anneal() {
        random_shuffle(w, w + n);
        for(double t = 1e6; t > 1e-5; t *= 0.98) {
            int a = rand() % n, b = rand() % n;
            int x = calc();
            swap(w[a], w[b]);
            int y = calc();
            int delta = x - y;
            if(!(exp(-delta /t) > (double)rand() / RAND_MAX)) swap(w[a], w[b]);
        }
    }
    int maxHappyGroups(int batchSize, vector<int>& groups) {
        m = batchSize;
        for(int x: groups) w[n++] = x;

        for(int i = 0; i < 100; i++) {
            simualate_anneal();
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值