转载请注明出处:
模拟退火算法来源于固体退火原理,在热力学上,退火(annealing)现象指物体逐渐降温的物理现象,温度愈低,物体的能量状态会低;够低后,液体开始冷凝与结晶,在结晶状态时,系统的能量状态最低。大自然在缓慢降温(亦即,退火)时,可“找到”最低能量状态:结晶。但是,如果过程过急过快,快速降温(亦称「淬炼」,quenching)时,会导致不是最低能态的非晶形。
如下图所示,首先(左图)物体处于非晶体状态。我们将固体加温至充分高(中图),再让其徐徐冷却,也就退火(右图)。加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小(此时物体以晶体形态呈现)。
1.定义域:x∈D,D为所有状态的空间,x为某一状态
2.目标函数 : f(x);
3.约束方程:g(x)≥0;
4.最优解z=min{f(x)| g(x)≥0, x∈D };
step1:先设定好初始温度t0=最高温度tMax, 随机选定一个初始状态i,计算f(i);
step2:若在当前温度下达到内层循环的退出条件,则转step3执行;否则,从邻域N(i)中随机选择一个状态j, 并计算出exp((f(i) - f(j))/temperature),
若exp((f(i) - f(j))/temperature)>random(0, 1), 则重复执行step2;
step3: 若温度t满足退出条件,则转step2执行;
/*
* J(y):在状态y时的评价函数值
* Y(i):表示当前状态
* Y(i+1):表示新的状态
* r: 用于控制降温的快慢
* T: 系统的温度,系统初始应该要处于一个高温的状态
* 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过大,则搜索到全局最优解的可能会较高,但搜索的过程也就较长。若r过小,则搜索的过程会很快,但最终可能会达到一个局部最优值
*/
i ++ ;
}
模拟退火算法数学模型可以描述为,在给定结构后,模拟退火过程是从一个状态到另一状态的随机游动:
TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
TSP问题是一个组合优化问题。该问题可以被证明具有NPC计算复杂性。TSP问题可以分为两类,一类是对称TSP问题(Symmetric TSP),另一类是非对称问题(Asymmetric TSP)。所有的TSP问题都可以用一个图(Graph)来描述:
V={c1, c2, …, ci, …, cn},i = 1,2, …, n,是所有城市的集合.ci表示第i个城市,n为城市的数目;
E={(r, s): r,s∈ V}是所有城市之间连接的集合;
C = {crs: r,s∈ V}是所有城市之间连接的成本度量(一般为城市之间的距离);
如果crs = csr, 那么该TSP问题为对称的,否则为非对称的。
一个TSP问题可以表达为:
求解遍历图G = (V, E, C),所有的节点一次并且回到起始节点,使得连接这些节点的路径成本最低。
City.java
public class City {
int x;
int y;
// Constructs a randomly placed city
public City(){
this.x = (int)(Math.random()*200);
this.y = (int)(Math.random()*200);
}
// Constructs a city at chosen x, y location
public City(int x, int y){
this.x = x;
this.y = y;
}
// Gets city's x coordinate
public int getX(){
return this.x;
}
// Gets city's y coordinate
public int getY(){
return this.y;
}
// Gets the distance to given city
public double distanceTo(City city){
int xDistance = Math.abs(getX() - city.getX());
int yDistance = Math.abs(getY() - city.getY());
double distance = Math.sqrt( (xDistance*xDistance) + (yDistance*yDistance) );
return distance;
}
@Override
public String toString(){
return getX()+", "+getY();
}
}
Tour.java
public class Tour {
/** Holds our citiesList of cities*/
private ArrayList citiesList ; // Cache
private int distance = 0;
public Tour() {
// TODO Auto-generated constructor stub
citiesList = new ArrayList();
}
/**
* Constructs a citiesList from another citiesList
* */
public Tour(ArrayList tour){
citiesList = new ArrayList();
for (City city : tour) {
this.citiesList.add(city);
}
}
/** Returns citiesList information*/
public ArrayList getCitiesList(){
return citiesList;
}
/** Creates a random individual*/
public Tour generateIndividual() {
// Loop through all our destination cities and add them to our citiesList
for (int cityIndex = 0; cityIndex < citiesList.size(); cityIndex++) {
setCity(cityIndex, this.getCity(cityIndex));
}
// Randomly reorder the citiesList
Collections.shuffle(citiesList);
return this;
}
/**Create new neighbour tour*/
public Tour generateNeighourTour(){
Tour newSolution = new Tour(this.citiesList);
// Get a random positions in the tour
int tourPos1 = (int) (newSolution.numberOfCities() * Math
.random());
int tourPos2 = (int) (newSolution.numberOfCities() * Math
.random());
// Get the cities at selected positions in the tour
City citySwap1 = newSolution.getCity(tourPos1);
City citySwap2 = newSolution.getCity(tourPos2);
// Swap them
newSolution.setCity(tourPos2, citySwap1);
newSolution.setCity(tourPos1, citySwap2);
return newSolution;
}
/** Gets a city from the citiesList*/
public City getCity(int tourPosition) {
return (City)citiesList.get(tourPosition);
}
/** Sets a city in a certain position within a citiesList*/
public void setCity(int tourPosition, City city) {
citiesList.set(tourPosition, city);
// If the tours been altered we need to reset the fitness and distance
distance = 0;
}
public Tour addCity(City city) {
citiesList.add(city);
return this;
}
public ArrayList getAllCities() {
return citiesList;
}
/** Gets the total distance of the citiesList*/
public int getDistance(){
if (distance == 0) {
int tourDistance = 0;
// Loop through our citiesList's cities
for (int cityIndex=0; cityIndex < numberOfCities(); cityIndex++) {
// Get city we're traveling from
City fromCity = getCity(cityIndex);
// City we're traveling to
City destinationCity;
// Check we're not on our citiesList's last city, if we are set our
// citiesList's final destination city to our starting city
if(cityIndex+1 < numberOfCities()){
destinationCity = getCity(cityIndex+1);
}
else{
destinationCity = getCity(0);
}
// Get the distance between the two cities
tourDistance += fromCity.distanceTo(destinationCity);
}
distance = tourDistance;
}
return distance;
}
/** Get number of cities on our citiesList*/
public int numberOfCities() {
return citiesList.size();
}
@Override
public String toString() {
String geneString = "|";
for (int i = 0; i < numberOfCities(); i++) {
geneString += getCity(i)+"|";
}
return geneString;
}
}
simulated annealing.java
public class SimulatedAnnealing {
/**Set initial temp*/
private double currentTemperature = 5000;
/**minimal temperature to cool*/
private double minTemperature = 0.0001;
private double internalLoop = 1000;
/**Cooling rate*/
private double coolingRate = 0.001;
/** Initialize intial solution*/
private Tour currentSolution ;
/**
* set a random initial tour
*
* */
public void initTour() {
Tour tour = new Tour();
tour.addCity(new City(60, 200))
.addCity(new City(180, 200))
.addCity(new City(80, 180))
.addCity(new City(140, 180))
.addCity(new City(20, 160))
.addCity(new City(100, 160))
.addCity(new City(200, 160))
.addCity(new City(140, 140))
.addCity(new City(40, 120))
.addCity(new City(100, 120))
.addCity(new City(180, 100))
.addCity(new City(60, 80))
.addCity(new City(120, 80))
.addCity(new City(180, 60))
.addCity(new City(20, 40))
.addCity(new City(100, 40))
.addCity(new City(200, 40))
.addCity(new City(20, 20))
.addCity(new City(60, 20))
.addCity(new City(160, 20));
currentSolution = tour.generateIndividual();
System.out.println("Initial solution distance: " + currentSolution.getDistance());
}
/**
* iterate for getting the best Tour
* @return best tour
* */
public Tour anneal() {
DynamicDataWindow ddWindow=new DynamicDataWindow("模拟退火算法收敛过程");
ddWindow.setY_Coordinate_Name("所有路径和 ");
ddWindow.setVisible(true);
long tp=0;
Tour bestSolution = new Tour(currentSolution.getCitiesList());
Tour newSolution = null;
// Loop until system has cooled
while (currentTemperature > minTemperature) {
for (int i = 0; i < internalLoop; i++) {
//get a solution from neighbour
newSolution=currentSolution.generateNeighourTour();
// Get energy of solutions
int currentEnergy = currentSolution.getDistance();
int neighbourEnergy = newSolution.getDistance();
// Decide if we should accept the neighbour
if (acceptanceProbability(currentEnergy, neighbourEnergy,
currentTemperature) > Math.random()) {
currentSolution = new Tour(newSolution.getCitiesList());
}
// Keep track of the best solution found
if (currentSolution.getDistance() < bestSolution.getDistance()) {
bestSolution = new Tour(currentSolution.getCitiesList());
}
}
// Cool system
currentTemperature *= 1-coolingRate;
long millis=System.currentTimeMillis();
if (millis-tp>300) {
tp=millis;
ddWindow.addData(millis, bestSolution.getDistance());
}
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return bestSolution;
}
/**
*
* Calculate the acceptance probability
**/
private double acceptanceProbability(int energy, int newEnergy, double temperature) {
// If the new solution is better, accept it
if (newEnergy < energy) {
return 1.0;
}
// If the new solution is worse, calculate an acceptance probability
return Math.exp((energy - newEnergy) / temperature);
}
public static void main(String[] args) {
SimulatedAnnealing sa=new SimulatedAnnealing();
sa.initTour();
Tour besTour = sa.anneal();
System.out.println("Final solution distance: " +besTour.getDistance());
System.out.println("Tour: " + besTour);
}
}
运行截图冷却速率(即温度下降的变化率)设置为0.001时,路径大小收敛过程如下图:
>
>
运行截图冷却速率(即温度下降的变化率)设置为0.01时,路径大小收敛过程如下图:
>
由上图对比可以发现,冷却速率越大,路径大小收敛越快。但是,也不是说冷却速率越大越好,冷却速率越大越容易陷入局部最优解。使用中,冷却速率和内循环次数应综合考虑。
模拟退火算法其特点是在开始搜索阶段解的质量提高比较缓慢,但是到了迭代后期,它的解的质量提高明显,所以如果在求解过程中,对迭代步数限制比较严格的话,模拟退火算法在有限的迭代步数内很难得到高质量的解。总体而言模拟退火算法比较适合用于有充足计算资源的问题求解。
附上本文的全部源码:项目源码
1.退火概念参考:http://www.cnblogs.com/ranjiewen/p/6084052.html
2.TSP问题参考:http://blog.csdn.net/wangqiuyun/article/details/8918523
3.TSP解法改编自:http://www.theprojectspot.com/tutorial-post/simulated-annealing-algorithm-for-beginners/6
4.伪代码参考自:http://blog.csdn.net/dearrita/article/details/52236807