模拟退火算法(SA)求解TSP 问题(C语言实现)

简介:     这篇文章是之前写的智能算法(遗传算法(GA)、粒子群算法(PSO))的补充。其实代码我老早之前就写完了,今天恰好重新翻到了,就拿出来给大家分享一下,也当是回顾与总结了。    首先介绍一下模拟退火算法(SA)。

    这篇文章是之前写的智能算法(遗传算法(GA)、粒子群算法(PSO))的补充。其实代码我老早之前就写完了,今天恰好重新翻到了,就拿出来给大家分享一下,也当是回顾与总结了。

    首先介绍一下模拟退火算法(SA)。模拟退火算法(simulated annealing,SA)算法最早是由Metropolis等人提出的。其出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。模拟退火算法是一种通用的优化算法,其物理退火过程由以下三部分组成:

    (1)加温过程

    (2)等温过程

    (3)冷却过程

     其中加温过程对应算法设定的初始温度,等温过程对应算法的Metropolis抽样过程,冷却过程对应控制参数的下降。这里能量的变化就是目标函数,要得到的最优解就是能量最低状态。Metropolis准则是SA算法收敛于全局最优解的关键所在,Metropolis准则以一定的概率接受恶化解,这样就使得算法可以跳离局部最优解的陷阱。

     模拟退火算法为求解传统方法难以处理的TSP问题提供了一个有效的途径和通用的处理框架,并逐渐发展成为一种迭代自适应启发式概率搜索算法。模拟退火算法可以用于求解不同的非线性问题,对于不可微甚至不连续函数的优化,能以较大概率求得全局最优解,该算法还具有较强的鲁棒性、全局收敛性、隐含并行性以及广泛的适应性,对目标函数以及约束函数没有任何要求。

     SA 算法实现的步骤如下:(下面以最小化问题为例)

     (1)初始化:取温度T0足够大,令T = T0,取任意解S1,确定每个T时 的迭代次数,即 Metropolis链长L。

     (2)对当前温度T和k=1,2,3,...,L,重复步骤(3)~(6)

     (3)对当前解S1随机产生一个扰动得到一个新解 S2.

     (4) 计算S2的增量df = f(S2) - f(S1),其中f(S1)为S1的代价函数。

      (5)若df < 0,接受S2作为新的当前解,即S1 = S2;否则S2的接受概率为 exp(-df/T),即随机产生(0,1)上的均匀分布的随机数rand,若 exp(-df/T)>rand

,则接受S2作为新的当前解,S1 = S2;否则保留当前解。

                (6)如果满足最终的终止条件,Stop,则输出当前解S1作为最优解,结束程序。终止条件Stop通常为:在连续若干个Metropolis链中新解S2都没有被接受时终止算法,或者是设定结束温度。否则按衰减函数衰减T后返回(2)

      以上的步骤称之为Metropolis过程。逐步降低控制温度,重复Metropolis过程,直至满足结束准则Stop求出最优解。可以看出SA整体的步骤相比GA以及PSO还是简单了很多了,而且亲测效果还不错,所以属于性价比较高的算法。关键的步骤在第(6)步。

      不废话了,直接上代码吧。TSP的数据和之前的数据一样,使用C语言实现。代码如下:

      

  1 /*
  2  * 使用模拟退火算法(SA)求解TSP问题(以中国TSP问题为例)
  3  * 参考自《Matlab 智能算法30个案例分析》
  4  * 模拟退火的原理这里略去,可以参考上书或者相关论文
  5  * update: 16/12/11
  6  * author:lyrichu
  7  * email:919987476@qq.com
  8  */
  9 #include<stdio.h>
 10 #include<stdlib.h>
 11 #include<string.h>
 12 #include<time.h>
 13 #include<math.h>
 14 #define T0 50000.0  // 初始温度
 15 #define T_end (1e-8)
 16 #define q  0.98   // 退火系数
 17 #define L 1000  // 每个温度时的迭代次数,即链长
 18 #define N 31  // 城市数量
 19 int city_list[N]; // 用于存放一个解
 20 double city_pos[N][2] = {{1304,2312},{3639,1315},{4177,2244},{3712,1399},{3488,1535},{3326,1556},{3238,1229},{4196,1004},{4312,790},
 21     {4386,570},{3007,1970},{2562,1756},{2788,1491},{2381,1676},{1332,695},{3715,1678},{3918,2179},{4061,2370},{3780,2212},{3676,2578},{4029,2838},
 22     {4263,2931},{3429,1908},{3507,2367},{3394,2643},{3439,3201},{2935,3240},{3140,3550},{2545,2357},{2778,2826},{2370,2975}}; // 中国31个城市坐标
 23 //函数声明
 24 double distance(double *,double *); // 计算两个城市距离
 25 double path_len(int *);  // 计算路径长度
 26 void  init();  //初始化函数
 27 void create_new(); // 产生新解
 28 // 距离函数
 29 double distance(double * city1,double * city2)
 30 {
 31     double x1 = *city1;
 32     double y1 = *(city1+1);
 33     double x2 = *(city2);
 34     double y2 = *(city2+1);
 35     double dis = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
 36     return dis;
 37 }
 38 
 39 // 计算路径长度
 40 double path_len(int * arr)
 41 {
 42     double path = 0; // 初始化路径长度
 43     int index = *arr; // 定位到第一个数字(城市序号)
 44     for(int i=0;i<N-1;i++)
 45     {
 46         int index1 = *(arr+i);
 47         int index2 = *(arr+i+1);
 48         double dis = distance(city_pos[index1-1],city_pos[index2-1]);
 49         path += dis;
 50     }
 51     int last_index = *(arr+N-1); // 最后一个城市序号
 52     int first_index = *arr; // 第一个城市序号
 53     double last_dis = distance(city_pos[last_index-1],city_pos[first_index-1]);
 54     path = path + last_dis;
 55     return path; // 返回总的路径长度
 56 }
 57 
 58 // 初始化函数
 59 void init()
 60 {
 61     for(int i=0;i<N;i++)
 62         city_list[i] = i+1;  // 初始化一个解
 63 }
 64 
 65 // 产生一个新解
 66 // 此处采用随机交叉两个位置的方式产生新的解
 67 void create_new()
 68 {
 69     double r1 = ((double)rand())/(RAND_MAX+1.0);
 70     double r2 = ((double)rand())/(RAND_MAX+1.0);
 71     int pos1 = (int)(N*r1); //第一个交叉点的位置
 72     int pos2 = (int)(N*r2);
 73     int temp = city_list[pos1];
 74     city_list[pos1] = city_list[pos2];
 75     city_list[pos2] = temp;   // 交换两个点
 76 }
 77 
 78 // 主函数
 79 int main(void)
 80 {
 81     srand((unsigned)time(NULL)); //初始化随机数种子
 82     time_t start,finish;
 83     start = clock(); // 程序运行开始计时
 84     double T;
 85     int count = 0; // 记录降温次数
 86     T = T0; //初始温度
 87     init(); //初始化一个解
 88     int city_list_copy[N]; // 用于保存原始解
 89     double f1,f2,df; //f1为初始解目标函数值,f2为新解目标函数值,df为二者差值
 90     double r; // 0-1之间的随机数,用来决定是否接受新解
 91     while(T > T_end) // 当温度低于结束温度时,退火结束
 92     {
 93         for(int i=0;i<L;i++)
 94         {
 95             memcpy(city_list_copy,city_list,N*sizeof(int)); // 复制数组
 96             create_new(); // 产生新解
 97             f1 = path_len(city_list_copy);
 98             f2 = path_len(city_list);
 99             df = f2 - f1;
100             // 以下是Metropolis准则
101             if(df >= 0)
102             {
103                 r = ((double)rand())/(RAND_MAX);
104                 if(exp(-df/T) <= r) // 保留原来的解
105                 {
106                     memcpy(city_list,city_list_copy,N*sizeof(int));
107                 }
108             }
109         }
110         T *= q; // 降温
111         count++;
112     }
113     finish = clock(); // 退火过程结束
114     double duration = ((double)(finish-start))/CLOCKS_PER_SEC; // 计算时间
115     printf("采用模拟退火算法,初始温度T0=%.2f,降温系数q=%.2f,每个温度迭代%d次,共降温%d次,得到的TSP最优路径为:\n",T0,q,L,count);
116     for(int i=0;i<N-1;i++)  // 输出最优路径
117     {
118         printf("%d--->",city_list[i]);
119     }
120     printf("%d\n",city_list[N-1]);
121     double len = path_len(city_list); // 最优路径长度
122     printf("最优路径长度为:%lf\n",len);
123     printf("程序运行耗时:%lf秒.\n",duration);
124     return 0;
125 }

运行结果截图如下:

注:如果使用gcc编译报错,可能需要加上 -std=c99选项以及在末尾加上 -lm(链接math库)。

热爱编程,热爱机器学习! github:http://www.github.com/Lyrichu github blog:http://Lyrichu.github.io 个人博客站点:http://www.movieb2b.com(不再维护)
目录
相关文章
|
3月前
|
存储 算法 数据可视化
基于禁忌搜索算法的TSP问题最优路径搜索matlab仿真
本程序基于禁忌搜索算法解决旅行商问题(TSP),旨在寻找访问多个城市的最短路径。使用 MATLAB 2022A 编写,包含城市坐标生成、路径优化及结果可视化功能。通过禁忌列表、禁忌长度与藐视准则等机制,提升搜索效率与解的质量,适用于物流配送、路径规划等场景。
|
4月前
|
算法 Java 调度
【车间调度】基于GA、PSO、SA、ACO、TS优化算法的车间调度比较研究(Matlab代码实现)
【车间调度】基于GA、PSO、SA、ACO、TS优化算法的车间调度比较研究(Matlab代码实现)
188 0
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
414 4
|
6月前
|
算法 数据安全/隐私保护
基于SA模拟退火算法的多车辆TSP问题求解matlab仿真
本程序基于模拟退火(SA)算法求解多车辆旅行商问题(VRPMTS),使用MATLAB2022A实现。程序为三辆车规划最短路径,输出路线规划图与SA收敛曲线。核心代码通过迭代调整温度参数和接受概率,避免陷入局部最优,逐步逼近全局最优解。算法原理包括初始高温允许劣质解、逐步降温探索解空间,并结合邻居解生成方法优化路径。适用于物流配送、路径规划等领域,具有较高实用价值。
|
11月前
|
算法 决策智能
基于SA模拟退火优化算法的TSP问题求解matlab仿真,并对比ACO蚁群优化算法
本项目基于MATLAB2022A,使用模拟退火(SA)和蚁群优化(ACO)算法求解旅行商问题(TSP),对比两者的仿真时间、收敛曲线及最短路径长度。SA源于金属退火过程,允许暂时接受较差解以跳出局部最优;ACO模仿蚂蚁信息素机制,通过正反馈发现最优路径。结果显示SA全局探索能力强,ACO在路径优化类问题中表现优异。
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
420 1
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
414 7
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
557 8
|
存储 算法 决策智能
基于免疫算法的TSP问题求解matlab仿真
旅行商问题(TSP)是一个经典的组合优化问题,目标是寻找经过每个城市恰好一次并返回起点的最短回路。本文介绍了一种基于免疫算法(IA)的解决方案,该算法模拟生物免疫系统的运作机制,通过克隆选择、变异和免疫记忆等步骤,有效解决了TSP问题。程序使用MATLAB 2022a版本运行,展示了良好的优化效果。