【oiwiki-模拟退火】

文章介绍了模拟退火算法在解决多峰/多谷函数最值问题中的应用。通过设置初始温度、降温系数和终止温度,不断尝试状态转移并依据概率接受新解,逐步逼近最优解。核心代码展示了如何在二元函数上运用该算法寻找极小值。
摘要由CSDN通过智能技术生成

转载于:模拟退火


模拟退火解决问题:求多峰 / 多谷函数的最值。

需要满足条件: 函数要有一定的连续性,即函数值不能过于随机。


先用一句话概括: 如果新状态的解更优则修改答案,否则以一定概率接受新状态。

模拟退火时我们有三个参数:初始温度 T 0 T_0 T0 ,降温系数 d d d ,终止温度 T k T_k Tk 。其中 T 0 T_0 T0 是一个比较大的数, d d d 是一个非常接近 1 1 1 但是小于 1 1 1 的数, T k T_k Tk 是一个接近 0 0 0 的正数。

首先让温度 T = T 0 T=T_0 T=T0 ,然后进行一次转移尝试,再让 T = d ⋅ T T=d\cdot T T=dT 。当 T < T k T<T_k T<Tk 时模拟退火过程结束,当前最优解即为最终的最优解。

注意为了使得解更为精确,我们通常不直接取当前解作为答案,而是在退火过程中维护遇到的所有解的最优值。

转移尝试:

我们定义当前温度为 T T T ,新状态与已知状态(由已知状态通过随机的方式得到)之间的能量(值)差为 Δ E ( Δ E ⩾ 0 ) \Delta E(\Delta E\geqslant 0) ΔE(ΔE0) ,则发生状态转移(修改最优解)的概率为

P ( Δ E ) = { 1 新状态更优 e − Δ E T 新状态更劣 P(\Delta E)= \begin{cases} 1&\text{新状态更优}\\ e^\frac{-\Delta E}{T}&\text{新状态更劣} \end{cases} P(ΔE)={1eTΔE新状态更优新状态更劣


卡时

有一个 clock() 函数,返回程序运行时间。

可以把主程序中的 simulateAnneal(); 换成 while ((double)clock()/CLOCKS_PER_SEC < MAX_TIME) simulateAnneal(); 。这样子就会一直跑模拟退火,直到用时即将超过时间限制。

这里的 MAX_TIME 是一个自定义的略小于时限的数(单位:秒)。


3167. 星星还是树

问题:求二元函数的极小值。

核心代码:

double ans = 1e18;

double rand(double l, double r) {
    return double(rand()) / RAND_MAX * (r - l) + l;
}

double f(PDD p) { /* */ ; return /* */; }

void saa() {
    PDD p = {rand(0, 1e4), rand(0, 1e4)}; // 在值域范围随机一个初始点
    for(double t = 1e4; t > 1e-4; t *= 0.99) { // 迭代步长,t 初始值一般为值域宽度
        PDD np = {rand(p.fi - t, p.fi + t), rand(p.se - t, p.se + t)};
        double fnp = f(np), fp = f(p);
        ans = min({ans, fnp, fp});
        if(exp(- (fnp - fp) / t) > double(rand()) / RAND_MAX) p = np;
    }
}

int main()
{
    while(double(clock()) / CLOCKS_PER_SEC < 0.9)
        saa();
      
    cout << fixed << setprecision(0) << ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值