转载请注明出处:
模拟退火算法来源于固体退火原理,在热力学上,退火(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执行;
模拟退火算法数学模型可以描述为,在给定结构后,模拟退火过程是从一个状态到另一状态的随机游动:
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<City> citiesList ; // Cache private int distance = 0; public Tour() { // TODO Auto-generated constructor stub citiesList = new ArrayList<City>(); } /** * Constructs a citiesList from another citiesList * */ public Tour(ArrayList<City> tour){ citiesList = new ArrayList<City>(); for (City city : tour) { this.citiesList.add(city); } } /** Returns citiesList information*/ public ArrayList<City> 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<City> 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
