一 什么是模拟退火算法?

  • 所谓退火,其实是金属冶炼的一个名词.比如加工一把刀,我们通常是把材料加工到很高的一个温度,加以锤炼.之后慢慢的将温度降下来,如果我们降温的控制比较好的话,那么金属里面的原子就更加偏向于形成能量比较低的状态,如果一个粒子能量很低,他的稳定性会更强,不易受到损坏.刀会更锋利,韧性更足.
  • 当一个函数问题比较复杂的时候,无法通过直接微分求出.因为受到退火的启发,我们就可以通过这个算法求出一个复杂函数的极值问题.

二 模拟退火算法原理(策略)



比如先看第一种情况(粉色),X(n)变化到X(n+1)时,可以是温度(能量)下降的,这个是百分百是允许的,因为我的目的是让他下降的(好像是废话哎…).但这就带来了一个问题,X(n+1)可能会陷入一个局部最优解,不能正确的找到全局最优.

因此,我们必须允许一定的概率产生第二种情况(红色)来跳出局部最优解.这个概率与温度(能量)有很大的关系,温度越高,往上蹦的概率也越大,蹦得高度也越大,这样它有可能跳出局部最优解,从而陷入更深的坑里面去(因此就可能是全局最优啦).因此蹦进小坑可能会出来,但进入大坑再出来的可能性就很小,通过这样迭代,就可以找到全局最优解.

因此,初始温度越高,退火过程越慢,越容易得到全局最优,当然这样花费的时间越长,这也是必须付出的代价.

三 公式



这下来看看公式,当从 X(n) 变化到 X(n+1) 时,能量 E 如果是下降的,让它产生这个概率为1.当从 X(n) 变化到 X(n+1) 时,能量 E 如果是 上升的,这个概率是 exp(-(E(new)-E(old))/T) .因此温度越大 ,概率越高.温度越低,往上蹦的概率越低.

模拟退火一般有两个循环过程来组成:

在外循环里面,每一次迭代温度都会改变。

T(n)= λT(n-1) ,其中 λ 是一个小于1的数,一 般取值在0.8-0.99之间,使得对每一温度,都有足够的次数去尝试.收敛速度比较慢.

在内循环里面,以一定规则在当前状态附近产生新的状态 x(n) 产生 x’(n) ,计算 f(x(n))f(x’(n)) . 得到

Δf=f(x’(n))-f(x(n)) .

如果 Δf<0 , 说明 x’(n) 好于 x(n) ,因此 x(n+1)=x’(n) . 如果 Δf>0, 计算 p , 产生一个随机数 α ,若 p>α .则接受 x’(n) 为 下一个状态值 , x(n+1)=x’(n) . 否则拒绝 x’(n) . x(n+1)=x(n) .

根据内循环终止准则,检查是否达到热平衡。按照公式调整温度,根据外循环终止准则检查退火算法是否收敛。 外循环终止的准则也可以设置为固定的迭代次数,达到该次数以后系统即停止计算。

四 代码(旅行商问题)

//模拟退火
#include<iostream>
#include<string.h>
#include<time.h>
#include<math.h>
#define T_start 100000 //初始温度
#define T_end (1e-9)
#define q 0.95 //退火系数
#define L 1000 //每个温度时的迭代次数
#define N 52//数组的行数
int city_l[N];//用于存放一个解
double city_p[N][2] = { { 565,575 },{ 25,185 },{ 345,750 },{ 945,685 },{ 845,655 },//每个城市的坐标
{ 880,660 },{ 25,230 },{ 525,1000 },{ 580,1175 },{ 650,1130 },{ 1605,620 },
{ 1220,580 },{ 1465,200 },{ 1530,5 },{ 845,680 },{ 725,370 },{ 145,665 },
{ 415,635 },{ 510,875 },{ 560,365 },{ 300,465 },{ 520,585 },{ 480,415 },
{ 835,625 },{ 975,580 },{ 1215,245 },{ 1320,315 },{ 1250,400 },{ 660,180 },
{ 410,250 },{ 420,555 },{ 575,665 },{ 1150,1160 },{ 700,580 },{ 685,595 },
{ 685,610 },{ 770,610 },{ 795,645 },{ 720,635 },{ 760,650 },{ 475,960 },
{ 95,260 },{ 875,920 },{ 700,500 },{ 555,815 },{ 830,485 },{ 1170,65 },
{ 830,610 },{ 605,625 },{ 595,360 },{ 1340,725 },{ 1740,245 } };
//计算两座城市距离
double distance(double* city1, double* city2) {
	double x1 = *city1;
	double y1 = *(city1 + 1);
	double x2 = *city2;
	double y2 = *(city2 + 1);
	double dis = sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
	return dis;
}
//计算从第一个到最后一个的路径长度
double path_len(int* arr) {
	double path = 0;
	int index = *arr;//定位至第一个城市
	for (int i = 0; i < N - 1; i++) {
		int index1 = *(arr + i); int index2 = *(arr + i + 1);
		double dis = distance(city_p[index1 - 1], city_p[index2 - 1]);
		path += dis;
	}
	int last_index = *(arr + N - 1);//最后一个城市序号
	int first_index = *arr;//第一个城市序号
	double last_dis = distance(city_p[last_index - 1], city_p[first_index - 1]);
	path += last_dis;
	return path;
} //初始化函数
void init() {
	for (int i = 0; i < N; i++)
		city_l[i] = i + 1;//初始化一个解
}
//产生一个新解,采用随机交叉两个位置的方式产生新的解
void create_new() {
	double r1 = ((double)rand()) /(double)(RAND_MAX + 1.0);
	double r2 = ((double)rand()) / (double)(RAND_MAX + 1.0);
	int pos1 = (int)(N*r1);
	int pos2 = (int)(N*r2);
	int temp = city_l[pos1];
	city_l[pos1] = city_l[pos2];
	city_l[pos2] = temp;
}
int main() {
	srand((unsigned)time(NULL));//初始化随机数种子
	time_t start, finish;//计算算法持续时间
	start = clock();
	double T = T_start;
	int count = 0;//记录降温次数
	init();//随便初始化一个值
	int city_l_copy[N];//用于保存原始解
	double f1, f2, df;
	double r;
	while (T > T_end) {//温度低于结束温度时,退火结束
		for (int i = 0; i < L; i++) //迭代L次
		{
			//复制数组
			memcpy(city_l_copy, city_l, N * sizeof(int));
			create_new();//产生新解
			f1 = path_len(city_l_copy);
			f2 = path_len(city_l);
			df = f2 - f1;
			/*
			如果df<0,那么结果取最小的就是city_1,不做任何。
			如果df>=0,那么
			*/

		 if (df <= 0) {
				r = ((double)rand()) / (RAND_MAX);
				if (exp(-df / T) >=r) {//保留原解
					memcpy(city_l, city_l_copy, N * sizeof(int));
				}
			}
		}
		T *= q;//降温0
		count++;
	}
	finish = clock();
	double duration = ((double)(finish - start)) / CLOCKS_PER_SEC;//持续时间
	double len = path_len(city_l);
	printf("初始温度 T=%d,降温系数 q=%.2f,每个温度迭代%d 次\n", T_start, q, L, count);
	printf("最优路径长度为:%lf\n程序耗时: %lf\n最优路径为:\n", len, duration);
	for (int i = 0; i < N - 1; i++) {
		printf("%d->", city_l[i]);
	}
	printf("%d\n", city_l[N - 1]);
	return 0;
}

五 应用

本算法多应用在神经网络的研究中,其他类似的算法包括蚁群算法,遗传算法等将在以后博客里再讲述.

算法:模拟退火(基于c++程序)的更多相关文章

  1. 最小生成树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind

    最小支撑树树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind 最小支撑树树 前几节中介绍的算法都是针对无权图的,本节将介绍带权图的最小 ...

  2. [转载] 使用C/C++语言编写基于DSP程序的注意事项

    原文地址:『转』使用C/C++语言编写基于DSP程序的注意事项作者:skysmile   1.不影响执行速度的情况下,可以使用c或c/c++语言提供的函数库,也可以自己设计函数,这样更易于使用“裁缝师 ...

  3. kmeans算法并行化的mpi程序

    用c语言写了kmeans算法的串行程序,再用mpi来写并行版的,貌似参照着串行版来写并行版,效果不是很赏心悦目~ 并行化思路: 使用主从模式.由一个节点充当主节点负责数据的划分与分配,其他节点完成本地 ...

  4. Breaseman算法绘制圆形|中点算法绘制圆形_程序片段

    Breaseman算法绘制圆形|中点算法绘制圆形_程序片段 1. Breaseman算法绘制圆形程序 由于算法的特殊性,限制绘制第一象限部分,其他部分通过旋转绘制. void CCGProjectWo ...

  5. 2维FFT算法实现——基于GPU的基2快速二维傅里叶变换

    上篇讲述了一维FFT的GPU实现(FFT算法实现——基于GPU的基2快速傅里叶变换),后来我又由于需要做了一下二维FFT,大概思路如下. 首先看的肯定是公式: 如上面公式所描述的,2维FFT只需要拆分 ...

  6. Net Core基于TopShelf程序运行于服务模式

    目录 Net Core基于TopShelf程序运行于服务模式 1 背景 2 优势 2.1 服务模式可设置重启条件 2.2 避免误操作 3.使用 3.1 GUI方式安装Topshelf包 4 配置 5 ...

  7. Canny边缘检测算法(基于OpenCV的Java实现)

    目录 Canny边缘检测算法(基于OpenCV的Java实现) 绪论 Canny边缘检测算法的发展历史 Canny边缘检测算法的处理流程 用高斯滤波器平滑图像 彩色RGB图像转换为灰度图像 一维,二维 ...

  8. 基于小程序请求接口 wx.request 封装的类 axios 请求

    基于小程序请求接口 wx.request 封装的类 axios 请求 Introduction wx.request 的配置.axios 的调用方式 源码戳我 feature 支持 wx.reques ...

  9. 基于小程序云Serverless开发微信小程序

    本文主要以使用小程序云Serverless服务开发一个记事本微信小程序为例介绍如何使用小程序云Serverless开发微信小程序.记事本小程序的开发涉及到云函数调用.云数据库存储.图片存储等功能,较好 ...

  10. canvas菜鸟基于小程序实现图案在线定制功能

    前言 最近收到一个这样的需求,要求做一个基于 vue 和 element-ui 的通用后台框架页,具体要求如下: 要求通用性高,需要在后期四十多个子项目中使用,所以大部分地方都做成可配置的. 要求做成 ...

随机推荐

  1. AI学习笔记:人工智能与机器学习概述

    一.人工智能基本概念 1.1 基本概念 数据分析:对历史规律的展现.对未来数据的预测. 机器学习:机器学习是指从一系列的原始数据中找到规律,提取人们可以识别的特征,然后通过学习这些特征,最终产生一个模 ...

  2. IdentityServer4源码解析_1_项目结构

    目录 IdentityServer4源码解析_1_项目结构 IdentityServer4源码解析_2_元数据接口 IdentityServer4源码解析_3_认证接口 IdentityServer4 ...

  3. 曹工说Spring Boot源码(25)-- Spring注解扫描的瑞士军刀,ASM + Java Instrumentation,顺便提提Jar包破解

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  4. udp和tcp特点 实现文件上传

    本周课程安排: 网络编程结束 并发网络开头 进程 线程 IO模型 上周内容回顾: 1.osi七层:应用层,表示层,会话层,传输层,网络层,数据链路层,物理连接层 也有人把他们归纳为五层: 应用层, 传 ...

  5. POJ1523 Tarjan求割点以及删除割点之后强连通分量的数量

    题目链接:http://poj.org/problem?id=1523 SPF:A Single Point of Failure也就是割点(一个点导致网络之间的不连通),由于给出的图是无向图,所以只 ...

  6. MySQL----DML(增删改表中数据)

    ##DML:增删改表中的数据 1.添加数据 *语法: *  insert into 表名(列名1,列名2,...列名n) values (值1,值2,...值n); *注意: 1.列名和值要一一对应. ...

  7. Codeforces Round #567 (Div. 2) B. Split a Number

    Split a Number time limit per test 2 seconds memory limit per test 512 megabytes input standard inpu ...

  8. 微信内置浏览器对于html5的支持

    微信内置浏览器对于html5的支持 来源: 作者: 热度:102 日期:14-06-10, 09:10 AM 我在做针对微信的HTML5应用, 目前遇到的几个问题是 一. 安卓版微信直接调用系统浏览器 ...

  9. effective-java学习笔记---静态工厂方法替代构造方法

    使用静态方法的优点: 1.它们是有名字的,生成的客户端代码更易阅读. 如:返回素数的静态方法 BigInteger.probablePrime 2.与构造方法不同,它们不需要每次调用时都创建一个对象. ...

  10. OpenCV-Python 理解SVM | 五十五

    目标 在这一章中 我们将对SVM有一个直观的了解 理论 线性可分数据 考虑下面的图像,它具有两种数据类型,红色和蓝色.在kNN中,对于测试数据,我们用来测量其与所有训练样本的距离,并以最小的距离作为样 ...