模拟退火算法

打算补一下上一把牛客比赛的K题Solar Energy,需要模拟退火知识点的储备。打算速通以后出出看K题。

参考资料

感谢在网上分享学习心得的各位同学。
模拟退火学习笔记1
模拟退火学习笔记2
模拟退火学习笔记3

正式阐述

命名来源:

模拟退火算法来源于固体退火原理,是一种基于概率的算法,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。

退火思想引入到组合优化领域。它是基于Monte-Carlo迭代求解策略的一种随机寻优算法,其出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。模拟退火算法从某一较高初温出发,伴随温度参数的不断下降,结合概率突跳特性在解空间中随机寻找目标函数的全局最优解,即在局部最优解能概率性地跳出并最终趋于全局最优。
模拟退火算法是一种通用的优化算法,理论上算法具有概率的全局优化性能,目前已在工程中得到了广泛应用,诸如VLSI、生产调度、控制工程、机器学习、神经网络、信号处理等领域。
模拟退火算法是通过赋予搜索过程一种时变且最终趋于零的概率突跳性,从而可有效避免陷入局部极小并最终趋于全局最优的串行结构的优化算法。
得到解组合优化问题的模拟退火算法:由初始解i和控制参数初值t开始,对当前解重复“产生新解→计算目标函数差→接受或舍弃”的迭代,并逐步衰减t值,算法终止时的当前解即为所得近似最优解,这是基于蒙特卡罗迭代求解法的一种启发式随机搜索过程。退火过程由冷却进度表(Cooling Schedule)控制,包括控制参数的初值t及其衰减因子Δt、每个t值时的迭代次数L和停止条件S。

以上资料来源于百度百科。
重点已经用加粗显示。

概率性跳出,有效避免陷入局部极小值并最终趋向于全局最小值。

适用问题:

总而言之,模拟退火SA是玄学的随机化算法。
当一个问题的方案数量极大(甚至是无穷时),而且不是一个单峰函数极值不唯一时,我们常使用模拟退火求解。

计算过程:


以上截图自百度百科,说实话我没咋看明白。
大概意思就是先找到一个初始解(局部最优解),然后跳出去寻找,有概率找到最优解。
显然算法是否能AC出最优解与初始解的位置以及搜寻的附近解的区域大小有关。
如果 Δ t 设置的比较大(左右横跳一次的半径)的话有几率跳出去,但是太大了又可能跳来跳去从而找不到最优解。
emmm,总而言之?看命!

著名动图:

公式:

  • 第一步!
    ​ 程序开始时,我们先要 srand(一个常数)。这个常数可以决定分数,可以使用 233333,2147483647 等等。
  • 继续调试!
    模拟退火一次往往跑不出最优解,常数一换,又可以冲冲冲!
    每次看到一片红wa都要感叹怎么不是股票是股票就好了。
    可以用一个全局变量记录所有跑过的 SA 的最优解,每次从哪个最优解开始继续 SA,可以减少误差。
    这步好像比较进阶,待我细学一下!
  • 时间复杂度
    主要看脸。
    这是算法竞赛界的抽卡。

如何生成新解

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

T(t) = T0/log10(1+t)这个叫经典模拟退火
T(t) = T0/(t+1)这个叫快速模拟退火

一些技巧

  1. 分块模拟退火
    有时函数的峰很多,模拟退火难以跑出最优解。
    此时可以把整个值域分成几段,每段跑一遍模拟退火,然后再取最优解。

  2. 卡时
    有一个 clock() 函数,返回程序运行时间。
    可以把主程序中的 simulateAnneal(); 换成 while((double)clock()/CLOCKS_PER_SEC < MAX_TIME) simulateAnneal();
    这样子就会一直跑模拟退火,直到用时即将超过时间限制。
    这里的 MAX_TIME是一个自定义的略小于时限的数。

板子:

伪代码:

/*
* J(y):在状态y时的评价函数值
* Y(i):表示当前状态
* Y(i+1):表示新的状态
* r: 用于控制降温的快慢
* T: 系统的温度,T0——>正无穷
* T_min :温度的下限,若温度T达到T_min,则停止搜索
*/
while( T > T_min )
{dE = J( Y(i+1) ) - J( Y(i) ) ;if ( dE >=0 ) //表达移动后得到更优解,则总是接受移动
Y(i+1) = Y(i) ; //接受从Y(i)到Y(i+1)的移动else{// 函数exp( dE/T )的取值范围是(0,1) ,dE/T越大,则exp( dE/T )也越大
if ( exp( dE/T ) > random( 0 , 1 ) )
Y(i+1) = Y(i) ; //接受从Y(i)到Y(i+1)的移动}T = r * T ; //降温退火 ,0<r<1 。r越大,降温越慢;r越小,降温越快/** 如果 r 设置的比较大的话有几率跳出去,但是太大了又可能跳来跳去从而找不到最优解。*/i ++ ;
}

例题1:给出平面内的一些点,求在一个矩形的范围内找到一个点,使得这个点到其他所有点的最小距离最大(费马点)。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define EPS 1e-3
#define INF 1e12
#define MAX 10000
using namespace std;struct Point{double x,y;double length;Point(double _x,double _y):x(_x),y(_y) {}Point() {}
}point[MAX],now,ans;int cases;
double m,n;
int cnt;double dE;inline double Calc(Point p1,Point p2);
inline void Initialize();
inline void SA();
inline double Rand();
inline double Statistic(Point p);int main()
{srand(19970804);for(cin >> cases;cases; --cases) {scanf("%lf%lf%d",&m,&n,&cnt);for(int i = 1;i <= cnt; ++i)scanf("%lf%lf",&point[i].x,&point[i].y);Initialize();SA();printf("The safest point is (%.1lf, %.1lf).\n",ans.x,ans.y);}return 0;
}inline double Calc(Point p1,Point p2)
{return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}inline void Initialize()
{now = Point(m * Rand(),n * Rand());ans.length = 0.0;
}inline void SA()
{double T = sqrt(m * m + n * n) * 0.5;for(;T > EPS;T *= 0.998) {double alpha = 2.0 * acos(-1.0) * Rand();Point temp(now.x + T * cos(alpha) * Rand(),now.y + T * sin(alpha) * Rand());if(temp.x < 0 || temp.y < 0 || temp.x > m || temp.y > n)continue;dE = Statistic(temp) - Statistic(now);if(dE >= 0 || exp(dE / T) >= Rand())now = temp;}T = 0.5;for(int i = 1;i <= 500; ++i) {double alpha = 2.0 * acos(-1.0) * Rand();Point temp(ans.x + T * cos(alpha) * Rand(),ans.y + T * sin(alpha) * Rand());if(temp.x < 0 || temp.y < 0 || temp.x > m || temp.y > n)continue;Statistic(temp);}
}inline double Rand()
{return (rand() % 1000 + 1) / 1000.0;
}inline double Statistic(Point p)
{double re = INF;for(int i = 1;i <= cnt; ++i)re = min(re,Calc(point[i],p));if(re > ans.length)ans = p,ans.length = re;return re;
}

例题二:求序列最小值

宝藏

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <iostream>
using namespace std;
const int maxn = 16;
int g[maxn][maxn], n, m;
#define INF 0x3f3f3f3f
struct Node {int d[maxn], deep[maxn];Node() {for (int i = 1; i <= n; i++)d[i] = i, deep[i] = 0;}Node(const Node &rhs) {memcpy(d, rhs.d, sizeof(d));memset(deep, 0, sizeof(deep));}Node operator=(const Node &rhs) {memcpy(d, rhs.d, sizeof(d));memset(deep, 0, sizeof(deep));return *this;}inline int solve() {//按照打通顺序求代价int ret = 0;deep[d[1]] = 1;//第一个打通的节点深度为1for (int i = 2; i <= n; i++) {int temp = 0x7fffffff;for (int j = 1; j < i; j++) {//枚举由哪一个已经打通的节点打通道路if (g[d[i]][d[j]] != 0x3f3f3f3f && deep[d[j]] * g[d[i]][d[j]] < temp)temp = deep[d[j]] * g[d[i]][d[j]], deep[d[i]] = deep[d[j]] + 1;}if (temp == 0x7fffffff)return 0x7fffffff;//当前方案不可行可以提前退出了ret += temp;}return ret;}
};int SA() {//SAconst double max_temp = 10000.0;//初始温度const double delta_temp = 0.98;//降温系数double temp = max_temp;//当前温度Node path;//打通顺序while (temp > 0.1) {Node tpath(path);swap(tpath.d[rand() % n + 1], tpath.d[rand() % n + 1]);//随机一个新解double delta = tpath.solve() - path.solve();//求解if (delta < 0)path = tpath;//如果新解更优,则接受else if (exp(-delta / temp) * RAND_MAX >= rand())path = tpath;//否则以一定概率接受temp *= delta_temp;}return path.solve();
}int main() {srand(rand());memset(g, 0x3f, sizeof(g));cin >> n >> m;for (int u, v, d, i = 1; i <= m; i++) {cin>>u>>v>>d;g[u][v] = min(g[u][v], d);g[v][u] = min(g[v][u], d);}int ans = INF;for (int i = 1; i <= 300; i++)//跑SA,取最优值ans = min(ans, SA());printf("%d\n", ans);return 0;
}

板子

int SA() {//SAconst double max_temp = 10000.0;//初始温度const double delta_temp = 0.98;//降温系数double temp = max_temp;//当前温度Node path;//打通顺序while (temp > 0.1) {Node tpath(path);swap(tpath.d[rand() % n + 1], tpath.d[rand() % n + 1]);//随机一个新解double delta = tpath.solve() - path.solve();//求解if (delta < 0)path = tpath;//如果新解更优,则接受else if (exp(-delta / temp) * RAND_MAX >= rand())path = tpath;//否则以一定概率接受temp *= delta_temp;}return path.solve();
}

接口

    srand(rand());for (int i = 1; i <= 300; i++)//跑SA,取最优值ans = min(ans, SA());

例题三:求经典多项式最值

Hdu 2899
给出y , 求
的最小值

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>using namespace std;const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e4 + 5;
double n, x, y;
double now;
double ans;double f(double x) {//评估函数,计算.return 6.0 * pow(x, 7) + 8.0 * pow(x, 6) + 7.0 * pow(x, 3) + 5.0 * pow(x, 2) - y * x;
}double sa() {double next_next;ans = inf;//最开始的能量值,需要非常大double T = 100.0;//初始温度//(可以适当修改,最好和给的数据最大范围相同,或者缩小其原来0.1)double d = 0.9765432;//降温系数// (可以适当修改,影响结果的精度和循环的次数,)double eps = 1e-12;//最终温度// (要是因为精度问题,可以适当减小最终温度)double TT = 0.99;//采纳新解的初始概率double dd = 0.99;//(可以适当修改,采纳新解变更的概率)// (这个概率下面新解更新的时候,最好和未采纳的新解更新的次数是一半一半)double res = f(now);//传入的初始默认解(now)下得到的评估能量值if (res < ans)ans = res;int num = 1;while (T > eps) {for (int i = -1; i <= 1; ++i)if (now + i * T <= 100 && now + T * i >= 0) {next_next = now + T * i;//新解double tmp = f(next_next);//新解下的评估能量值if (tmp < ans) ans = tmp;//降温成功,更新当前最优解if (tmp < res) res = tmp, now = next_next;// 降温成功,采纳新解else if (TT > rand() % 10000 / 10000.0) {res = tmp;now = next_next;}//没有降温成功,但是以一定的概率采纳新解// 用于测试,设定的采纳新解的概率,是否为一半一半,可以适当修改降温参数dd}T *= d;TT *= dd;}return ans;
}int main() {srand(114514);cin>>n;while (n--) {cin >> y;now = 50.0;sa();cout << ans << fixed << setprecision(4) << endl;}return 0;
}

小结:

做了题目感觉靠自己理解就好,写就完事了。
这几次都蛮欧的,虽然是随机化算法但还是一次就A了。
这算法我学两三天了。
一会就冲一下K.Solar Energy看看!好耶!

# 知识点学习——模拟退火算法相关推荐

  1. [算法学习]模拟退火算法(SA)、遗传算法(GA)、布谷鸟算法(CS)、人工蜂群算法(ABC)学习笔记---附MATLAB注释代码

    目录 1.模拟退火算法(Simulated Annealing,SA) 1.1 本质: 1.2 算法思想 1.3 SA流程图 1.4 模拟退火过程 1.5 SA解决TSP问题 1.6 SA改进方向 1 ...

  2. 数学建模国赛 常考赛题类型(模拟退火算法、粒子群算法、遗传算法)

    不知小伙伴们有没有发现,在1992~2020年历年国赛赛题中,优化类赛题所占的比例非常大,如在近五年的题目中: 2016A:系泊系统的设计: 2017B:"拍照赚钱"的任务定价 2 ...

  3. 模拟退火算法求解TSP问题

    前言:模拟退火(simulated annealing)技术,在每一步都以一定的概率接受比当前结果更差的结果,从而有助于"跳出"局部极小.在每次迭代过程中,接受"次优解& ...

  4. 模拟退火算法详细讲解(含实例python代码)

    模拟退火算法详细讲解(含实例python代码) (一)模拟退火算法简介 (二)模拟退火算法原理 (三)退火过程中参数控制 (四)算法步骤 (五)实例分析 最近老师要求做模拟退火算法实验,看了很多博客之 ...

  5. 算法学习之模拟退火算法路径规划(python代码实现)

    模拟退火算法路径规划(python代码实现) 一.引言 二.算法介绍以及伪代码 1.算法通俗介绍 2.路径规划算法伪代码 三.算法流程及代码实现 1.地图创建 2.初始化路径 小结 3.计算适应度值 ...

  6. 机器学习知识点(十六)集成学习AdaBoost算法Java实现

    理解http://blog.csdn.net/fjssharpsword/article/details/61913092中AdaBoost算法,从网上找了一套简单的代码加以理解. 1.基分类器,实现 ...

  7. 模拟退火算法学习笔记

    0 基本术语介绍 (1)组合优化(Combinatorial Optimization) 组合优化问题的目标是从组合问题的可行解集中求出最优解,通常可描述为:令Ω={S1,S2,-,Sn}为所有状态构 ...

  8. 数学建模学习(83):模拟退火算法(SA)求解多元函数

    接着上一篇函数的可视化进行讲解,那一篇画图实在太棒了,因此我单独写了一篇,本篇就着重于算法的原理与实现. 文章目录 一.基本思想 二.模拟退火算法过程 三.模拟退火算法的优缺点 四.函数可视化 4.1 ...

  9. 《MATLAB智能算法30个案例》:第19章 基于模拟退火算法的TSP算法

    <MATLAB智能算法30个案例>:第19章 基于模拟退火算法的TSP算法 1. 前言 2. MATLAB 仿真示例 3. 小结 1. 前言 <MATLAB智能算法30个案例分析&g ...

最新文章

  1. InputFlinger崩溃问题分析报告
  2. python网课什么平台好-python网课什么平台好
  3. linux 配置sftp日志,在CentOS 7.5上为SFTP配置ProFTPd
  4. OpenCV实现张正友相机标定源代码
  5. 您认为有关垃圾收集的7件事-完全错了
  6. 关于机器学习会议的点评
  7. [中奖]第九届“泰迪杯”挑战赛A题
  8. 线性代数(一)—— 行列式
  9. NX二次开发(C#)-UI Styler-选择对象TaggedObject转换为Body、Face等对象
  10. DAC7724数模转换芯片小结
  11. 计算机科学与技术高校毕业生要求,计算机科学与技术 毕业要求(2020)
  12. 用大白话聊聊JavaSE
  13. linu离线安装java1.8
  14. 云呐|常用的固定资产盘点方法有哪些
  15. 关于多径效应,平坦衰落,频率选择性衰落以及瑞利衰落的理解
  16. Android Google地图接入(一)
  17. ShareSDK移动APP社会化分享组件
  18. WPF ComboBox 默认选中无效
  19. 《Windows CE嵌入式开发入门——基于Xscale架构》 第9章 Windows CE BSP及驱动程序结构分析
  20. miniGUI编译详细过程以及示例

热门文章

  1. Spread 16.0 for ASP.NET-WEB-Crack
  2. ios android 同步的备忘录,iOS 备忘录如何共享给好友编辑 / Android 如何实现下拉搜索 | 有轻功 #012...
  3. 企业年会直播方案,看完这份就够了
  4. 互联网晚报 | 8月25日 星期三 | 拼多多年度活跃买家数达8.5亿;钉钉推出首个数字工牌产品“钉工牌”;小度科技完成B轮融资...
  5. 改变未来IT世界的十种编程语言
  6. 把数据库的表结构导出Excel格式
  7. matlab 代码分析 内存溢出,matlab内存溢出的解决方案
  8. baresip 729(G.729)编码实现
  9. windows server2016搭建FTP服务器
  10. Python编程练习(三):21 - 30