【建模算法】Python调用scikit-opt工具箱中的模拟退火算法求解TSP问题

【建模算法】Python调用scikit-opt工具箱中的模拟退火算法求解TSP问题

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

一、问题描述

​ 本案例以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

二、scikit-opt工具箱简介

scikit-opt工具箱是一个封装了7种启发式算法的 Python 代码库,包含差分进化算法、遗传算法、粒子群算法、模拟退火算法、蚁群算法、鱼群算法、免疫优化算法。官网链接:https://scikit-opt.github.io/scikit-opt/#/zh/

三、调用scikit-opt工具箱中模拟退火算法求解TSP问题的步骤

1.安装工具箱

pip install scikit-opt

2.编写自定义问题

主要是将自己问题的数据输入到封装的代码中,操作简单,修改码量很少
其中只有两行代码需要修改

num_points = 31                #num_points指需要遍历的节点个数
points_coordinate = np.random.rand(num_points, 2)    #points_coordinate是自定义问题中的节点坐标信息

3.调用模拟退火算法进行求解

其中的best_points表示最短距离的num_points点的序列(即points_coordinate矩阵中第几个点,但没有回到起点)

from sko.SA import SA_TSP

sa_tsp = SA_TSP(func=cal_total_distance, x0=range(num_points), T_max=100, T_min=1, L=10 * num_points)

best_points, best_distance = sa_tsp.run()
print(best_points, best_distance, cal_total_distance(best_points))

4.绘制近似最优路径图像

其中的best_points_ 表示从起点出发并回到起点的路径。
(其实与best_points差别就是在best_points后加入起点(best_points的第一个点))

from matplotlib.ticker import FormatStrFormatter

fig, ax = plt.subplots(1, 2)

best_points_ = np.concatenate([best_points, [best_points[0]]])
best_points_coordinate = points_coordinate[best_points_, :]
ax[0].plot(sa_tsp.best_y_history)
ax[0].set_xlabel("Iteration")
ax[0].set_ylabel("Distance")
ax[1].plot(best_points_coordinate[:, 0], best_points_coordinate[:, 1],
           marker='o', markerfacecolor='b', color='c', linestyle='-')
ax[1].xaxis.set_major_formatter(FormatStrFormatter('%.3f'))
ax[1].yaxis.set_major_formatter(FormatStrFormatter('%.3f'))
ax[1].set_xlabel("Longitude")
ax[1].set_ylabel("Latitude")
plt.show()

四、求解结果

城市序列与距离:
在这里插入图片描述
TSP图和Loss图:
在这里插入图片描述
在这里插入图片描述

动画演示:
在这里插入图片描述

五、完整源代码

python源码:

#模拟退火算法求解31座城市的TSP问题完整代码:
import numpy as np
from scipy import spatial
import matplotlib.pyplot as plt
import sys
from sko.SA import SA_TSP
from matplotlib.ticker import FormatStrFormatter
from time import perf_counter

def cal_total_distance(routine):
    '''The objective function. input routine, return total distance.
    cal_total_distance(np.arange(num_points))
    '''    
    num_points, = routine.shape
    return sum([distance_matrix[routine[i % num_points], routine[(i + 1) % num_points]] for i in range(num_points)])

def print_route(best_points):
    result_cur_best=[]
    for i in best_points:
        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

if __name__=="__main__":
    file_name = 'data.csv'
    points_coordinate = np.loadtxt(file_name, delimiter=',')
    num_points = points_coordinate.shape[0]
    distance_matrix = spatial.distance.cdist(points_coordinate, points_coordinate, metric='euclidean')
    distance_matrix = distance_matrix * 111000  # 1 degree of lat/lon ~ = 111000m,原模型将坐标数据视为经纬度数据,要进行数据转换后使用
    
    start=perf_counter()       #计时开始
    # 执行模拟退火(SA)算法
    sa_tsp = SA_TSP(func=cal_total_distance, x0=range(num_points), T_max=100, T_min=1, L=10 * num_points)  #调用工具箱
    
    # 结果输出
    best_points, best_distance = sa_tsp.run()
    print("运行时间是: {:.5f}s".format(perf_counter()-start))   #计时结束
    print("最优路线:", print_route(best_points))
    print("最优值:", cal_total_distance(best_points)/111000)   #数据还原

    best_points_ = np.concatenate([best_points, [best_points[0]]])
    best_points_coordinate = points_coordinate[best_points_, :]
    sa_tsp.best_y_history=np.array(sa_tsp.best_y_history)/111000   #数据还原

    #绘图
    #fig, ax = plt.subplots(1, 2)
    plt.rcParams['font.sans-serif'] = 'SimHei'  # 设置中文显示
    plt.rcParams['axes.unicode_minus'] = False
    fig1, ax1 = plt.subplots(1, 1)
    ax1.set_title('优化过程', loc='center')
    ax1.plot(sa_tsp.best_y_history)
    ax1.set_xlabel("迭代次数")
    ax1.set_ylabel("最优值")

    fig2, ax2 = plt.subplots(1, 1)
    ax2.set_title('轨迹图', loc='center')
    line=ax2.plot(best_points_coordinate[:, 0], best_points_coordinate[:, 1], marker='>', mec='r', mfc='w',label=u'路线')
    ax2.legend()  # 让图例生效
    for i in range(num_points):
        plt.text(best_points_coordinate[:, 0][i] + 0.05, best_points_coordinate[:, 1][i] + 0.05, str(best_points[i]+1), color='red')
    ax2.xaxis.set_major_formatter(FormatStrFormatter('%.3f'))
    ax2.yaxis.set_major_formatter(FormatStrFormatter('%.3f'))
    ax2.set_xlabel("横坐标")
    ax2.set_ylabel("纵坐标")
    plt.show()
  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
有几个常用的Python模拟退火算法库可供使用,这些库提供了实现模拟退火算法的功能和工具。以下是其一些库的示例: 1. Simulated Annealing (sa): 这个库提供了一个用于模拟退火算法的简单实现。你可以使用`pip`命令安装它:`pip install SimulatedAnnealing`。 ```python from SimulatedAnnealing import Annealer # 定义问题的目标函数和状态转移函数 def objective_function(state): # 目标函数逻辑 pass def state_transition(current_state): # 状态转移函数逻辑 pass # 创建退火实例 annealer = Annealer(objective_function, state_transition) # 运行退火算法 best_state, best_energy = annealer.anneal() ``` 2. Scipy: SciPy是一个强大的科学计算库,其包含了一个优化模块,可以使用模拟退火算法进行全局优化。你可以使用`pip`命令安装它:`pip install scipy`。 ```python from scipy.optimize import minimize # 定义问题的目标函数 def objective_function(x): # 目标函数逻辑 pass # 使用模拟退火算法进行全局优化 result = minimize(objective_function, initial_guess, method='anneal') ``` 3. PySAP: PySAP是一个用于稀疏数据重建和分析的Python库,它包含了一个模拟退火算法实现。你可以使用`pip`命令安装它:`pip install pysap`。 ```python from pysap.algorithms import annealing # 定义问题的目标函数和状态转移函数 def objective_function(x): # 目标函数逻辑 pass def state_transition(x): # 状态转移函数逻辑 pass # 运行模拟退火算法 best_state, best_energy = annealing(objective_function, state_transition) ``` 这些库都提供了模拟退火算法的功能,你可以根据自己的需求选择其之一来使用。希望能对你有帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值