模拟退火算法简介:
模拟退火算法在处理全局优化、离散变量优化等困难问题中,具有传统优化算法无可比拟的优势。模拟退火算法的思想最早由Metorpolis等提出的。其出发点是基于物理中固体物质的退火过程与一般的组合优化问题之间的相似性。模拟退火法是一种通用的优化算法,其物理退火过程由以下三部分组成:
1)加温过程:其目的是增强粒子的热运动,使其偏离平衡位置。当温度足够高时,固体将熔为液体,从而消除系统原先存在的非均匀状态。
2)等温过程:对于与周围环境交换热量而温度不变的封闭系统,系统状态的自发变化总是朝自由能减少的方向进行,当自由能达到最小时,系统达到平衡状态。
3)冷却过程:使粒子热运动减弱,系统能量下降,得到晶体结构。其中,加温过程对应算法的设定初温,等温过程对应算法的Metropolis抽样过程,冷却过程对应控制参数的下降。这里能量的变化就是目标函数,我们要得到的最优解就是能量最低态。其中Metropolis准则是SA算法收敛于全局最优解的关键所在,Metropoli、准则以一定的概率接受恶化解,这样就使算法跳离局部最优的陷阱。
SA 算法实现的步骤如下:(下面以最小化问题为例)
(1)初始化:取温度T0足够大,令T = T0,取任意解S1,确定每个T时 的迭代次数,即 Metropolis链长L。
(2)对当前温度T和k=1,2,3,…,L,重复步骤(3)~(6)
(3)对当前解S1随机产生一个扰动得到一个新解 S2.
(4) 计算S2的增量df = f(S2) - f(S1),其中f(S1)为S1的代价函数。
(5)若df < 0,接受S2作为新的当前解,即S1 = S2;否则S2的接受概率为 exp(-df/T),即随机产生(0,1)上的均匀分布的随机数rand,若 exp(-df/T)>rand
,则接受S2作为新的当前解,S1 = S2;否则保留当前解。
(6)如果满足最终的终止条件,Stop,则输出当前解S1作为最优解,结束程序。终止条件Stop通常为:在连续若干个Metropolis链中新解S2都没有被接受时终止算法,或者是设定结束温度。否则按衰减函数衰减T后返回(2)
以上的步骤称之为Metropolis过程。逐步降低控制温度,重复Metropolis过程,直至满足结束准则Stop求出最优解。可以看出SA整体的步骤相比GA以及PSO还是简单了很多了,而且亲测效果还不错,所以属于性价比较高的算法。关键的步骤在第(6)步。
个体构造函数
//构造函数1
public Individual()
{
}
//构造函数2
public Individual(int CityNumber)
{
//如果城市城市数量小于3,则抛出异常
if (CityNumber <= 3)
throw new Exception("错误提示:城市数量必须大于3!");
//设置城市数量
_CityNumber = Param._CityNumber;
//为访问路径赋初始值(-1)
_rout = new int[_CityNumber];
for (int i = 0; i <= _CityNumber - 1; i++)
_rout[i] = -1;
}
参数设置
//设置初始温度
public static double _IniialTemperature = 100000;
//设置降温系数
public static double _CoefficientTemperature = 0.9;
//设置每个温度的最大迭代次数
public static int _MaxIterateNumber = 100;
//城市数量
public static int _CityNumber=30;
//城市坐标数组
public static Point[] _CityCoord;
//两两城市间的距离数组
public static double[,] _CityDist;
//Random随机数类的种子(备注:种子的作用是保证产生真正的随机数)
public static int _Seed = 0;
重点内容核心代码
//类方法:用随机数对城市出现顺序进行初始化
public void InitialCity()
{
//定义第一个解
Individual _firstIndividual = new Individual(Param._CityNumber);
//用随机数打乱该解
_firstIndividual.InitialCityOrder();
//将该解添加入_TempIndividual
_TempIndividual.Add(_firstIndividual);
}
//类方法:产生新解
public string ProNewSolution()
{
//设置随机数(++Param._Seed是为了产生一个真正的随机数)
Random _rand = new Random((++Param._Seed) * DateTime.Now.Millisecond);
//进化日志
string evolveLog = "";
int pos1;
int pos2;
int pos3=0;
do
{
//产生三个随机数
pos1 = _rand.Next(0, Param._CityNumber);
pos2 = _rand.Next(0, Param._CityNumber);
} while (pos1 == pos2 );
if (pos1 > pos2)
{
int temp = pos1;
pos1 = pos2;
pos2 = temp;
}
//获取上一个解
Individual _lastIndividual = new Individual(Param._CityNumber);
_lastIndividual.Clone(_TempIndividual[_TempIndividual.Count - 1]);
//定义一个新解
Individual _newIndividual = new Individual(Param._CityNumber);
//产生一个随机数,选择新解产生方式
int Method = _rand.Next(1,3);
//记录选择的随机方式
evolveLog = evolveLog+"采用第" + Method + "种随机方法,";
if (Method == 1)
{
_newIndividual = NewSolution1(pos1, pos2, _lastIndividual);
evolveLog = evolveLog + "交换位置:" + "(" + pos1.ToString() + "," + pos2.ToString() + ")" + "\r\n";
}
else if (Method == 2)
{
_newIndividual = NewSolution2(pos1, pos2, _lastIndividual);
evolveLog = evolveLog + "逆转位置:" + "(" + pos1.ToString() + "," + pos2.ToString() + ")" + "\r\n";
}
else
{
_newIndividual = NewSolution3(pos1, pos2, _lastIndividual);
evolveLog = evolveLog+"移动位置:" + "(" + pos1.ToString() + "," + pos2.ToString() + "," + ")"+"\r\n";
}
//获取新解的距离
double _newIndividualDist = _newIndividual.CalcFitness();
//获取旧解距离
double _lastIndividualDist = _lastIndividual.CalcFitness();
//计算de
double de = _newIndividualDist - _lastIndividualDist;
evolveLog = evolveLog+"计算的de为:" + de.ToString()+",";
//判断是否接受旧解
if (de < 0)
{
//把新解添加入_TempIndividual链表中
_TempIndividual.Add(_newIndividual);
evolveLog = evolveLog + "de小于0,接受新解。" + "\r\n";
//记录解个数
_solutionNum++;
return evolveLog;
}
else
{
//计算接受概率
double p =Math.Exp(-de /_K);
//添加入概率图_Probability
Coordinate._Probability.Add(p);
//产生随机概率
double pByrandom = _rand.NextDouble();
//添加入随机概率
Coordinate._ProbabilityByRandom.Add(pByrandom);
if(p<pByrandom)
{
//把新解添加如_TempIndividual链表中
_TempIndividual.Add(_newIndividual);
evolveLog = evolveLog + "随机概率大于计算概率,接受新解。" + "\r\n";
//记录解个数
_solutionNum++;
return evolveLog;
}
evolveLog = evolveLog + "随机概率小于计算概率,不接受新解。" + "\r\n";
return evolveLog;
}
}
//新解产生方式1(随机选择两个节点,交换路径中两个节点的顺序)
public Individual NewSolution1(int pos1, int pos2,Individual individual)
{
//声明一个临时个体
Individual _tempIndidual = new Individual(Param._CityNumber);
//从上一个个体中克隆
_tempIndidual.Clone(individual);
int temp =_tempIndidual._rout[pos1];
_tempIndidual._rout[pos1] = _tempIndidual._rout[pos2];
_tempIndidual._rout[pos2] = temp;
return _tempIndidual;
}
//新解产生方式2(随机选择两个节点,将路径中这两个节点间的顺序逆转)
public Individual NewSolution2(int pos1, int pos2, Individual individual)
{
//声明一个临时个体
Individual _tempIndidual = new Individual(Param._CityNumber);
//从上一个个体中克隆
_tempIndidual.Clone(individual);
//定义一个临时数组,保存pos1到pos2之间的城市出现顺序
int[] _tempRout = new int[100];
//将pos1到pos2之间的城市出现顺序存入临时数组
int j = 0;
for (int i = pos2 - 1; i >= pos1; i--)
{
_tempRout[j++] = _tempIndidual._rout[i];
}
j = 0;
for (int i = pos1; i <= pos2 - 1; i++)
{
_tempIndidual._rout[i] = _tempRout[j++];
}
return _tempIndidual;
}
//新解产生方式3(随机选择两个节点pos1,pos2,然后将节点pos1与pos2间的节点移位到最后)
public Individual NewSolution3(int pos1, int pos2,Individual individual)
{
//声明一个临时个体
Individual _tempIndidual = new Individual(Param._CityNumber);
//从上一个个体中克隆
_tempIndidual.Clone(individual);
//定义两个变量,临时储存_rout[pos1]和_rout[pos2]
int temp1 = _tempIndidual._rout[pos1];
int temp2 = _tempIndidual._rout[pos2];
//把pos1后的城市往前移一位
for (int i = pos1; i <= Param._CityNumber - 2; i++)
_tempIndidual._rout[i] = _tempIndidual._rout[i + 1];
//把pos2后的城市往前移一位
for (int i = pos2; i <= Param._CityNumber - 2; i++)
_tempIndidual._rout[i] = _tempIndidual._rout[i + 1];
//插入最后面
_tempIndidual._rout[Param._CityNumber-1] = temp1;
_tempIndidual._rout[Param._CityNumber - 2] = temp2;
return _tempIndidual;
}