运筹优化 | 模拟退火求解旅行商问题 | Python实现

"""模拟退火旅行商"""
import random
import numpy as np
import math
import time
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
location = np.loadtxt('city_location.txt') # 加载数据

'''一些关键参数'''
num_city = 30  # 城市总数
initial_t = 100  # 初始温度
lowest_t = 0.001  # 最低温度
iteration = 10000  # 设置迭代次数

'''输入两个城市之间的距离'''
def distance_mat():
    distance = [] # 初始化距离数组
    for i in range(30):
        distance_each = [] # 初始化每个城市到其他城市的距离
        for j in range(30):
            dis = math.sqrt(pow(location[i][0] - location[j][0], 2) +
                            pow(location[i][1] - location[j][1], 2)) # 计算距离
            distance_each.append(dis) # 赋值到distance_each数组
        distance.append(distance_each) # 按列添加
    return distance

'''计算所有路径对应的距离'''
def cal_newpath(distance, path):
    # 此时传入的path是一条随机生成的路径
    dis = 0
    for j in range(num_city - 1): # 只遍历0到28个城市,是因为第29个城市的下一个是起点,这样才是TSP问题,形成闭环
        dis = distance[path[j]][path[j + 1]] + dis # 这条路径上经过的两两个点的距离之和即为这条路径的长度
    dis = dis + distance[path[29]][path[0]]  # 计算的距离之和再加上第28个城市回到起点的距离
    return dis

'''点对点的距离矩阵'''
distance = distance_mat()
'''初始路径'''
path = list(range(30)) # 生成0到29的列表,即城市索引
'''初始距离'''
dis = cal_newpath(distance, path) # 先计算初始的距离,这样在模拟退火的时候才可以开始比较
'''初始温度'''
t_current = initial_t
'''灵敏度分析'''
sensibility = []

start_time = time.time() # 开始计时

'''模拟退火'''
while t_current > lowest_t:  # 外层循环:改变温度
    count_m = 0  # M的计数
    count_iter = 0  # 迭代次数计数
    while count_iter < iteration:  # 内层循环:连续多次不接受新的状态则跳出内循环
        i = 0
        j = 0
        while i == j:  # 防止随机了同一城市
            i = random.randint(1, 29)
            j = random.randint(1, 29)
        path_new = path.copy()
        path_new[i], path_new[j] = path_new[j], path_new[i]  # 任意交换两个城市的位置,产生新的路径组合
        dis_new = cal_newpath(distance, path_new) # 计算新路径的距离
        dis_delta = dis_new - dis # 计算新距离和旧距离的差值
        rand = random.random() # 生成一个0到1的浮点随机数
        exp_d = math.exp(-dis_delta / t_current) # Metropolis准则

        '''是否接受新解的过程'''
        if dis_delta < 0: # 如果新距离小于旧距离,则直接接受
            path = path_new
            dis = dis_new
        elif exp_d > rand: # 如果新距离大于旧距离,用Metropolis准则判断是否接受
            path = path_new
            dis = dis_new
        else: # 不接受新解
            count_m = count_m + 1

        count_iter = count_iter + 1 # 迭代次数加1
        sensibility.append(dis)
    
    t_current = 0.99 * t_current  # 改变温度

end_time = time.time()
elapsed_time = end_time - start_time

# 绘制最短路径的坐标图
x_coords = [location[i][0] for i in path]
y_coords = [location[i][1] for i in path]

# 添加起点到终点的连线
x_coords.append(x_coords[0])
y_coords.append(y_coords[0])

plt.figure(1)
plt.plot(x_coords, y_coords, marker='o', linestyle='-')
plt.title('最短路径坐标图')
plt.xlabel('X 坐标')
plt.ylabel('Y 坐标')
plt.grid(True)
plt.show()

plt.figure(2)
plt.plot(sensibility,marker='.',color='r',markersize=3)
plt.title('最短路径坐标图')
plt.xlabel('迭代次数')
plt.ylabel('最短距离')
plt.grid(True)
plt.show()

'''输出结果'''
print('最短距离:', dis)
print('最短路径:', path)
print('运行时间:', elapsed_time, '秒')

最短距离: 424.69177537685437
最短路径: [0, 5, 4, 3, 12, 11, 29, 22, 21, 16, 15, 28, 27, 26, 25, 24, 23, 14, 13, 7, 9, 20, 19, 18, 6, 10, 8, 2, 17, 1]
运行时间: 43.86513066291809 秒

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
旅行问题(Traveling Salesman Problem,TSP)是指给定一个城市的集合和每两个城市之间的距离,求解访问每个城市恰好一次并返回起点的最短路径。这是一个经典的组合优化问题,在计算机科学和运筹学等领域中有广泛的应用。 模拟退火算法是一种通用的随机优化算法,可以用于解决各种组合优化问题,包括旅行问题。其基本思想是模拟物质在高温下的退火过程,逐步降低温度以达到稳定状态。在算法的执行过程中,我们从一个随机解开始,通过随机扰动和接受劣解的策略,逐步向最优解靠近。 具体来说,模拟退火算法求解旅行问题的流程如下: 1. 初始化一个随机解作为当前解,并设置一个初始温度和一个终止温度。 2. 在当前温度下,对当前解进行随机扰动得到一个新解。 3. 计算新解的成本(即访问每个城市的路径长度),并计算成本差(即新解成本减去当前解成本)。 4. 如果成本差小于0,则接受新解作为当前解;否则,以一定概率接受新解(概率函数为 exp(-成本差/当前温度))。 5. 降低温度,并重复步骤2-4,直到温度降至终止温度为止。 6. 返回最终的最优解。 需要注意的是,模拟退火算法的性能受到很多因素的影响,如初始温度、温度降低速率、随机扰动方式等。因此,在实际应用中需要对这些参数进行调整以获得最佳性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值