現代啓發式算法--初步瞭解模擬退火算法

模擬退火算法(搜索過程引入隨機因素的貪心法,具有全局優化的性能)

1.目的
求問題的最優化解;

2.原理解釋
模仿物理退火的過程。高溫物體的粒子可從高能態轉換爲低能態,而從低能態轉爲高能態則有一定的隨機性,物體溫度越低越難從低能轉換爲高能,最終所有粒子轉爲最低能量態。
對應的物理準則:Metropolis準則
假設在狀態Xold 時,系統受到某種擾動使其狀態變爲Xnew 。與此對應,系統的能量也從E(Xold) 變爲 E(Xnew) 。系統由狀態Xold 變爲狀態Xnew 的接收概率P:

第二行的解釋:溫度越高,出現一次能量差爲dE的降溫的概率就越大,溫度越低,出現降溫的概率就越小;

3.與物理退火過程對應的算法的要素
(1)狀態空間(搜索空間):有可行解的集合組成;
(2)狀態產生函數(領域函數):內能E 轉換爲目標函數值f,應儘可能保證產生的候選解遍歷全部解空間;
(3)狀態轉移概率:近似於Metroplis準則,接受一個新解爲當前解的概率,與溫度參數T有關;
(4)冷卻進度表T(t):即降溫管理表,用來控T下降的函數,假設時刻t的溫度用T(t)來表示,t的含義也不一定只能是時間,比如:

第三個式子與t無關,k是進行迭代的次數。
溫度T演化爲控制參數;
(5)初始溫度T0 : 確定初溫應折中考慮優化質量與效率;
(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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章