模擬退火算法從原理到實戰【基礎篇】


  模擬退火算法來源於固體退火原理,將固體加溫至充分高,再讓其徐徐冷卻,加溫時,固體內部粒子隨溫升變為無序狀,內能增大,而徐徐冷卻時粒子漸趨有序,在每個溫度都達到平衡態,最后在常溫時達到基態,內能減為最小。根據Metropolis准則,粒子在溫度T時趨於平衡的概率為e-ΔE/(kT),其中E為溫度T時的內能,ΔE為其改變量,k為Boltzmann常數。用固體退火模擬組合優化問題,將內能E模擬為目標函數值f,溫度T演化成控制參數t,即得到解組合優化問題的模擬退火算法:由初始解i和控制參數初值t開始,對當前解重復“產生新解→計算目標函數差→接受或舍棄”的迭代,並逐步衰減t值,算法終止時的當前解即為所得近似最優解,這是基於蒙特卡羅迭代求解法的一種啟發式隨機搜索過程。退火過程由冷卻進度表(Cooling Schedule)控制,包括控制參數的初值t及其衰減因子Δt、每個t值時的迭代次數L和停止條件S。

模擬退火算法的模型   

模擬退火算法可以分解為解空間、目標函數和初始解三部分。  

模擬退火的基本思想:   

(1) 初始化:初始溫度T(充分大),初始解狀態S(是算法迭代的起點), 每個T值的迭代次數L   

(2) 對k=1,……,L做第(3)至第6步:   

(3) 產生新解S′   

(4) 計算增量Δt′=C(S′)-C(S),其中C(S)為評價函數   

(5) 若Δt′<0則接受S′作為新的當前解,否則以概率exp(-Δt′/T)接受S′作為新的當前解.   

(6) 如果滿足終止條件則輸出當前解作為最優解,結束程序。 終止條件通常取為連續若干個新解都沒有被接受時終止算法。   

(7) T逐漸減少,且T->0,然后轉第2步。 

模擬退火的算法流程圖如下:

 

 

模擬退火算法新解的產生和接受可分為如下四個步驟:   

第一步是由一個產生函數從當前解產生一個位於解空間的新解;為便於后續的計算和接受,減少算法耗時,通常選擇由當前新解經過簡單地變換即可產生新解的方法,如對構成新解的全部或部分元素進行置換、互換等,注意到產生新解的變換方法決定了當前新解的鄰域結構,因而對冷卻進度表的選取有一定的影響。   

第二步是計算與新解所對應的目標函數差。因為目標函數差僅由變換部分產生,所以目標函數差的計算最好按增量計算。事實表明,對大多數應用而言,這是計算目標函數差的最快方法。   

第三步是判斷新解是否被接受,判斷的依據是一個接受准則,最常用的接受准則是Metropo1is准則: 若Δt′<0則接受S′作為新的當前解S,否則以概率exp(-Δt′/T)接受S′作為新的當前解S。   

第四步是當新解被確定接受時,用新解代替當前解,這只需將當前解中對應於產生新解時的變換部分予以實現,同時修正目標函數值即可。此時,當前解實現了一次迭代。可在此基礎上開始下一輪試驗。而當新解被判定為舍棄時,則在原當前解的基礎上繼續下一輪試驗。   模擬退火算法與初始值無關,算法求得的解與初始解狀態S(是算法迭代的起點)無關;模擬退火算法具有漸近收斂性,已在理論上被證明是一種以概率l 收斂於全局最優解的全局優化算法;模擬退火算法具有並行性

如果你對退火的物理意義還是暈暈的,沒關系我們還有更為簡單的理解方式。想象一下如果我們現在有下面這樣一個函數,現在想求函數的(全局)最優解。如果采用Greedy策略,那么從A點開始試探,如果函數值繼續減少,那么試探過程就會繼續。而當到達點B時,顯然我們的探求過程就結束了(因為無論朝哪個方向努力,結果只會越來越大)。最終我們只能找打一個局部最后解B。

 

模擬退火其實也是一種Greedy算法,但是它的搜索過程引入了隨機因素。模擬退火算法以一定的概率來接受一個比當前解要差的解,因此有可能會跳出這個局部的最優解,達到全局的最優解。以上圖為例,模擬退火算法在搜索到局部最優解B后,會以一定的概率接受向右繼續移動。也許經過幾次這樣的不是局部最優的移動后會到達B 和C之間的峰點,於是就跳出了局部最小值B。

根據Metropolis准則,粒子在溫度T時趨於平衡的概率為exp(-ΔE/(kT)),其中E為溫度T時的內能,ΔE為其改變數,k為Boltzmann常數。Metropolis准則常表示為

 


Metropolis准則表明,在溫度為T時,出現能量差為dE的降溫的概率為P(dE),表示為:P(dE) = exp( dE/(kT) )。其中k是一個常數,exp表示自然指數,且dE<0。所以P和T正相關。這條公式就表示:溫度越高,出現一次能量差為dE的降溫的概率就越大;溫度越低,則出現降溫的概率就越小。又由於dE總是小於0(因為退火的過程是溫度逐漸下降的過程),因此dE/kT < 0 ,所以P(dE)的函數取值范圍是(0,1) 。隨着溫度T的降低,P(dE)會逐漸降低。
我們將一次向較差解的移動看做一次溫度跳變過程,我們以概率P(dE)來接受這樣的移動。也就是說,在用固體退火模擬組合優化問題,將內能E模擬為目標函數值 f,溫度T演化成控制參數 t,即得到解組合優化問題的模擬退火演算法:由初始解 i 和控制參數初值 t 開始,對當前解重復“產生新解→計算目標函數差→接受或丟棄”的迭代,並逐步衰減 t 值,算法終止時的當前解即為所得近似最優解,這是基於蒙特卡羅迭代求解法的一種啟發式隨機搜索過程。退火過程由冷卻進度表(Cooling Schedule)控制,包括控制參數的初值 t 及其衰減因子Δt 、每個 t 值時的迭代次數L和停止條件S。

總結起來就是:

  • f( Y(i+1) ) <= f( Y(i) )  (即移動后得到更優解),則總是接受該移動;
  • f( Y(i+1) ) > f( Y(i) )  (即移動后的解比當前解要差),則以一定的概率接受移動,而且這個概率隨着時間推移逐漸降低(逐漸降低才能趨向穩定)相當於上圖中,從B移向BC之間的小波峰時,每次右移(即接受一個更糟糕值)的概率在逐漸降低。如果這個坡特別長,那么很有可能最終我們並不會翻過這個坡。如果它不太長,這很有可能會翻過它,這取決於衰減 t 值的設定。

關於普通Greedy算法與模擬退火,有一個有趣的比喻:

    • 普通Greedy算法:兔子朝着比現在低的地方跳去。它找到了不遠處的最低的山谷。但是這座山谷不一定最低的。這就是普通Greedy算法,它不能保證局部最優值就是全局最優值。
    • 模擬退火:兔子喝醉了。它隨機地跳了很長時間。這期間,它可能走向低處,也可能踏入平地。但是,它漸漸清醒了並朝最低的方向跳去。這就是模擬退火。

模擬退火算法的簡單應用   

作為模擬退火算法應用,討論貨郎擔問題(Travelling Salesman Problem,簡記為TSP):設有n個城市,用數碼1,…,n代表。城市i和城市j之間的距離為d(i,j) i, j=1,…,n.TSP問題是要找遍訪每個域市恰好一次的一條回路,且其路徑總長度為最短.。   

求解TSP的模擬退火算法模型可描述如下:   

解空間 解空間S是遍訪每個城市恰好一次的所有回路,是{1,……,n}的所有循環排列的集合,S中的成員記為(w1,w2 ,……,wn),並記wn+1= w1。初始解可選為(1,……,n)   

目標函數 此時的目標函數即為訪問所有城市的路徑總長度或稱為代價函數:   

我們要求此代價函數的最小值。   

新解的產生 隨機產生1和n之間的兩相異數k和m,若k<m,則將   

(w1, w2 ,…,wk , wk+1 ,…,wm ,…,wn)   

變為:

(w1, w2 ,…,wm , wm-1 ,…,wk+1 , wk ,…,wn).   

如果是k>m,則將   

(w1, w2 ,…,wk , wk+1 ,…,wm ,…,wn)   

變為:   

(wm, wm-1 ,…,w1 , wm+1 ,…,wk-1 ,wn , wn-1 ,…,wk).   

上述變換方法可簡單說成是“逆轉中間或者逆轉兩端”。   

也可以采用其他的變換方法,有些變換有獨特的優越性,有時也將它們交替使用,得到一種更好方法。   

代價函數差 設將(w1, w2 ,……,wn)變換為(u1, u2 ,……,un), 則代價函數差為:

根據上述分析,可寫出用模擬退火算法求解TSP問題的偽程序:

Procedure TSPSA:
               begin 
                init-of-T; { T為初始溫度}
                S={1,……,n}; {S為初始值}
                termination=false;
                while termination=false
                 begin 
                  for i=1 to L do
                    begin
                      generate(S′form S); { 從當前回路S產生新回路S′}
                      Δt:=f(S′))-f(S);{f(S)為路徑總長}
                      IF(Δt<0) OR (EXP(-Δt/T)>Random-of-[0,1])
                      S=S′;
                      IF the-halt-condition-is-TRUE THEN 
                      termination=true;
                    End;
                  T_lower;
                 End;
               End

下面給出C++實現參考源碼:

  1 /*
  2 模擬退火算法解決TSP問題
  3 輸入格式(tsp.in):
  4 第1行:1個整數N,表示城市的數量
  5 第2..N+1行:每行有2個空格分開的整數x,y,第i+1行的x,y表示城市i的坐標
  6 */
  7 #include <iostream>
  8 #include <string.h>
  9 #include <stdlib.h>
 10 #include <algorithm>
 11 #include <stdio.h>
 12 #include <time.h>
 13 #include <math.h>
 14 
 15 #define N     30      //城市數量
 16 #define T     3000    //初始溫度
 17 #define EPS   1e-8    //終止溫度
 18 #define DELTA 0.98    //溫度衰減率
 19 
 20 #define LIMIT 1000   //概率選擇上限
 21 #define OLOOP 20    //外循環次數
 22 #define ILOOP 100   //內循環次數
 23 
 24 using namespace std;
 25 
 26 //定義路線結構體
 27 struct Path
 28 {
 29     int citys[N];
 30     double len;
 31 };
 32 
 33 //定義城市點坐標
 34 struct Point
 35 {
 36     double x, y;
 37 };
 38 
 39 Path bestPath;        //記錄最優路徑
 40 Point p[N];       //每個城市的坐標
 41 double w[N][N];   //兩兩城市之間路徑長度
 42 int nCase;        //測試次數
 43 
 44 double dist(Point A, Point B)
 45 {
 46     return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
 47 }
 48 
 49 void GetDist(Point p[], int n)
 50 {
 51     for(int i = 0; i < n; i++)
 52         for(int j = i + 1; j < n; j++)
 53             w[i][j] = w[j][i] = dist(p[i], p[j]);
 54 }
 55 
 56 void Input(Point p[], int &n)
 57 {
 58     scanf("%d", &n);
 59     for(int i = 0; i < n; i++)
 60         scanf("%lf %lf", &p[i].x, &p[i].y);
 61 }
 62 
 63 void Init(int n)
 64 {
 65     nCase = 0;
 66     bestPath.len = 0;
 67     for(int i = 0; i < n; i++)
 68     {
 69         bestPath.citys[i] = i;
 70         if(i != n - 1)
 71         {
 72             printf("%d--->", i);
 73             bestPath.len += w[i][i + 1];
 74         }
 75         else
 76             printf("%d\n", i);
 77     }
 78     printf("\nInit path length is : %.3lf\n", bestPath.len);
 79     printf("-----------------------------------\n\n");
 80 }
 81 
 82 void Print(Path t, int n)
 83 {
 84     printf("Path is : ");
 85     for(int i = 0; i < n; i++)
 86     {
 87         if(i != n - 1)
 88             printf("%d-->", t.citys[i]);
 89         else
 90             printf("%d\n", t.citys[i]);
 91     }
 92     printf("\nThe path length is : %.3lf\n", t.len);
 93     printf("-----------------------------------\n\n");
 94 }
 95 
 96 Path GetNext(Path p, int n)
 97 {
 98     Path ans = p;
 99     int x = (int)(n * (rand() / (RAND_MAX + 1.0)));
100     int y = (int)(n * (rand() / (RAND_MAX + 1.0)));
101     while(x == y)
102     {
103         x = (int)(n * (rand() / (RAND_MAX + 1.0)));
104         y = (int)(n * (rand() / (RAND_MAX + 1.0)));
105     }
106     swap(ans.citys[x], ans.citys[y]);
107     ans.len = 0;
108     for(int i = 0; i < n - 1; i++)
109         ans.len += w[ans.citys[i]][ans.citys[i + 1]];
110     cout << "nCase = " << nCase << endl;
111     Print(ans, n);
112     nCase++;
113     return ans;
114 }
115 
116 void SA(int n)
117 {
118     double t = T;
119     srand((unsigned)(time(NULL)));
120     Path curPath = bestPath;
121     Path newPath = bestPath;
122     int P_L = 0;
123     int P_F = 0;
124     while(1)       //外循環,主要更新參數t,模擬退火過程
125     {
126         for(int i = 0; i < ILOOP; i++)    //內循環,尋找在一定溫度下的最優值
127         {
128             newPath = GetNext(curPath, n);
129             double dE = newPath.len - curPath.len;
130             if(dE < 0)   //如果找到更優值,直接更新
131             {
132                 curPath = newPath;
133                 P_L = 0;
134                 P_F = 0;
135             }
136             else
137             {
138                 double rd = rand() / (RAND_MAX + 1.0);
139                 //如果找到比當前更差的解,以一定概率接受該解,並且這個概率會越來越小
140                 if(exp(dE / t) > rd && exp(dE / t) < 1)
141                     curPath = newPath;
142                 P_L++;
143             }
144             if(P_L > LIMIT)
145             {
146                 P_F++;
147                 break;
148             }
149         }
150         if(curPath.len < bestPath.len)
151             bestPath = curPath;
152         if(P_F > OLOOP || t < EPS)
153             break;
154         t *= DELTA;
155     }
156 }
157 
158 int main(int argc, const char * argv[]) {
159 
160     freopen("TSP.data", "r", stdin);
161     int n;
162     Input(p, n);
163     GetDist(p, n);
164     Init(n);
165     SA(n);
166     Print(bestPath, n);
167     printf("Total test times is : %d\n", nCase);
168     return 0;
169 }

TSP.data的數據格式如下,第一行的數字表示一個有多少座城市,第2至最后一行,每行有兩個數字表示,城市的坐標(平面直角坐標系)。例如: 

20 80 
16 84 
23 66 
62 90 
11 9 
35 28 

注意由於是基於蒙特卡洛的方法,所以上面代碼每次得出的結果並不完全一致。你可以通過增加迭代的次數來獲得一個更優的結果。

我們這里需要說明的是,在之前的文章里,我們用求最小值的例子來解釋模擬退火的執行:如果新一輪的計算結果更前一輪之結果更小,那么我們就接受它,否則就以一個概率來拒絕或接受它,而這個拒絕的概率會隨着溫度的降低(也即是迭代次數的增加)而變大(也就是接受的概率會越來越小)。

但現在我們面對一個TSP問題,我們如何定義或者說如何獲取下一輪將要被考察的哈密爾頓路徑呢?在一元函數最小值的例子中,下一輪就是指向左或者向右移動一小段距離。而在TSP問題中,我們可以采用的方式其實是很多的。上面代碼中GetNext()函數所采用的方式是隨機交換兩個城市在路徑中的順序。例如當前路徑為 A->B->C->D->A,那么下一次路徑就可能是A->D->C->B->A,即交換B和D。

 1 public class Tour{
 2     ... ...
 3     // Creates a random individual
 4     public void generateIndividual() {
 5         // Loop through all our destination cities and add them to our tour
 6         for (int cityIndex = 0; cityIndex < TourManager.numberOfCities(); cityIndex++) {
 7           setCity(cityIndex, TourManager.getCity(cityIndex));
 8         }
 9         // Randomly reorder the tour
10         Collections.shuffle(tour);
11     }
12     ... ...
13 }

可見把上一輪路徑做一個隨機的重排(這顯然也是一種策略)。

我們對上述問題提出一種新的策略:

 

首先,我們需要創建一個城市類,它可以用來為旅行推銷員的不同目的地建模。

 1 /*
 2 * City.java
 3 * Models a city
 4 */
 5 
 6 package sa;
 7 
 8 public class City {
 9     int x;
10     int y;
11     
12     // Constructs a randomly placed city
13     public City(){
14         this.x = (int)(Math.random()*200);
15         this.y = (int)(Math.random()*200);
16     }
17     
18     // Constructs a city at chosen x, y location
19     public City(int x, int y){
20         this.x = x;
21         this.y = y;
22     }
23     
24     // Gets city's x coordinate
25     public int getX(){
26         return this.x;
27     }
28     
29     // Gets city's y coordinate
30     public int getY(){
31         return this.y;
32     }
33     
34     // Gets the distance to given city
35     public double distanceTo(City city){
36         int xDistance = Math.abs(getX() - city.getX());
37         int yDistance = Math.abs(getY() - city.getY());
38         double distance = Math.sqrt( (xDistance*xDistance) + (yDistance*yDistance) );
39         
40         return distance;
41     }
42     
43     @Override
44     public String toString(){
45         return getX()+", "+getY();
46     }
47 }

接下來讓我們創建一個可以跟蹤城市的類:

 1 /*
 2 * TourManager.java
 3 * Holds the cities of a tour
 4 */
 5 
 6 package sa;
 7 
 8 import java.util.ArrayList;
 9 
10 public class TourManager {
11 
12     // Holds our cities
13     private static ArrayList destinationCities = new ArrayList<City>();
14 
15     // Adds a destination city
16     public static void addCity(City city) {
17         destinationCities.add(city);
18     }
19     
20     // Get a city
21     public static City getCity(int index){
22         return (City)destinationCities.get(index);
23     }
24     
25     // Get the number of destination cities
26     public static int numberOfCities(){
27         return destinationCities.size();
28     }
29     
30 }

現在來創建一個可以模擬旅行推銷員之旅:

 1 /*
 2 * Tour.java
 3 * Stores a candidate tour through all cities
 4 */
 5 
 6 package sa;
 7 
 8 import java.util.ArrayList;
 9 import java.util.Collections;
10 
11 public class Tour{
12 
13     // Holds our tour of cities
14     private ArrayList tour = new ArrayList<City>();
15     // Cache
16     private int distance = 0;
17     
18     // Constructs a blank tour
19     public Tour(){
20         for (int i = 0; i < TourManager.numberOfCities(); i++) {
21             tour.add(null);
22         }
23     }
24     
25     // Constructs a tour from another tour
26     public Tour(ArrayList tour){
27         this.tour = (ArrayList) tour.clone();
28     }
29     
30     // Returns tour information
31     public ArrayList getTour(){
32         return tour;
33     }
34 
35     // Creates a random individual
36     public void generateIndividual() {
37         // Loop through all our destination cities and add them to our tour
38         for (int cityIndex = 0; cityIndex < TourManager.numberOfCities(); cityIndex++) {
39           setCity(cityIndex, TourManager.getCity(cityIndex));
40         }
41         // Randomly reorder the tour
42         Collections.shuffle(tour);
43     }
44 
45     // Gets a city from the tour
46     public City getCity(int tourPosition) {
47         return (City)tour.get(tourPosition);
48     }
49 
50     // Sets a city in a certain position within a tour
51     public void setCity(int tourPosition, City city) {
52         tour.set(tourPosition, city);
53         // If the tours been altered we need to reset the fitness and distance
54         distance = 0;
55     }
56     
57     // Gets the total distance of the tour
58     public int getDistance(){
59         if (distance == 0) {
60             int tourDistance = 0;
61             // Loop through our tour's cities
62             for (int cityIndex=0; cityIndex < tourSize(); cityIndex++) {
63                 // Get city we're traveling from
64                 City fromCity = getCity(cityIndex);
65                 // City we're traveling to
66                 City destinationCity;
67                 // Check we're not on our tour's last city, if we are set our 
68                 // tour's final destination city to our starting city
69                 if(cityIndex+1 < tourSize()){
70                     destinationCity = getCity(cityIndex+1);
71                 }
72                 else{
73                     destinationCity = getCity(0);
74                 }
75                 // Get the distance between the two cities
76                 tourDistance += fromCity.distanceTo(destinationCity);
77             }
78             distance = tourDistance;
79         }
80         return distance;
81     }
82 
83     // Get number of cities on our tour
84     public int tourSize() {
85         return tour.size();
86     }
87     
88     @Override
89     public String toString() {
90         String geneString = "|";
91         for (int i = 0; i < tourSize(); i++) {
92             geneString += getCity(i)+"|";
93         }
94         return geneString;
95     }
96 }

最后,讓我們創建模擬退火算法:

  1 package sa;
  2 
  3 public class SimulatedAnnealing {
  4 
  5     // Calculate the acceptance probability
  6     public static double acceptanceProbability(int energy, int newEnergy, double temperature) {
  7         // If the new solution is better, accept it
  8         if (newEnergy < energy) {
  9             return 1.0;
 10         }
 11         // If the new solution is worse, calculate an acceptance probability
 12         return Math.exp((energy - newEnergy) / temperature);
 13     }
 14 
 15     public static void main(String[] args) {
 16         // Create and add our cities
 17         City city = new City(60, 200);
 18         TourManager.addCity(city);
 19         City city2 = new City(180, 200);
 20         TourManager.addCity(city2);
 21         City city3 = new City(80, 180);
 22         TourManager.addCity(city3);
 23         City city4 = new City(140, 180);
 24         TourManager.addCity(city4);
 25         City city5 = new City(20, 160);
 26         TourManager.addCity(city5);
 27         City city6 = new City(100, 160);
 28         TourManager.addCity(city6);
 29         City city7 = new City(200, 160);
 30         TourManager.addCity(city7);
 31         City city8 = new City(140, 140);
 32         TourManager.addCity(city8);
 33         City city9 = new City(40, 120);
 34         TourManager.addCity(city9);
 35         City city10 = new City(100, 120);
 36         TourManager.addCity(city10);
 37         City city11 = new City(180, 100);
 38         TourManager.addCity(city11);
 39         City city12 = new City(60, 80);
 40         TourManager.addCity(city12);
 41         City city13 = new City(120, 80);
 42         TourManager.addCity(city13);
 43         City city14 = new City(180, 60);
 44         TourManager.addCity(city14);
 45         City city15 = new City(20, 40);
 46         TourManager.addCity(city15);
 47         City city16 = new City(100, 40);
 48         TourManager.addCity(city16);
 49         City city17 = new City(200, 40);
 50         TourManager.addCity(city17);
 51         City city18 = new City(20, 20);
 52         TourManager.addCity(city18);
 53         City city19 = new City(60, 20);
 54         TourManager.addCity(city19);
 55         City city20 = new City(160, 20);
 56         TourManager.addCity(city20);
 57 
 58         // Set initial temp
 59         double temp = 10000;
 60 
 61         // Cooling rate
 62         double coolingRate = 0.003;
 63 
 64         // Initialize intial solution
 65         Tour currentSolution = new Tour();
 66         currentSolution.generateIndividual();
 67         
 68         System.out.println("Initial solution distance: " + currentSolution.getDistance());
 69 
 70         // Set as current best
 71         Tour best = new Tour(currentSolution.getTour());
 72         
 73         // Loop until system has cooled
 74         while (temp > 1) {
 75             // Create new neighbour tour
 76             Tour newSolution = new Tour(currentSolution.getTour());
 77 
 78             // Get a random positions in the tour
 79             int tourPos1 = (int) (newSolution.tourSize() * Math.random());
 80             int tourPos2 = (int) (newSolution.tourSize() * Math.random());
 81 
 82             // Get the cities at selected positions in the tour
 83             City citySwap1 = newSolution.getCity(tourPos1);
 84             City citySwap2 = newSolution.getCity(tourPos2);
 85 
 86             // Swap them
 87             newSolution.setCity(tourPos2, citySwap1);
 88             newSolution.setCity(tourPos1, citySwap2);
 89             
 90             // Get energy of solutions
 91             int currentEnergy = currentSolution.getDistance();
 92             int neighbourEnergy = newSolution.getDistance();
 93 
 94             // Decide if we should accept the neighbour
 95             if (acceptanceProbability(currentEnergy, neighbourEnergy, temp) > Math.random()) {
 96                 currentSolution = new Tour(newSolution.getTour());
 97             }
 98 
 99             // Keep track of the best solution found
100             if (currentSolution.getDistance() < best.getDistance()) {
101                 best = new Tour(currentSolution.getTour());
102             }
103             
104             // Cool system
105             temp *= 1-coolingRate;
106         }
107 
108         System.out.println("Final solution distance: " + best.getDistance());
109         System.out.println("Tour: " + best);
110     }
111 }

結果如下:

Initial solution distance: 1966
Final solution distance: 911
Tour: |180, 200|200, 160|140, 140|180, 100|180, 60|200, 40|160, 20|120, 80|100, 40|60, 20|20, 20|20, 40|60, 80|100, 120|40, 120|20, 160|60, 200|80, 180|100, 160|140, 180|

在這個例子中,我們能夠超過我們初始隨機生成路徑的一半以上。很大程度上說明,當應用到某些類型的優化問題時,這個相對簡單的算法是多么方便。

 

模擬退火算法的參數控制問題  

模擬退火算法的應用很廣泛,可以求解NP完全問題,但其參數難以控制,其主要問題有以下三點:

  (1) 溫度T的初始值設置問題。
  溫度T的初始值設置是影響模擬退火算法全局搜索性能的重要因素之一、初始溫度高,則搜索到全局最優解的可能性大,但因此要花費大量的計算時間;反之,則可節約計算時間,但全局搜索性能可能受到影響。實際應用過程中,初始溫度一般需要依據實驗結果進行若干次調整。
  (2) 退火速度問題。
  模擬退火算法的全局搜索性能也與退火速度密切相關。一般來說,同一溫度下的“充分”搜索(退火)是相當必要的,但這需要計算時間。實際應用中,要針對具體問題的性質和特征設置合理的退火平衡條件。
  (3) 溫度管理問題。
  溫度管理問題也是模擬退火算法難以處理的問題之一。實際應用中,由於必須考慮計算復雜度的切實可行性等問題,常采用如下所示的降溫方式:
       T(t+1)=k×T(t)
式中k為正的略小於1.00的常數,t為降溫的次數。

例題推薦

  • 給定n個質點,求重心,這n個質點的重心滿足Σ(重心到點i的距離)*g[i]最小。---BZOJ 3680 參考題解請看這里
  • 給n個點,找出一個點,使這個點到其他所有點的距離之和最小,也就是求費馬點。---POJ 2420
  • 給定三維空間的n點,找出一個半徑最小的球把這些點全部包圍住。---POJ 2069
  • 平面上給定n條線段,找出一個點,使這個點到這n條線段的距離和最小。參考源碼在這里
  • 地圖中有N個陷阱,給出他們的坐標,求一個點,使得這個點到所有陷阱的最小距離最大。---POJ 1379
  • 求一個橢球面上的一個點到原點的最短距離。---HDU 5017
  • 找出一個點使得這個店到n個點的最長距離最短,即求最小覆蓋圓的半徑。---HDU 3932
  • 給一個矩陣的長寬,再給n個點,求矩陣區域內某個點到各個點的最小距離的最大值,輸出所求點的坐標。---HDU 1109
  • 給定n個點的一個多邊形,一個圓的半徑,判斷圓是否可以放在多邊形里。---HDU 3644
  • 給定n個點的坐標和它x和y方向的分速度,要求在任意時刻兩兩點之間距離最大值中的最小值。---HDU 4717

參考文獻

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM