zoukankan      html  css  js  c++  java
  • 模拟退火

    一、什么是模拟退火算法

    1、爬山算法

    在了解模拟退火算法之前,先来看一下爬山算法:爬山算法是一种贪心算法,该算法每次从当前的解空间中选取一个解作为最优解,直到达到一个局部最优解。假设函数f(x)的图像如下图:

    现在使用爬山算法来求f(x)的最大值,若C为当前最优解,则爬山算法搜索到A就会停止搜索,这会获得一个局部最优解,而不是全局最优解。

    模拟退火:继续考虑寻找f(x)最大值的问题,爬山算法搜索到A点时就会停止搜索,原因是A点左右的值均小于A点的值。模拟退火算法采用的解决办法是以一定的概率选择A两边的点,尽管A两边的点并不是局部最优解,这样就有一定的概率搜索到D点,从而搜索到B点,最终获得了全局最优解。

    2、模拟退火算法

     

    现在想求函数的(全局)最优解。如果采用Greedy策略,那么从A点开始试探,如果函数值继续减少,那么试探过程就会继续。而当到达点B时,显然我们的探求过程就结束了(因为无论朝哪个方向努力,结果只会越来越大)。最终我们只能找打一个局部最后解B。

    模拟退火其实也是一种Greedy算法,但是它的搜索过程引入了随机因素。模拟退火算法以一定的概率来接受一个比当前解要差的解,因此有可能会跳出这个局部的最优解,达到全局的最优解。以上图为例,模拟退火算法在搜索到局部最优解B后,会以一定的概率接受向右继续移动。也许经过几次这样的不是局部最优的移动后会到达B 和C之间的峰点,于是就跳出了局部最小值B。

    模拟退火算法(Simulate Anneal,SA)是一种通用概率演算法,用来在一个大的搜寻空间内找寻命题的最优解

        爬山算法:兔子朝着比现在高的地方跳去。它找到了不远处的最高山峰。但是这座山不一定是珠穆朗玛峰。这就是爬山算法,它不能保证局部最优值就是全局最优值。

      模拟退火:兔子喝醉了。它随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,它渐渐清醒了并朝最高方向跳去。这就是模拟退火。

    使用模拟退火算法解决旅行商问题

    旅行商问题(TravelingSalesmanProblem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。该问题可以使用模拟退火算法解决,C++代码如下:

    使用模拟退火算法可以比较快的求出TSP的一条近似最优路径。(使用遗传算法也是可以的,我将在下一篇文章中介绍)模拟退火解决TSP的思路:

    1. 产生一条新的遍历路径P(i+1),计算路径P(i+1)的长度L( P(i+1) )

    2. 若L(P(i+1)) < L(P(i)),则接受P(i+1)为新的路径,否则以模拟退火的那个概率接受P(i+1) ,然后降温

    3. 重复步骤1,2直到满足退出条件

      产生新的遍历路径的方法有很多,下面列举其中3种:

    1. 随机选择2个节点,交换路径中的这2个节点的顺序。

    2. 随机选择2个节点,将路径中这2个节点间的节点顺序逆转。

    3. 随机选择3个节点m,n,k,然后将节点m与n间的节点移位到节点k后面。

    #include <iostream>
    #include <string.h>
    #include <stdlib.h>
    #include <algorithm>
    #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 20    //外循环次数
    #define ILOOP 100   //内循环次数
    
    using namespace std;
    
    //定义路线结构体
    struct Path
    {
        int citys[N];
        double len;
    };
    
    //定义城市点坐标
    struct Point
    {
        double x, y;
    };
    
    Path bestPath;        //记录最优路径
    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;
        bestPath.len = 0;
        for(int i = 0; i < n; i++)
        {
            bestPath.citys[i] = i;
            if(i != n - 1)
            {
                printf("%d--->", i);
                bestPath.len += w[i][i + 1];
            }
            else
                printf("%d
    ", i);
        }
        printf("
    Init path length is : %.3lf
    ", bestPath.len);
        printf("-----------------------------------
    
    ");
    }
    
    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
    ", t.citys[i]);
        }
        printf("
    The path length is : %.3lf
    ", t.len);
        printf("-----------------------------------
    
    ");
    }
    
    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((unsigned)(time(NULL)));
        Path curPath = bestPath;
        Path newPath = bestPath;
        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);
                    //如果找到比当前更差的解,以一定概率接受该解,并且这个概率会越来越小
                    if(exp(dE / t) > rd && exp(dE / t) < 1)
                        curPath = newPath;
                    P_L++;
                }
                if(P_L > LIMIT)
                {
                    P_F++;
                    break;
                }
            }
            if(curPath.len < bestPath.len)
                bestPath = curPath;
            if(P_F > OLOOP || t < EPS)
                break;
            t *= DELTA;
        }
    }
    
    int main(int argc, const char * argv[]) {
    
        freopen("TSP.data", "r", stdin);
        int n;
        Input(p, n);
        GetDist(p, n);
        Init(n);
        SA(n);
        Print(bestPath, n);
        printf("Total test times is : %d
    ", nCase);
        return 0;
    }
    View Code

     

    转自:https://www.cnblogs.com/sench/p/9427193.html

  • 相关阅读:
    数型DP
    士兵杀敌(三)(RMQ)(DP) or ( 线段树 )
    还是回文
    dp(DAG)
    mysql的内连接外连接查询
    一些概念
    函数式编程中的一些概念
    Optional<T>
    计算文件和字符串的MD5摘要
    SpringMVC的一些配置
  • 原文地址:https://www.cnblogs.com/Lemon1234/p/11635001.html
Copyright © 2011-2022 走看看