模拟退火

模拟退火

模拟退火时有三个参数,分别是初始温度T0 、降温系数Δ (delta)终止温度 Tk 。

其中,T0 是一个比较大的数,Δ 是一个略小于 1 的正数,Tk 是一个略大于 0 的正数。

我们先让温度 T=T0 ,然后每次降温时T=TΔ ,直到 TTk 为止。

我们探讨一下SA的玄学调参。

Q:答案不是最优的怎么办?

A:有以下几种方法:调大Δ (delta)、调大T0 、调小Tk ,以及多跑几遍SA。

当您的程序跑的比较快时,可以选择多跑几遍SA,或者调大 Δ ,从而增大得到最优解的概率。

调大T0 和调小Tk 也可以,而且时间并不会增大太多

大致过程如下

可以看出,随着温度的降低,解逐渐稳定下来,并逐渐集中在最优解附近。

如何生成新解

  • 坐标系内:随机生成一个点,或者生成一个向量。
  • 序列问题: random\_shufflerandom_shuffle 或者随机交换两个数。
  • 网格问题:可以看做二维序列,每次交换两个格子即可。

P1337 [JSOI2004]平衡点 / 吊打XXX

模板代码:

 1 #include <bits/stdc++.h>
 2 #define delta 0.996 // 徐徐降温
 3 
 4 using namespace std;
 5 
 6 struct node {
 7     int x,y,w;
 8 }object[2005];// 存物体的坐标和重量
 9 int n;
10 double ansx,ansy,answ; // 最终答案
11 
12 double energy( double x, double y ){//根据题意变化, 能量总和越小越稳定( 即越接近正确答案 )
13     double r=0,dx,dy;
14     for ( int i=1; i<=n; i++ ) {
15         dx = x-object[i].x;
16         dy = y-object[i].y;
17         r += sqrt(dx*dx+dy*dy)*object[i].w;
18     }
19     return r;
20 }
21 
22 void sa(){
23     double t = 3000; // 温度足够高
24     while ( t>1e-15 ) {
25         double ex = ansx + (rand()*2-RAND_MAX)*t; // 随机产生新答案.
26         double ey = ansy + (rand()*2-RAND_MAX)*t;
27         double ew = energy(ex,ey);
28         double de = ew - answ;
29         if ( de<0 ) { // 新答案能量低,更稳定
30             ansx = ex;
31             ansy = ey;
32             answ = ew;
33         }
34         else if ( exp(-de/t)*RAND_MAX>rand() ) { // 否则根据多项式概率接受
35             ansx = ex;
36             ansy = ey;
37         }
38         t *= delta;
39     }
40 }
41 
42 void solve() // 多跑几遍退火,增加得到最优解的概率
43 {
44     sa();sa();sa();sa();
45 }
46 
47 int main()
48 {
49     cin >> n;
50     for ( int i=1; i<=n; i++ ) {
51         scanf("%d %d %d",&object[i].x,&object[i].y,&object[i].w);
52         ansx += object[i].x;
53         ansy += object[i].y;
54     }
55     ansx/=n; ansy/=n; // 以平均值作为初始答案
56     answ = energy(ansx,ansy);
57     solve();
58     printf("%.3f %.3f
",ansx,ansy);
59 
60     return 0;
61 }
原文地址:https://www.cnblogs.com/wsy107316/p/12207573.html