随机化

随机化选讲

一.模拟退火

开讲

模拟退火是用来解决能抽象成函数而且要取最值的问题的,以下举两个例子:

  1. 给出二维平面的一些点,让你找出一个点,使这个点到所有给出的点的距离和最小/最大值最小/最小值最大等。

  2. 给出一个序列,让你自行决定序列的顺序,使得这个序列的权值符合一定限制或取最值。

对于解决这些问题,我们需要使用万能的模拟退火,这里我先讲一下模板

基本思想(看不懂可以直接看省流)
  1. 首先,明确一点物理上的芝士,温度(内能)越大,分子运动速度越剧烈,随着温度(内能)降低,分子运动逐渐趋于稳定,而且分子是无规律运动。
  2. 根据这一点,我们可以窥探到模拟退火的本质,我们把一个状态(通常二维平面的一个点或序列的一个排列)抽象成 \(x\) ,它对应的答案抽象成 \(y\)
  3. 首先,我们需要一个较高的初温 \(T\) ,以及一个点模拟分子,然后每次循环随机一个状态模拟分子随机运动(假如是实数范围的话通常与 \(T\) 有关,因为温度越高分子运动越剧烈),假如答案更优我们就接受这个状态,否则以一定的概率( \(T\) 越高概率越大,温度越高分子运动越剧烈)接受劣解。感觉说的有点抽象,请看演示。

省流:先抽象成函数,在图像上随机蹦,更优就取,否则温度高接受劣解的概率高,而且蹦的远

关于接受劣解

我们通常用数学上的 \(exp\) 函数来求概率,下面给出 \(exp\) 函数图像

下面是代码。

if(exp(del/T)*RAND_MAX > rand()) /*接受劣解*/

其中 RAND_MAX 为一个常量,它的值为 32767 ,即 rand() 的最大值。

观察图像我们发现当 \(x<0\) 时,\(y < 1\) ,假如 \(\frac{del}{t} \ge 0\) ,那么对应的 \(y\ge1\) ,这时 exp(del/T)*RAND_MAX 必然是大于 rand() 的,所以我们要保证 $ \frac{del}{t}<0 $ 。

del 为当前状态对应的答案和记录的最优答案的差值,有两种情况:

  1. 要求最小值,所以 del 为正, del 要取负。
  2. 要求最大值,所以 del 为负,不用管。
关于如何退火

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

首先让温度 \(T=T_0\) ,然后按照上述步骤进行一次转移尝试,再让 \(T=d\times T\) 。当 \(T<T_k\) 时模拟退火过程结束,当前最优解即为最终的最优解。

代码
void sa()
{
    double T = 1111;
    while(T > 1e-9)
    {
        int x = nx+(rand()*2-RAND_MAX)*T,y = ny+(rand()*2-RAND_MAX)*T;//随机s
        int now = W(),del = now-ans;//计算当前答案和与记录最优解的差值
        if (del >= 0) ansx = x,ansy = y,ans = now;
        else if (exp(-del/T)*RAND_MAX > rand()) nx = x,ny = y;//接受劣解
        T *= d;//d为降温系数
    }
}

巨大多例题

[TJOI2010] 分金币 随机交换两个金币

RUNAWAY - Run Away 随机移动点

[JSOI2016] 炸弹攻击1 随机移动点,注意生成状态的答案为 0 时不接受劣解

A Star not a Tree? 随机移动点

Haywire 随机交换两只奶牛

[JSOI2004] 平衡点 / 吊打XXX 随机移动点

[SCOI2008] 城堡 随机交换两个城市

Coloring 随机交换两个格子的颜色

[HAOI2006] 均分数据 随机交换两个数跑 \(dp\)

[CEOI2004] 锯木厂选址 随机选两个点作为锯木厂,要推式子降低计算答案的复杂度

偷上网 虽然要可行解,但是可以直接求最大解判断,生成的点记得取模

外太空旅行 随机交换

[AHOI2014/JSOI2014] 保龄球 随机交换

[POI2008] POD-Subdivision of Kingdom 随机交换

[USACO12MAR] Cows in a Skyscraper G 随机交换,也可以试试 shuffle

关路灯 降温系数要小

[WC2018] 通道 这是爬山

[蓝桥杯 2015 国 B] 居民集会 还没调出来呢,写的很烦人,不写了

Mike and distribution 退火因为有两个限制不是很好退,所以直接 shuffle

Oranges and Apples 同上

[POI2004] PRZ 随便退

吃奶酪 旅行商问题,退

部落卫队 退

Leaving the Bar \(n\) 有点大,shuflle

Graph Reconstruction shuffle ,相邻两个点连

[HNOI2011] 任务调度 这个有点恶心,别碰

售货员的难题 旅 ~ 行 ~ 商

邦邦的大合唱站队 The last

退火的两种写法

第一种

将随机到的答案跟最优解比较

void sa()
{
    double T = 1111;
    while(T > 1e-9)
    {
        int x = nx+(rand()*2-RAND_MAX)*T,y = ny+(rand()*2-RAND_MAX)*T;
        int now = W(),del = now-ans;
        if (del >= 0) ansx = x,ansy = y,ans = now;
        else if (exp(-del/T)*RAND_MAX > rand()) nx = x,ny = y;
        T *= d;
    }
}

第二种

跟目前接受的解比较,这个更不容易被卡在一个局部最优解,但是也容易跑偏

void sa()
{
    double T = 1111;
    while(T > 1e-9)
    {
        int x = nx+(rand()*2-RAND_MAX)*T,y = ny+(rand()*2-RAND_MAX)*T;
        int now = W(),del = now-nans;
        else if (del < 0 || exp(-del/T)*RAND_MAX > rand()) nx = x,ny = y;
        ans = min(ans,now);
        T *= d;
    }
}

结论

主要是个人经验,借鉴一下就好。

具体情况还是要具体分析。

  1. 关于退火能解决的问题一般具备的特点

  2. 什么时候用 shuffle

    • 跟序列关系有关,整数范围。
    • 可行解,因为最优解跑 shuffle 不是找死?
    • \(n\) 较大,这样依赖退火生成的排列就会比较集中,不够随机。或者你可以更改退火里随机状态的方式(其实就是变得更随机)
    • 无法判断优劣解的题
  3. 关于我的参数

    • 实数范围的时候终止温度 \(T_k\) 是 1e-11 ,整数范围 1e-7
    • 初温 \(T\) 几千几万都无所谓,能把所有的状态都覆盖掉就行。假如 del 很大的话为了方便接受劣解可以多除一个数
    • 降温系数实数范围 0.996-0.998 差不多了,有的题比较极端,要 0.999,自行调整。整数范围 0.99-0.998
  4. 如何调参

    这里只说一下各个参数所造成的影响,我觉得理解了这个就会自己调参了

    • 初温 \(T\) ,活动范围的大小,接受劣解的概率,退火的次数
    • 末温 \(T_k\) ,与正确答案的接近程度,退火的次数
    • 降温系数 \(down\) ,退火的次数,接受劣解的概率
关于不连续函数

容易发现这里你随机序列的话很容易导致无法把灯全部关掉,这样的问题无法用退火解决。

posted @ 2024-07-15 19:27  ZepX_D  阅读(170)  评论(3)    收藏  举报