模擬退火算法

模擬退火算法是用來求解最優化問題的算法。比如著名的TSP問題函數最大值最小值問題等等。接下來將以如下幾個方面來詳細介紹模擬退火算法。

 

Contents

 

   1. 模擬退火算法認識

   2. 模擬退火算法描述

   3. 費馬點問題求解

   4. 最小包含球問題求解

   5. 函數最值問題求解

   6. TSP問題求解

 

 

1. 模擬退火算法認識

 

   爬山算法也是一個用來求解最優化問題的算法,每次都向着當前上升最快的方向往上爬,但是初始化不同可能

   會得到不同的局部最優值,模擬退火算法就可能跳出這種局部最優解的限制。模擬退火算法是模擬熱力學系統

   中的退火過程。在退火過程中是將目標函數作爲能量函數。大致過程如下

 

   初始高溫 => 溫度緩慢下降=> 終止在低溫 (這時能量函數達到最小,目標函數最小)

 

   在熱力學中的退火過程大致是變溫物體緩慢降溫而達到分子之間能量最低的狀態。設熱力學系統S中有有限個且

   離散的n個狀態,狀態的能量爲,在溫度下,經過一段時間達到熱平衡,這時處於狀態的概率爲

 

                    

 

   模擬退火算法也是貪心算法,但是在其過程中引入了隨機因素,以一定的概率接受一個比當前解要差的解,並且

   這個概率隨着時間的推移而逐漸降低。

 

  

2. 模擬退火算法描述


      模擬退火算法來源於固體退火原理,將固體加溫至充分高,再讓其徐徐冷卻,加溫時,固體內部粒子隨溫升變爲無序狀,內能增大,而徐徐冷卻時粒子漸趨有序,在每個溫度都達到平衡態,最後在常溫時達到基態,內能減爲最小。 
    模擬退火算法(Simulated Annealing,SA)最早由Kirkpatrick等應用於組合優化領域,它是基於Monte-Carlo迭代求解策略的一種隨機尋優算法,其出發點是基於物理中固體物質的退火過程與一般組合優化問題之間的相似性。模擬退火算法從某一較高初溫出發,伴隨溫度參數的不斷下降,結合概率突跳特性在解空間中隨機尋找目標函數的全局最優解,即在局部最優解能概率性地跳出並最終趨於全局最優。模擬退火算法是一種通用的優化算法,理論上算法具有概率的全局優化性能,目前已在工程中得到了廣泛應用,諸如VLSI、生產調度、控制工程、機器學習、神經網絡、信號處理等領域。

模擬退火算法是通過賦予搜索過程一種時變且最終趨於零的概率突跳性,從而可有效避免陷入局部極小並最終趨於全局最優的串行結構的優化算法。

模擬退火算法的模型 
模擬退火算法可以分解爲解空間、目標函數和初始解三部分。 
模擬退火的基本思想
(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步。

模擬退火算法流程圖

 Simulated_Annealing_flowchart


 

   若,即移動後得到更優的解,那麼總是接受改移動。

   若,即移動後得到更差的解,則以一定的概率接受該移動,並且這個概率隨時間推移

   逐漸降低。這個概率表示爲

 

   

 

   由於是退火過程,所以dE < 0,這個公式說明了溫度越高出現一次能量差爲dE的降溫概率就越大,溫度越低,

   出現降溫的概率就越小,由於dE總是小於0,所以P(dE)取值在01之間。僞碼如下

 

   

 

   

 

3. 費馬點問題求解

 

   題目:http://poj.org/problem?id=2420

 

   題意:給n個點,找出一個點,使這個點到其他所有點的距離之和最小,也就是求費馬點。

 

代碼:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. #include <stdio.h>  
  5. #include <time.h>  
  6. #include <math.h>  
  7.   
  8. #define N 1005  
  9. #define eps 1e-8     //搜索停止條件閥值  
  10. #define INF 1e99       
  11. #define delta 0.98   //溫度下降速度  
  12. #define T 100        //初始溫度  
  13.   
  14. using namespace std;  
  15.   
  16. int dx[4] = {0, 0, -1, 1};  
  17. int dy[4] = {-1, 1, 0, 0};  //上下左右四個方向  
  18.   
  19. struct Point  
  20. {  
  21.     double x, y;  
  22. };  
  23.   
  24. Point p[N];  
  25.   
  26. double dist(Point A, Point B)  
  27. {  
  28.     return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));  
  29. }  
  30.   
  31. double GetSum(Point p[], int n, Point t)  
  32. {  
  33.     double ans = 0;  
  34.     while(n--)  
  35.         ans += dist(p[n], t);  
  36.     return ans;  
  37. }  
  38.   
  39. //其實我覺得這玩意兒根本不叫模擬退火  
  40. double Search(Point p[], int n)  
  41. {  
  42.     Point s = p[0];    //隨機初始化一個點開始搜索  
  43.     double t = T;      //初始化溫度  
  44.     double ans = INF;  //初始答案值  
  45.     while(t > eps)  
  46.     {  
  47.         bool flag = 1;  
  48.         while(flag)  
  49.         {  
  50.             flag = 0;  
  51.             for(int i = 0; i < 4; i++)    //上下左右四個方向  
  52.             {  
  53.                 Point z;  
  54.                 z.x = s.x + dx[i] * t;  
  55.                 z.y = s.y + dy[i] * t;  
  56.                 double tp = GetSum(p, n, z);  
  57.                 if(ans > tp)  
  58.                 {  
  59.                     ans = tp;  
  60.                     s = z;  
  61.                     flag = 1;  
  62.                 }  
  63.             }  
  64.         }  
  65.         t *= delta;  
  66.     }  
  67.     return ans;  
  68. }  
  69.   
  70. int main()  
  71. {  
  72.     int n;  
  73.     while(scanf("%d", &n) != EOF)  
  74.     {  
  75.         for(int i = 0; i < n; i++)  
  76.             scanf("%lf %lf", &p[i].x, &p[i].y);  
  77.         printf("%.0lf\n", Search(p, n));  
  78.     }  
  79.     return 0;  
  80. }  


 

題目:平面上給定n條線段,找出一個點,使這個點到這n條線段的距離和最小。

 

代碼:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. #include <stdio.h>  
  5. #include <time.h>  
  6. #include <math.h>  
  7.   
  8. #define N 1005  
  9. #define eps 1e-8     //搜索停止條件閥值  
  10. #define INF 1e99       
  11. #define delta 0.98   //溫度下降速度  
  12. #define T 100        //初始溫度  
  13.   
  14. using namespace std;  
  15.   
  16. int dx[4] = {0, 0, -1, 1};  
  17. int dy[4] = {-1, 1, 0, 0};  //上下左右四個方向  
  18.   
  19. struct Point  
  20. {  
  21.     double x, y;  
  22. };  
  23.   
  24. Point s[N], t[N];  
  25.   
  26. double cross(Point A, Point B, Point C)    
  27. {    
  28.     return (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x);    
  29. }    
  30.   
  31. double multi(Point A, Point B, Point C)    
  32. {    
  33.     return (B.x - A.x) * (C.x - A.x) + (B.y - A.y) * (C.y - A.y);    
  34. }    
  35.   
  36. double dist(Point A, Point B)  
  37. {  
  38.     return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));  
  39. }  
  40.   
  41. double GetDist(Point A, Point B, Point C)    
  42. {    
  43.     if(dist(A, B) < eps) return dist(B, C);    
  44.     if(multi(A, B, C) < -eps) return dist(A, C);    
  45.     if(multi(B, A, C) < -eps) return dist(B, C);    
  46.     return fabs(cross(A, B, C) / dist(A, B));    
  47. }    
  48.   
  49. double GetSum(Point s[], Point t[], int n, Point o)  
  50. {  
  51.     double ans = 0;  
  52.     while(n--)  
  53.         ans += GetDist(s[n], t[n], o);  
  54.     return ans;  
  55. }  
  56.   
  57. double Search(Point s[], Point t[], int n, Point &o)  
  58. {  
  59.     o = s[0];      
  60.     double tem = T;        
  61.     double ans = INF;  
  62.     while(tem > eps)  
  63.     {  
  64.         bool flag = 1;  
  65.         while(flag)  
  66.         {  
  67.             flag = 0;  
  68.             for(int i = 0; i < 4; i++)    //上下左右四個方向  
  69.             {  
  70.                 Point z;  
  71.                 z.x = o.x + dx[i] * tem;  
  72.                 z.y = o.y + dy[i] * tem;  
  73.                 double tp = GetSum(s, t, n, z);  
  74.                 if(ans > tp)  
  75.                 {  
  76.                     ans = tp;  
  77.                     o = z;  
  78.                     flag = 1;  
  79.                 }  
  80.             }  
  81.         }  
  82.         tem *= delta;  
  83.     }  
  84.     return ans;  
  85. }  
  86.   
  87. int main()  
  88. {  
  89.     int n;  
  90.     while(scanf("%d", &n) != EOF)  
  91.     {  
  92.         for(int i = 0; i < n; i++)  
  93.             scanf("%lf %lf %lf %lf", &s[i].x, &s[i].y, &t[i].x, &t[i].y);  
  94.         Point o;  
  95.         double ans = Search(s, t, n, o);  
  96.         printf("%.1lf %.1lf %.1lf\n", o.x, o.y, ans);    
  97.     }  
  98.     return 0;  
  99. }  


 

4. 最小包含球問題求解

 

   題目:http://poj.org/problem?id=2069

 

   題意:給定三維空間的n點,找出一個半徑最小的球把這些點全部包圍住。

 

代碼:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <string.h>  
  3. #include <stdio.h>  
  4. #include <math.h>  
  5.   
  6. #define N 55  
  7. #define eps 1e-7  
  8. #define T 100  
  9. #define delta 0.98  
  10. #define INF 1e99  
  11.   
  12. using namespace std;  
  13.   
  14. struct Point  
  15. {  
  16.     double x, y, z;  
  17. };  
  18.   
  19. Point p[N];  
  20.   
  21. double dist(Point A, Point B)  
  22. {  
  23.     return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + (A.z - B.z) * (A.z - B.z));  
  24. }  
  25.   
  26. double Search(Point p[], int n)  
  27. {  
  28.     Point s = p[0];  
  29.     double t = T;  
  30.     double ans = INF;  
  31.     while(t > eps)  
  32.     {  
  33.         int k = 0;  
  34.         for(int i = 0; i < n; i++)  
  35.             if(dist(s, p[i]) > dist(s, p[k]))  
  36.                 k = i;  
  37.         double d = dist(s, p[k]);  
  38.         ans = min(ans, d);  
  39.         s.x += (p[k].x - s.x) / d * t;  
  40.         s.y += (p[k].y - s.y) / d * t;  
  41.         s.z += (p[k].z - s.z) / d * t;  
  42.         t *= delta;  
  43.     }  
  44.     return ans;  
  45. }  
  46.   
  47. int main()  
  48. {  
  49.     int n;  
  50.     while(cin >> n && n)  
  51.     {  
  52.         for(int i = 0; i < n; i++)  
  53.             cin >> p[i].x >> p[i].y >> p[i].z;  
  54.         double ans = Search(p, n);  
  55.         printf("%.5lf\n", ans);  
  56.     }  
  57.     return 0;  
  58. }  


 

5. 函數最值問題求解

 

   題目:http://acm.hdu.edu.cn/showproblem.php?pid=2899

 

   題意:給出方程,其中,輸入,求的最小值。

 

   分析:本題可以用經典的二分法求解,這種方法比較簡單,就不說了。主要來說模擬退火做法。

 

代碼:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. #include <algorithm>  
  5. #include <stdio.h>  
  6. #include <time.h>  
  7. #include <math.h>  
  8.   
  9. #define ITERS 10  
  10. #define T 100  
  11. #define eps 1e-8  
  12. #define delta 0.98  
  13. #define INF 1e99  
  14.   
  15. using namespace std;  
  16.   
  17. double x[ITERS];  
  18.   
  19. int Judge(double x, double y)  
  20. {  
  21.     if(fabs(x - y) < eps) return 0;  
  22.     return x < y ? -1 : 1;  
  23. }  
  24.   
  25. double Random()  
  26. {  
  27.     double x = rand() * 1.0 / RAND_MAX;  
  28.     if(rand() & 1) x *= -1;  
  29.     return x;  
  30. }  
  31.   
  32. double F(double x, double y)  
  33. {  
  34.     return 6 * x * x * x * x * x * x * x + 8 * x * x * x * x * x * x + 7 * x * x * x + 5 * x * x - y * x;  
  35. }  
  36.   
  37. void Init()  
  38. {  
  39.     for(int i = 0; i < ITERS; i++)  
  40.         x[i] = fabs(Random()) * 100;  
  41. }  
  42.   
  43. double SA(double y)  
  44. {  
  45.     double ans = INF;  
  46.     double t = T;  
  47.     while(t > eps)  
  48.     {  
  49.         for(int i = 0; i < ITERS; i++)  
  50.         {  
  51.             double tmp = F(x[i], y);  
  52.             for(int j = 0; j < ITERS; j++)  
  53.             {  
  54.                 double _x = x[i] + Random() * t;  
  55.                 if(Judge(_x, 0) >= 0 && Judge(_x, 100) <= 0)  
  56.                 {  
  57.                     double f = F(_x, y);  
  58.                     if(tmp > f)   
  59.                         x[i] = _x;  
  60.                 }  
  61.             }   
  62.         }  
  63.         t *= delta;  
  64.     }  
  65.     for(int i = 0; i < ITERS; i++)  
  66.         ans = min(ans, F(x[i], y));  
  67.     return ans;  
  68. }  
  69.   
  70. int main()  
  71. {  
  72.     int t;  
  73.     scanf("%d", &t);  
  74.     while(t--)  
  75.     {  
  76.         double y;  
  77.         scanf("%lf", &y);  
  78.         srand(time(NULL));  
  79.         Init();  
  80.         printf("%.4lf\n", SA(y));  
  81.     }  
  82.     return 0;  
  83. }  


 

6. TSP問題求解

 

   TSP問題是一個NP問題,但是可以求近似解,通過模擬退火算法實現,代碼如下

 

代碼:

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


 

數據:TSP.data

[cpp] view plain copy
  1. 27  
  2. 41 94  
  3. 37 84  
  4. 53 67  
  5. 25 62  
  6. 7  64  
  7. 2  99  
  8. 68 58  
  9. 71 44  
  10. 54 62  
  11. 83 69  
  12. 64 60  
  13. 18 54  
  14. 22 60  
  15. 83 46  
  16. 91 38  
  17. 25 38  
  18. 24 42  
  19. 58 69  
  20. 71 71  
  21. 74 78  
  22. 87 76  
  23. 18 40  
  24. 13 40  
  25. 82  7  
  26. 62 32  
  27. 58 35  
  28. 45 21  
發佈了0 篇原創文章 · 獲贊 20 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章