模擬退火算法(搜索過程引入隨機因素的貪心法,具有全局優化的性能)
1.目的
求問題的最優化解;
2.原理解釋
模仿物理退火的過程。高溫物體的粒子可從高能態轉換爲低能態,而從低能態轉爲高能態則有一定的隨機性,物體溫度越低越難從低能轉換爲高能,最終所有粒子轉爲最低能量態。
對應的物理準則:Metropolis準則
假設在狀態
第二行的解釋:溫度越高,出現一次能量差爲dE的降溫的概率就越大,溫度越低,出現降溫的概率就越小;
3.與物理退火過程對應的算法的要素
(1)狀態空間(搜索空間):有可行解的集合組成;
(2)狀態產生函數(領域函數):內能E 轉換爲目標函數值f,應儘可能保證產生的候選解遍歷全部解空間;
(3)狀態轉移概率:近似於Metroplis準則,接受一個新解爲當前解的概率,與溫度參數T有關;
(4)冷卻進度表T(t):即降溫管理表,用來控T下降的函數,假設時刻t的溫度用T(t)來表示,t的含義也不一定只能是時間,比如:
第三個式子與t無關,k是進行迭代的次數。
溫度T演化爲控制參數;
(5)初始溫度
(6)抽樣穩定準則:應保證連續若干步迭代所得的值變化較小且按一定的步數抽樣;
(7)算法終止準則:設置終止溫度,設置迭代次數,搜索得到的最終值連續若干步保持不變。
4.與物理退火過程對應的算法理論:
在進行優化問題的領域搜索的時候引入退火規則。
下一狀態比當前狀態更優時接受新狀態,反之有一定的概率接受新狀態,該概率隨着迭代的進行或溫度的下降不斷減小,最終趨近於零。
> 5.模擬退火算法的模型
模擬退火算法可以分解爲解空間、目標函數和初始解三部分。
模擬退火的基本思想:
(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步。
6.模擬退火算法的流程圖:
還有一種清晰理解版:
模擬退火解決TSP問題(修改原博主bug後添加註釋,轉載並測試成功,,原博客爲參考第三個):
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#define N 30 //城市數量
#define T 3000 //初始溫度
#define EPS 1e-8 //終止溫度
#define DELTA 0.98 //溫度衰減率
#define LIMIT 1000 //概率選擇上限
#define OLOOP 100 //外循環次數
#define ILOOP 1500 //內循環次數
using namespace std;
//定義路線結構體
struct Path
{
int citys[N];
double len;
};
//定義城市點座標
struct Point
{
double x, y;
};
Path path; //記錄最優路徑
Point p[N]; //每個城市的座標
double w[N][N]; //兩兩城市之間路徑長度
int nCase; //測試次數
double dist(Point A, Point B)
{
return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}
void GetDist(Point p[], int n)
{
for(int i = 0; i < n; i++)
for(int j = i + 1; j < n; j++)
w[i][j] = w[j][i] = dist(p[i], p[j]);
}
void Input(Point p[], int &n)
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%lf %lf", &p[i].x, &p[i].y);
}
//初始化
void Init(int n)
{
nCase = 0;
path.len = 0;
for(int i = 0; i < n; i++)
{
path.citys[i] = i;
if(i != n - 1)
{
printf("%d--->", i);
path.len += w[i][i + 1];
}
else
printf("%d\n", i);
}
printf("\nInit path length is : %.3lf\n", path.len);
}
//畫出路線圖
void Print(Path t, int n)
{
printf("Path is : ");
for(int i = 0; i < n; i++)
{
if(i != n - 1)
printf("%d-->", t.citys[i]);
else
printf("%d\n", t.citys[i]);
}
printf("\nThe path length is : %.3lf\n", t.len);
}
//隨機抽取兩座城市並交換兩座城市記錄在旅行鏈中的順序
Path GetNext(Path p, int n)
{
Path ans = p;
int x = (int)(n * (rand() / (RAND_MAX + 1.0)));//隨機取兩座城市
int y = (int)(n * (rand() / (RAND_MAX + 1.0)));
while(x == y)
{
x = (int)(n * (rand() / (RAND_MAX + 1.0)));
y = (int)(n * (rand() / (RAND_MAX + 1.0)));
}
swap(ans.citys[x], ans.citys[y]);
ans.len = 0;
for(int i = 0; i < n - 1; i++)
ans.len += w[ans.citys[i]][ans.citys[i + 1]];//求得新路徑的長度
cout << "nCase = " << nCase << endl;
Print(ans, n);//輸出新解的路徑
nCase++;
return ans;
}
void SA(int n)
{
double t = T;//初始溫度
// srand(time(NULL));
Path curPath = path;
Path newPath = path;
int P_L = 0;
int P_F = 0;
while(1){ //外循環,主要更新參數t,模擬退火過程
for(int i = 0; i < ILOOP; i++){ //內循環,尋找在一定溫度下的最優值
newPath = GetNext(curPath, n);
double dE = newPath.len - curPath.len;
if(dE < 0){ //如果找到更優值,直接更新
curPath = newPath;
P_L = 0;
P_F = 0;
}
else{
double rd = rand() / (RAND_MAX + 1.0);//即爲[0,1)的隨機數
if(exp(-dE / t) > rd && exp(-dE / t) < 1) //如果找到比當前更差的解,以一定概率接受該解,並且這個概率會越來越小
curPath = newPath;
P_L++;//不管有沒有接受新解,P_L加一
}
if(P_L > LIMIT){//如果很多次沒有接受新解,
P_F++;
break;//break只對循環起作用,無視if
}
}
if(curPath.len < newPath.len)//r如果很多次沒有接受更好解
path = curPath;
if(P_F > OLOOP || t < EPS)
break;
t *= DELTA;
}
}
int main()
{
int n;
Input(p, n);// 輸入城市座標
GetDist(p, n);//計算每兩座城市間的距離
Init(n);//初始化
SA(n);//模擬退火
Print(path, n);
printf("Total test times is : %d\n", nCase);
return 0;
}
模擬退火解決01揹包問題:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#define T0 1000
#define TF 0.01
#define T 0.95
#define N 1000
#define M 50
int weight[M]= { 80, 82, 85, 70, 72, 70, 66, 50, 55, 25,
50, 55, 40, 48, 50, 32, 22, 60, 30, 32,
40, 38, 35, 32, 25, 28, 30, 22, 50, 30,
45, 30, 60, 50, 20, 65, 20, 25, 30, 10,
20, 25, 15, 10, 10, 10, 4, 4, 2, 1 },
profit[M]= { 220, 208, 198, 192, 180, 180, 165, 162, 160, 158,
155, 130, 125, 122, 120, 118, 115, 110, 105, 101,
100, 100, 98, 96, 95, 90, 88, 82, 80, 77,
75, 73, 72, 70, 69, 66, 65, 63, 60, 58,
56, 50, 30, 20, 15, 10, 8, 5, 3, 1},
contain = 1000;
int premaxp = 0, bestmaxp = 0;
int a[M] = {0}, preseq[M] = {0}, bestseq[M]={0};
int calprofit(int a[M]) //計算總利潤
{
int i = 0;
int p = 0;
for (i = 0; i < M; i++)
p = p + (a[i] * profit[i]);
return p;
}
int calweight(int a[M])
{
int i = 0;
int w = 0;
for (i = 0; i < M; i++)
{
w = w + (weight[i] * a[i]); //a[i]==0代表不放,==1代表放
}
return w;
}
void initialize(void)
{
int i = 0;
for (i = 0; i < M; i++)
{
a[i] = 0;
preseq[i] = 0;
bestseq[i] = 0;
}
premaxp = calprofit(a);
bestmaxp = premaxp;
}
void getrand(int *i, int *j)
{
*i = 0;
*j = 0;
while (*i == *j)
{
*i = rand()%50;
*j = rand()%50;
}
}
void change(void)
{
int r1 = 0, r2 = 0;
getrand(&r1, &r2); //隨機取兩個物品
a[r1] = 1;
a[r2] = 1;
if (calweight(a) > contain)
{
a[r2] = 0;
} //如果總重量超出,則不放入
if (calweight(a) > contain)
{
a[r1] = 0;
}
}
void SA(void)
{
double t = T0; //設定初始溫度
int i = 0, j = 0;
int dif1 = 0, dif2 = 0;
double p = 0.0;
while(t > TF) //沒有達到限定溫度時
{
for (i = 0; i < N; i++)//循環次數
{
change();
dif1 = calprofit(a) - bestmaxp;
if (dif1 > 0)//若總利潤比目標利潤的大
{
for (j = 0 ; j < M; j++)
{
preseq[j] = a[j];
bestseq[j] = a[j];//把物品的取放賦予目標數組與先前一次數組
}
premaxp = calprofit(a);
bestmaxp = premaxp;//把當前利潤賦予目標利潤與先前一次利潤
}
else
{
dif2 = calprofit(a) - premaxp;//把當前一次的利潤與先前一次的利潤做對比
if (dif2 > 0)
{
for (j = 0; j < M; j++)
{
preseq[j] = a[j];
}
premaxp = calprofit(a);
}
else
{
p = rand()%20001/20000.0;
if (exp((dif2)/ t) > p)
{
for (j = 0; j < M; j++)
{
preseq[j] = a[j];
}
premaxp = calprofit(a);
}
else
{
for (j = 0; j < M; j++)
{
a[j] = preseq[j];
}
}
}
}
}
t = t * T;//溫度降低
}
}
int main(void)
{
int i;
srand((unsigned)time(NULL));
initialize();
SA();
for (i = 0; i < M; i++)
{
if (0 == (i % 5))
{
printf(" ");
}
printf("%d", bestseq[i]);
}
printf("\nThe best value is %d\n", bestmaxp);
return 1;
}
參考:
http://www.cnblogs.com/growing/archive/2010/12/16/1908255.html
http://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html
http://blog.csdn.net/acdreamers/article/details/10019849
http://www.cnblogs.com/emanlee/archive/2011/07/31/2122727.html