版权声明:转载请注明出处,谢谢合作 https://blog.csdn.net/qq_40738840/article/details/84324494
利用模拟退火算法解决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;
}