【建模算法】基于模拟退火算法求解TSP问题(Python实现)

【建模算法】基于模拟退火算法求解TSP问题(Python实现)

TSP (traveling salesman problem,旅行商问题)是典型的NP完全问题,即其最坏情况下的时间复杂度随着问题规模的增大按指数方式增长,到目前为止还未找到一个多项式时间的有效算法。本文探讨了基于模拟退火算法求解TSP问题的Python实现。

一、问题描述

​ 本案例以31个城市为例,假定31个城市的位置坐标如表1所列。寻找出一条最短的遍历31个城市的路径。

城市编号X坐标Y坐标城市编号X坐标Y坐标
11.3042.312173.9182.179
23.6391.315184.0612.37
34.1772.244193.782.212
43.7121.399203.6762.578
53.4881.535214.0292.838
63.3261.556224.2632.931
73.2381.229233.4291.908
84.1961.044243.5072.376
94.3120.79253.3942.643
104.3860.57263.4393.201
113.0071.97272.9353.24
122.5621.756283.143.55
132.7881.491292.5452.357
142.3811.676302.7782.826
151.3320.695312.372.975
163.7151.678

二、模拟退火算法思想

模拟退火算法的(Simulated Annealing,SA)是一种基于概率的全局寻优方法,已在理论上被证明以概率l 收敛于全局最优解。模拟退火算法模拟物理退火过程,从某一较高初温出发,随着温度的不断下降,以一定概率突跳在全局进行寻优,并最终趋于全局最优,搜索过程中趋于零概率的突跳特性可有效避免算法陷入局部最优。模拟退火算法依赖现有求解规则,是一种对已有规则进行改造的算法,它的解与初始值无关;其核心思想是以1概率接受较优解,以较小概率接受裂解(Metropolis准则)。

三、求解结果

初始路线与初始距离:

在这里插入图片描述
在这里插入图片描述
最优路线与最优值:
在这里插入图片描述
最优轨迹图:
在这里插入图片描述
四、实现代码

#模拟退火算法求解TSP问题完整代码:
#31座城市TSP问题
import math
import random
import numpy as np 
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import sys
from numpy.matlib import rand
from matplotlib.artist import getp
import copy

#构建初始参考距离矩阵
def getdistance():
    for i in range(n):
        for j in range(n):
            x = pow(city_x[i] - city_x[j], 2)
            y = pow(city_y[i] - city_y[j], 2)
            distance[i][j] = pow(x + y, 0.5)
    for i in range(n):
        for j in range(n):
            if distance[i][j] == 0:
                distance[i][j] = sys.maxsize

#计算总距离
def cacl_best(rou):
    sumdis = 0.0
    for i in range(n-1):
        sumdis += distance[rou[i]][rou[i+1]]
    sumdis += distance[rou[n-1]][rou[0]]     
    return sumdis

#得到新解
def getnewroute(route, time):
    #如果是偶数次,二变换法
    '''
    注意:数组直接复制是复制地址
    例如, current = route
    想要得到一个新的有同样内容的数组,应该用: current = copy.copy(route) 
    '''
    current = copy.copy(route)  
    
    if time % 2 == 0:
        u = random.randint(0, n-1)
        v = random.randint(0, n-1)
        temp = current[u]
        current[u] = current[v]
        current[v] = temp
    #如果是奇数次,三变换法 
    else:
        temp2 = random.sample(range(0, n), 3)
        temp2.sort()
        u = temp2[0]
        v = temp2[1]
        w = temp2[2]
        w1 = w + 1
        temp3 = [0 for col in range(v - u + 1)]
        j =0
        for i in range(u, v + 1):
            temp3[j] = current[i]
            j += 1
        
        for i2 in range(v + 1, w + 1):
            current[i2 - (v-u+1)] = current[i2]
        w = w - (v-u+1)
        j = 0
        for i3 in range(w+1, w1):
            current[i3] = temp3[j]
            j += 1
    
    return current
    
def draw(best):
    result_x = [0 for col in range(n+1)]
    result_y = [0 for col in range(n+1)]
    
    for i in range(n):
        result_x[i] = city_x[best[i]]
        result_y[i] = city_y[best[i]]
    result_x[n] = result_x[0]
    result_y[n] = result_y[0]
    plt.rcParams['font.sans-serif'] = 'SimHei'  # 设置中文显示
    plt.rcParams['axes.unicode_minus'] = False
    plt.xlim(0, 5)  # 限定横轴的范围
    plt.ylim(0, 4)  # 限定纵轴的范围
    plt.plot(result_x, result_y, marker='>', mec='r', mfc='w',label=u'路线')
    plt.legend()  # 让图例生效
    plt.margins(0)
    plt.subplots_adjust(bottom=0.15)
    for i in range(len(best)):
        plt.text(result_x[i] + 0.05, result_y[i] + 0.05, str(best[i]+1), color='red')
    plt.xlabel('横坐标')
    plt.ylabel('纵坐标')
    plt.title('轨迹图')
    plt.show()
     
def print_route(route):
    result_cur_best=[]
    for i in route:
        result_cur_best+=[i]
    for i in range(len(result_cur_best)):
        result_cur_best[i] += 1
    result_path = result_cur_best
    result_path.append(result_path[0])
    return result_path    
    
def solve():
    #得到距离矩阵
    getdistance()
    #得到初始解以及初始距离
    route = random.sample(range(0, n), n) 
    total_dis = cacl_best(route)
    print("初始路线:", print_route(route))
    print("初始距离:", total_dis)
    draw(route)
    #新解
    newroute = []
    new_total_dis = 0.0
    best = route
    best_total_dis = total_dis
    t = T0
    
    while True:
        if t <= Tend:
            break
        #令温度为初始温度
        for rt2 in range(L):
            newroute = getnewroute(route, rt2)
            new_total_dis = cacl_best(newroute)
            delt = new_total_dis - total_dis
            if delt <= 0:
                route = newroute
                total_dis = new_total_dis
                if best_total_dis > new_total_dis:
                    best = newroute
                    best_total_dis = new_total_dis
            elif delt > 0:
                p = math.exp(-delt / t)
                ranp = random.uniform(0, 1)
                if ranp < p:
                    route = newroute
                    total_dis = new_total_dis
        t = t * a
    print("现在温度为:", t)
    print("最优路线:", print_route(best))
    print("最优值:", best_total_dis)  
    draw(best)   
if __name__=="__main__":
    #读取31座城市坐标
    coord = []
    with open("data.txt", "r") as lines:
        lines = lines.readlines()
    for line in lines:
        xy = line.split()
        coord.append(xy)
    coord = np.array(coord)
    w, h = coord.shape
    coordinates = np.zeros((w, h), float)
    for i in range(w):
        for j in range(h):
            coordinates[i, j] = float(coord[i, j])
    city_x=coordinates[:,0]
    city_y=coordinates[:,1]
    #城市数量
    n = coordinates.shape[0]
    distance = [[0 for col in range(n)] for raw in range(n)]
    #初始温度 结束温度
    T0 = 31
    Tend = 1e-8
    #循环控制常数
    L = 10
    #温度衰减系数
    a = 0.98
    solve() 
  • 5
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值