模拟退火也叫做蒙特卡罗退火、统计冷却、概率爬山、随机松弛和概率交换算法,它来自于对热力学过程的模拟。为了生成规则晶体,先将原材料加热到熔化状态,再将晶体熔融液徐徐降温,使之凝固成晶体结构。在冷却过程中,如果降温太快,则会带来一些不良后果,特别是导致所形成的晶体不够规则,并且能量远远高于一个完美结构的晶体。物理系统和优化问题之间有明显的相似点,例如,物理系统中的能量就相当于优化问题的评估函数;快速淬火就相当于局部搜索;徐徐退火就相当于模拟退火的过程;温度就相当于模拟退火的控制参数T(后面有详细内容)等等。
在搜索过程中如果过早结束,就会陷入局部最优的情况,为了跳出局部最优,引入一个接受概率P和参数T。在当前解的邻域内选择一点,如果比当前解好,则总是接受它;如果没有当前解好则以接受概率接受它。注意,接受概率中的T是随着时间从大到小变化的(冷却温度),一开始T值很大,近似于随机搜索(随机选择当前解);后来T很小,近似于普通搜索法(选择最优作为当前解)。其中接受概率的定义为:
这是针对极大化问题的,如果是极小化问题则只需将指数的分子的两个变量交换位置。
由公式可以看出,当T很大时,则两个点的质量竞争的重要性就很小,对结果影响不大,这是概率接近于0.5,等同于随机选取;当T很小(T=1)时,概率接近于1,这就退化为普通局部搜索法了(每次都选择较优解作为当前解)。因此,T的值选择依据问题而定,既不能太大,也不能太小。另外,当T值固定时,如果新解与当前解质量相当,那么p为0.5;如果新解好于当前解,那么p大于0.5,而且新解越好,接受的概率就越大。这些事实都是符合常理的。
下面以TSP问题为例,给出模拟退火算法的一种实现(java实现):import java.util.ArrayList;
import java.util.Random;
import tools.TSPData;
import interfaces.IRunning;
/*
* 使用模拟退火方法求解TSP问题
*/
public class SASolver implements IRunning{
//当前解
private ArrayList current;
//邻域内下一个解
private ArrayList next;
//城市数量
private int number;
//开始的城市标号
private int origin;
//城市信息文件名
private String file;
//近似最优解
private double shortestDist;
//距离矩阵
private double[][] distances;
public SASolver(String filename)
{
file = filename;
shortestDist = 0;
current = new ArrayList();
next = new ArrayList();
}
@Override
public void run() {
//搜索迭代次数
int iteration = number * 2;
//退火温度
double T = 10000.0;
//退火速率
double cRate = 0.9999;
//凝固温度
double ST = 0.00001;
//随机数发生器
Random rand = new Random(System.nanoTime());
initialSolution();
double dist = getTotalDist(current);
shortestDist = dist;
origin = 0;
while(T > ST)
{
int count = iteration;
double nextDist;
dist = shortestDist;
while(count > 0)
{
getNextSolution();
nextDist = getTotalDist(next);
if (nextDist < dist) {
current = (ArrayList) next.clone();
shortestDist = nextDist;
} else {
double r = rand.nextDouble();
if (r < Math.exp((dist - nextDist) / T)) {
current = (ArrayList) next.clone();
shortestDist = nextDist;
}
}
count--;
}
T *= cRate;
}
//shortestDist = dist;
System.out.println("算法结束");
}
/*
* 总是根据当前解生成邻域新解
*/
private void getNextSolution() {
next = (ArrayList)current.clone();
Random rand = new Random(System.nanoTime());
//这里的算子:随机交换两个城市的位置(0作为起始点除外)
int index1 = rand.nextInt(number - 1) + 1;
int index2 = index1;
while(index2 == index1)
index2 = rand.nextInt(number - 1) + 1;
int temp = next.get(index1);
next.set(index1, next.get(index2));
next.set(index2, temp);
}
private void initialSolution() {
int i;
for(i = 0; i < number; i++)
{
if(i != origin)
current.add(i);
}
current.add(0, origin);
}
private double getTotalDist(ArrayList list) {
double res = 0;
int i = 0;
for(; i < number - 1; i++)
res += distances[list.get(i)][list.get(i + 1)];
if(number > 1)
res += distances[list.get(number - 1)][0];
return res;
}
@Override
public void init() {
distances = TSPData.getDistances(file);
number = distances[0].length;
}
@Override
public void print() {
System.out.println("算法得到的近似最优解为:" + shortestDist);
System.out.println("该解对应的路径为:");
int i;
for(i = 0; i < number; i++)
System.out.print(current.get(i) + "-->");
System.out.println(current.get(0));
}
}
这里使用的算子是随机交换两个城市的位置。 实验得到的解(18左右)与最优解(14)很相近。 (待修订)