模拟退火

小灰灰 2023-06-02 12:52 130阅读 0赞

Summary

退火总结.退火其实不难…难的是怎么调参.
贡献两页提交才只有\(55pts\)的经历真是惨不忍睹.这就是调参的艰难.
主要就是这么几个参数:
\(T_0,T,d,T_e\).
分别是初温,当前温度,降温系数,终止温度.
主要就说一下降温系数叭.
这个东西,呃,很玄乎,你改个\(0.\)几它可能人就没了,也可能就直接\(AC\)了.
那么退火这个东西,实质上是个随机化算法,是对爬山算法的优化.
爬山算法的流程就是随机状态,得到该状态解,更优则转移,否则跳过.
这样很显然如果答案是多峰函数,很可能就死在一个峰出不来了.
而退火,则有效地避免了这种情况.
它的工作流程也是,随机状态,得到该状态解,更优则转移,否则以一定的概率去接受这个状态得到的不优解.
为什么会有这个一定概率接受呢?
因为这个不优解有可能来自于另一个峰的某个位置,而我们当前峰的峰顶很可能并不如另一个峰的峰顶要优.于是我们选择接受这个不优解,实际上是给了自己一个机会,不让自己死在一个峰上.
那重点来了!这个一定概率是个啥啊?
你想啊,退火是个玄学随机算法,那么不如这个概率也随个机算了.
于是我们一般取这个概率\(P_c=rand()/RAND\_MAX\).
而这个概率是在这里了,那我们以什么为标准取和这个概率评判呢?
以\(e\)的能量差次幂和当前温度的商作为标准比对,大于则接受否则拒绝(也有可能改变),这个能量差\(\Delta\),要小于等于\(0\).
为什么呢?显然,\(P_c\le 1\)且\(e=2.718281828…\),若\(\Delta>0\),显然我们就只能一直接受或一直拒绝,具体接受还是拒绝取决于你前面的决策.
至于那个著名的退火图…
图挂了
啊,清爽!唉…好像忘了说降温系数咋用了…
一句话,每次降温用.当前温度乘上降温系数.
显然,温度越低,就越稳定.(图也能说明问题)
通用板子:

  1. for (int T = T0 ; T >= Te ; T *= d) { // rand 一个合法状态 // Example:A permutation. int l = rand () % n + 1 , r = rand () % n + 1 ; // 得到该状态 swap ( v[l] , v[r] ) ; // 得出该状态的解 // Example: int res = get_ans () ; // 判断是否比当前解更优,若是,直接继承 // Example: if ( ans < res ) ans = res ; // 否则以一定概率接受该不优解 else if ( exp ( ( res - ans ) / T ) * RAND_MAX < rand () ) // 撤回操作. swap ( v[l] , v[r] ) ; }

重点就是快速得到某个状态的解,所以通常退火应用于\(n\)比较小,或者有其他美妙的性质可以使你快速得到一个解的情况.(所以最优性状压题经常被退过去,但也不是所有的最优性状压都能被退过去)
例题的话,这个叭:
[TJOI2010]分金币
代码的话,也有:

  1. #include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <string> #include <vector> #include <queue> #include <cmath> #include <ctime> #include <map> #include <set> #define MEM(x,y) memset ( x , y , sizeof ( x ) ) #define rep(i,a,b) for (int i = (a) ; i <= (b) ; ++ i) #define per(i,a,b) for (int i = (a) ; i >= (b) ; -- i) #define pii pair < int , int > #define one first #define two second #define rint read<int> #define int long long #define pb push_back #define db double using std::queue ; using std::set ; using std::pair ; using std::max ; using std::min ; using std::priority_queue ; using std::vector ; using std::swap ; using std::sort ; using std::unique ; using std::greater ; template < class T > inline T read () { T x = 0 , f = 1 ; char ch = getchar () ; while ( ch < '0' || ch > '9' ) { if ( ch == '-' ) f = - 1 ; ch = getchar () ; } while ( ch >= '0' && ch <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ; ch = getchar () ; } return f * x ; } const int N = 35 ; int Tot , n , v[N] , ans ; inline int mabs (int x) { return x < 0 ? - x : x ; } inline int initial () { int mid = ( n + 1 ) >> 1 ; int sum1 = 0 , sum2 = 0 ; rep ( i , 1 , mid ) sum1 += v[i] ; rep ( i , mid + 1 , n ) sum2 += v[i] ; return mabs ( sum1 - sum2 ) ; } signed main (int argc , char * argv[]) { srand ( time ( NULL ) ) ; srand ( rand () ^ rand () ) ; Tot = rint () ; while ( Tot -- ) { n = rint () ; ans = 0x7f7f7f7f ; rep ( i , 1 , n ) v[i] = rint () ; rep ( i , 1 , 100 ) { db T = 5e4 ; while ( T >= 1e-10 ) { int l = rand () % n + 1 , r = rand () % n + 1 ; swap ( v[l] , v[r] ) ; int res = initial () ; if ( ans > res ) ans = res ; else if ( exp ( ( ans - res ) / T ) * RAND_MAX < (db) rand () ) swap ( v[l] , v[r] ) ; T *= 0.98 ; } } printf ("%lld\n" , ans ) ; } system ("pause") ; return 0 ; }

什么?\(n\le500\)?退就完了!

转载于:https://www.cnblogs.com/Equinox-Flower/p/11586969.html

发表评论

表情:
评论列表 (有 0 条评论,130人围观)

还没有评论,来说两句吧...

相关阅读

    相关 模拟退火

    Summary 退火总结.退火其实不难...难的是怎么调参. 贡献两页提交才只有\\(55pts\\)的经历真是惨不忍睹.这就是调参的艰难. 主要就是这么几个参数

    相关 模拟退火算法(代码)

    在实际日常中,人们会经常遇到如下问题:在某个给定的定义域![X][]内,求函数![f(x)][f_x]对应的最优值。此处以最小值问题举例(最大值问题可以等价转化成最小值问题),

    相关 模拟退火入门题*3

    CTS2019Day2T1出了道可以乱搞的的计算几何,我辛辛苦苦写了280多行,结果就给了我10分,这不气炸了。 据我分析,可能是因为我没有学过模拟退火,乱搞是真的乱搞,儿