模拟退火浅记

模拟退火

P2503 [HAOI2006]均分数据

P1337 [JSOI2004] 平衡点 / 吊打XXX

P4035 [JSOI2008]球形空间产生器

P2210 Haywire

P3878 [TJOI2010]分金币

P2538 [SCOI2008]城堡

const double delta=...; //调一个适合自己的降温参数
int calc()
{
    //用dp,最短路,贪心,模拟等算法求出当前解。
}
int SA()
{
    int T=...;  //初始温度
    int T0=...;  //最终温度
    while(T>T0){
        //对于序列,枚举两个数并进行交换,得出当前解
        //对于坐标,随机生成一个点进行计算
        //对于网格图,随机枚举两个格点进行交换
        //...
        if(当前解优于最优解)  更新最优解
        else if(一定概率不接受)  //还原之前的状态
        T*=delta;  //模拟降温过程
    }
}
int main()
{
   srand(time(0));srand(rand());srand(rand());
   /*主程序*/
}

注意:

int posa=rand()%n+1;
int posb=rand()%n+1;
//注意需要 +1
if(de<0) ...
else if(exp(-1.0*de/t)*RAND_MAX<rand()) ... //这里是**不接受**新解的情况
// exp(-1.0*de/t)*RAND_MAX>rand() 则是**接受**新解的情况
// t 越大,exp(-1.0*de/t)*RAND_MAX 越大,接受新解的概率越大 
//注意要保证 exp 函数中的数为负
  1. 答案要先初始化,就是一开始的时候要 Calc 求一下答案

  2. 利用卡时技巧多跑退火

while((double)clock()/CLOCKS_PER_SEC<0.9)
	SA();

最好不要开到 0.9

多组数据最好不要卡时,或者说平均分配一下每组数据的时限

利用卡时技巧时在本地运行需要用 freopen 来进行数据读入,因为直接在程序上输入的话输入的时间也会算进程序运行时间,导致无法跑模拟退火函数,利用 luogu ide 也可以。

  1. 模拟退火 WA 了可能是因为码错了,也可能是因为自己的参数没调好,WA了太多次的话建议重构

  2. 对于一些题目的数据很小的话,rand 有的时候的随机性不强,建议是

-> rand()*rand()

或 ((rand()&32767)<<15)|(rand()&32767)

因为 rand() 最大只有 2^15

或用 mt19937

  1. 当函数的峰很多时,模拟退火难以跑出最优解,那么可以将值域分成若干段,对于每一段再跑一边模拟退火。

其中段数不一定是要 sqrt n 段,需要根据函数峰值的周期大概估算一下段数。

  1. 一般温度 t 可设为 3000 , Down 可设为 0.97 , t_k 可设为 1e-10 , rand() 的种子自选,跑退火的次数需视具体题目而定。

这个主要可以看个人喜好(

  1. 如果是小数据,rand 下标交换数组值的时候,两个下标可能会 rand 到同一个值,这时不妨继续退火下去,因为数据小、rand 随机性比较差,如果直接 continue 则可能难以使温度下降,进而导致 TLE
posted @   zhln  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示