TSP--模拟退火算法(c++实现+详细解释)

利用模拟退火算法解决TSP问题,能看到这篇文章的应该知道TSP是什么,在此就不赘述了,模拟退火算法思想网络上优质结束很多,因此直接讲讲如何用C++实现它

算法步骤

1.初始化:起始温度,终止温度,温度变化率,最优路径顶点集 ,最短长度S
2.利用蒙特卡洛法得到一个比较好的初始解
3.当前温度大于终止温度时,利用两点交换法在当前最优路径基础上构造一条新路径,计算新路径的长度S1,差值dE= S1 - S
4.若 dE < 0,当前最优路径更新为新路径,最短长度更新为S1
否则,任意产生一个介于0~1的概率rt,并计算exp(-dE/T),T为当前温度;若exp(-dE/T) > rt, 当前最优路径更新为新路径,最短长度更新为S1,更新温度

C++代码(内附详细注释)

#include<iostream>
using namespace std;
#include<cstdlib>
#include<ctime>
#include<vector>
#include<cmath>

//思路:
//1.初始化:起始温度,终止温度,温度变化率,最优路径顶点集 ,最短长度S 
//2.利用蒙特卡洛法得到一个比较好的初始解
//3.当前温度大于终止温度时,利用两点交换法在当前最优路径基础上构造一条新路径,计算新路径的长度S1,差值dE= S1 - S 
//4.若 dE < 0,当前最优路径更新为新路径,最短长度更新为S1
//否则,任意产生一个介于0~1的概率rt,并计算exp(-dE/T),T为当前温度;若exp(-dE/T) > rt, 当前最优路径更新为新路径,最短长度更新为S1
//更新温度 
void TSP(vector<vector<int> > W)
{
	int n = W.size();//顶点数量
	
	//----------------------初始化参数------------------- 
	 
	int startT = 3000;//初始温度
	
	double endT = 1e-8;//结束温度;科学计数法不需要头文件,字符间不允许有空格 
	
	double delta = 0.999;//温度变化率
	
	int limit =  10000;//概率选择上限,表示已经接近最优解 
	 
	//vector<vector<int> > w = W;
	
	vector<int> path;//最优路径(顶点集)
	
	int length_sum = 0;//最优路径长度和 
	
	//初始化最优路径 
	for(int i = 0; i < n; i++)
	{
		path.push_back(i);//必须使用入栈	
	} 
	//swap(path[3],path[1]);//修改可用重载 [] 
	
	
	
	//计算初始最优路径和 
	for(int i = 0; i < n - 1; i++)
	{
//		cout<<path[i]<<" ";
		
		length_sum += W[path[i]][path[i + 1]];
	}
	
	length_sum += W[path[n - 1]][path[0]];
	
//----------------使用蒙特卡洛得到一个较好的初始解---------------------- 
	vector<int> cur = path;
	
	int cur_sum = 0;
	
	for(int i = 0; i < 8000; i++)
	{
		for(int k = 0; k < n; k++)
		{
			int j = rand() % n;
			
			swap(cur[k],cur[j]);
		}
		//计算初始最优路径和 
		for(int i = 0; i < n - 1; i++)
		{
			cur_sum += W[cur[i]][cur[i + 1]];
		}
		
		cur_sum += W[cur[n - 1]][cur[0]];
		
		if(cur_sum < length_sum)
		{
			path = cur;
			
			length_sum = cur_sum;
			
		}
		
	}
//	cout<<endl<<length_sum<<endl;
	
	
	//-------------------模拟退火具体过程--------------------------------	
	
	//1.若是没有此函数,每次执行该代码产生结果都相同,默认1是种子 
	//2.有此函数,若是放在循环内部,则每次循环都设置同一个种子 
	srand((int)(time(NULL)));//确定一个随机种子
	
	//退火模拟过程 
	while(startT > endT)//控制循环次数 
	{
	//	cout<<endl<<count++<<endl;
	
//----------------构造新路径----------------------------------------
	 
		vector<int> path_new = path;//为构造一条新路径准备
		
		int length_sum_new = 0;//新的路径总和
		
		int P_L = 0;//以一定概率接受次数 
		
		//随机产生两个点,交换,得到一条新的路径 
		int x = rand() % n;
		
		int y = rand() % n;
		
		while(x == y)//直到随机产生两个互异顶点才继续向下执行 
		{
			x = rand() % n;
		
			y = rand() % n;
			
		} 
		
	//	cout<<" x:"<<x<<"  y: "<<y<<endl; 
		
		swap(path_new[x], path_new[y]);//等价于在原最优路径上随机交换两个互异顶点得到新路径
		
		//计算新路径和 
		for(int i = 0; i < n - 1; i++)
		{
	//		cout<<path_new[i]<<" ";
			
			length_sum_new += W[path_new[i]][path_new[i + 1]];
		}
		
		length_sum_new += W[path_new[n - 1]][path_new[0]];
		
	//	cout<<endl<<length_sum_new<<endl;
	
	
	
	//--------------------------比较新旧路径,取优-------------------------- 
		double dE = length_sum_new - length_sum;
		
		
		if(dE < 0)//新路径更短,直接取用 
		{
			path = path_new;
			
			length_sum = length_sum_new;
		}
		else //新路径不会更优,按一定概率接受 
		{
			double rd = rand() / (RAND_MAX + 1.0);//随机产生概率:0~1
			
			if(exp(- dE / startT) > rd )
			{
				path  = path_new;
				
				length_sum = length_sum_new;
				
				P_L++; //一定概率接受次数 
			} 
		}
		
		if(P_L == limit)break;//达到限制,直接退出 
		
		startT *= delta;//温度变化,降低 
	} 
		
//--------------------输出---------------------------------- 
	
	cout<<endl<<endl<<"--------说明:邻接矩阵顶点从 0 开始存储,而输出时顶点从 1 开始--------"<<endl<<endl;
	
	for(int i = 0; i < n - 1; i++)
	{
		cout<<"当前点序号为: "<<path[i]+1<<"   下个点序号为: "<<path[i + 1]+1<<"    权值为: "<<W[path[i]][path[i+1]]<<endl; 
	}
	cout<<"当前点序号为: "<<path[n -1]+1<<"   下个点序号为: "<<path[0]+1<< "    权值为: "<<W[path[n - 1]][path[0]]<<endl;
	cout<<endl<<"最优解:"<<length_sum<<endl;
}

int main()
{
	int n;
	vector<vector<int> > w;
	vector<int> v;
	cout<<"please input the number of vertex n:"<<endl;
	cin>>n; 
	
	//输入方法,先分解为一维向量 
	for(int i=0;i<n;i++)
	{
		w.push_back(v);
	}
	int temp;

	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			cin>>temp;
		
			w[j].push_back(temp);
		}
	}
	
	TSP(w);	
	return 0;
	
 } 
  • 14
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值