旅行商问题的模拟退火解法

版权声明:欢迎转载 https://blog.csdn.net/suntengnb/article/details/80083937

问题描述:
一名商人要去n个城市推销产品,他需要经过每个城市一次且仅一次,最后回到出发的城市,问最短路径长度是多少。
数据:

20
A 2.5 4.0
B 1.2 -2.4
C 8.7 1.2
D 3.6 12.1
E -5.5 0.94
F -6.6 -12.6
G 0.18 5.219
H 12.5 14.3609
I 22.5 -5.26
J 1.61 4.5
K 2.1 -5.6
L 0 25
M 9.2 -32
N -1 7
O -5 -8
P 21 35
Q 16 7.5
R -21 5
S -7 -25.5
T 12 17.5

代码:

struct city//城市
{
    char name;//名字
    int id;//下标
    double x;
    double y;
};
int n;//城市数
vector<city> v;//路径
double dist[110][110];//邻接矩阵
double sum = 0;//路径长度
double dis(city a,city b)//计算两点间的距离
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double get_sum(vector<city> v)//获取当前路径的长度
{
    double sum = 0;
    for(int i = 1; i <= v.size() - 1; ++i)
        sum += dist[v[i].id][v[i - 1].id];
    sum += dist[v[0].id][v[v.size() - 1].id];//注意是回路
    return sum;
}
void monte_carlo()//蒙特卡洛
{
    vector<city> cur = v;
    for(int k = 1; k <= 8000; ++k)//8000次循环
    {
        for(int i = 0; i <= n - 1; ++i)
        {
            int j = rand() % n;
            swap(cur[i],cur[j]);//随机交换两个数
        }
    }
    double cur_sum = get_sum(cur);//计算路径长度
    if(cur_sum < sum)//如果比当前路径短
    {
        sum = cur_sum;//更新
        v = cur;//更新
    }
    return;
}
double end_of_t = 1e-16;//结束时的温度
double rate_of_t = 0.99999999;//温度下降的比率
double T = 1.0;//温度,初始为1.0
int cnt = 200000;//最多迭代2w次
void simulated_annealing()//模拟退火
{
    while(cnt--)
    {
        vector<city> cur = v;
        int r1 = rand() % n;
        int r2 = rand() % n;
        if(r1 == r2)//如果随机数相等
        {
            cnt++;
            continue;//重新生成
        }
        swap(cur[r1],cur[r2]);//交换
        double df = get_sum(cur) - sum;//计算路径长度的改变量
        double r3 = (rand() % 10000) / 10000.0;//随机数r3
        if(df < 0)//如果更短,则接受
        {
            v = cur;
            sum += df;// +=
        }
        else if(exp(-df / T) > r3)//否则按照一定的概率(不等式的左边)接受
        {
            v = cur;
            sum += df;// +=
        }
        T = T * rate_of_t;//温度下降
        if(T < end_of_t)//如果到达最低温度
            break;
    }
    return;
}
void print_ans()//输出答案
{
    cout << "路径长度为:" << sum << endl;
    cout << "路径为:";
    for(int i = 0; i <= n - 1; ++i)
        cout << v[i].name << ' ';
    cout << endl;
    return;
}
int main()
{
    cin >> n;
    srand((unsigned int)time(NULL));//初始化随机数种子
    for(int i = 0; i <= n - 1; ++i)
    {
        city t;
        cin >> t.name >> t.x >> t.y;
        t.id = i;
        v.push_back(t);
    }
    for(int i = 0; i <= n - 2; ++i)//求出邻接矩阵
        for(int j = i + 1; j <= n - 1; ++j)
            dist[i][j] = dist[j][i] = dis(v[i],v[j]);
    sum = get_sum(v);//初始路径长度
    monte_carlo();//生成初始解
    simulated_annealing();//模拟退火
    print_ans();//输出答案
    return 0;
}

解决方法:
模拟退火算法

猜你喜欢

转载自blog.csdn.net/suntengnb/article/details/80083937