基于模拟退火算法(SA)的TSP(Python实现)

news2024/11/19 14:31:57

        本篇文章是博主在最化优学习、人工智能等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在最优化算法:

       最优化算法(2)---《基于模拟退火算法(SA)的TSP(Python实现)》

基于模拟退火算法(SA)的TSP(Python实现)

目录

基于模拟退火算法(SA)的TSP(Python实现)

1.项目介绍      

算法介绍:

算法的核心思想包括以下几个方面:

SA算法求解TSP的基本思路包括:

2.程序代码

3.运行结果


1.项目介绍      

        基于模拟退火算法(Simulated Annealing, SA)的TSP(Traveling Salesman Problem,旅行商问题),我们涉及一种用于解决TSP的启发式优化方法。TSP是一个经典的组合优化问题,旨在寻找一条最短路径,使得旅行商可以访问每个城市恰好一次并返回起点城市。

算法介绍:

        模拟退火算法(SA)是一种经典的全局优化算法,其灵感来自固体材料的退火过程。在物理学中,高温下的固体粒子具有较高的动能,可以跳出局部能量最小值,而随着温度降低,粒子逐渐趋向稳定状态。这种原理启发了模拟退火算法的设计思想,即通过在初始阶段接受较差解的方式,逐渐减小接受较差解的概率,从而逼近全局最优解。

        在TSP问题上,SA算法通过定义合适的状态空间和能量函数,并结合退火策略,能够很好地应用于寻找最优旅行路径。初始解可以通过随机生成初始路径得到,状态空间可以定义为对当前路径进行微小的变化(如交换城市顺序),而能量函数通常是路径长度的计算。退火策略包括控制温度参数和退火速度,以在搜索空间中进行状态的变化,并逐步接近最优解。

        在SA算法求解TSP时,关键的一点是合理设置退火过程中的温度下降速率和终止条件,以确保算法能够在合理的时间内收敛到较优解。

算法的核心思想包括以下几个方面:

  1. 初始解:随机生成初始旅行路径
  2. 状态空间:定义了TSP解空间中可行解之间的相邻关系,如通过交换、翻转等操作生成新的解
  3. 能量函数:通常是TSP问题中路径长度的计算,用于评估每个解的质量
  4. 退火策略:通过控制温度参数和退火速度,在搜索空间中进行状态的变化,从而逐步接近最优解

SA算法求解TSP的基本思路包括:

  • 初始化:随机生成初始路径
  • 退火过程:在退火过程中,根据能量函数和温度参数,接受或拒绝新的解,并降低温度以逐步收敛到全局最优解
  • 终止条件:达到预设的迭代次数或满足特定条件时结束搜索,返回最优路径

        通过利用SA算法求解TSP问题,可以有效地寻找到较为优秀的旅行路线,尽管无法保证找到全局最优解,但通常能够获得接近最优解的结果。


2.程序代码

""""
题目:基于模拟退火算法的TSP
姓名:Rainbook
最终修改时间:2023.12.30
"""
import math                         # 导入模块 math
import random                       # 导入模块 random
import pandas as pd                 # 导入模块 pandas 并简写成 pd
import numpy as np                  # 导入模块 numpy 并简写成 np YouCans
import matplotlib.pyplot as plt     # 导入模块 matplotlib.pyplot 并简写成 plt

plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']  # 使用微软雅黑字体
plt.rcParams['axes.unicode_minus'] = False  # 处理负号显示异常

np.set_printoptions(precision=4)
pd.set_option('display.max_rows', 20)
pd.set_option('expand_frame_repr', False)
pd.options.display.float_format = '{:,.2f}'.format


# 子程序:初始化模拟退火算法的控制参数
def initParameter():
    tInitial = 100.0                # 设定初始退火温度(initial temperature)
    tFinal = 1                    # 设定终止退火温度(stop temperature)
    nMarkov = 1000                # Markov链长度,也即内循环运行次数
    alfa = 0.98                 # 设定降温参数,T(k)=alfa*T(k-1)
    return tInitial, tFinal, alfa, nMarkov


# 子程序:读取TSPLib数据
def read_TSPLib(fileName):
    res = []
    with open(fileName, 'r') as fid:
        for item in fid:
            if len(item.strip())!=0:
                res.append(item.split())
    loadData = np.array(res).astype('int')      # 数据格式:i Xi Yi
    City = loadData[:,1::]
    return City

# 子程序:计算各城市间的距离,得到距离矩阵
def getDistMat(nCities, City):
    distMat = np.zeros((nCities,nCities))       # 初始化距离矩阵
    for i in range(nCities):
        for j in range(i,nCities):
            # np.linalg.norm 求向量的范数(默认求 二范数),得到 i、j 间的距离
            distMat[i][j] = distMat[j][i] = round(np.linalg.norm(City[i]-City[j]))
    return distMat                              # 城市间距离取整(四舍五入)


# 子程序:计算 TSP 路径长度
def calTourMileage(tourGiven, nCities, distMat):
    mileageTour = distMat[tourGiven[nCities-1], tourGiven[0]]   # dist((n-1),0)
    for i in range(nCities-1):                                  # dist(0,1),...dist((n-2)(n-1))
        mileageTour += distMat[tourGiven[i], tourGiven[i+1]]
    return round(mileageTour)                     # 路径总长度取整(四舍五入)


# 绘制 TSP 路径图
def plot_tour(tour, value, City):
    num = len(tour)
    x0, y0 = City[tour[num - 1]]
    x1, y1 = City[tour[0]]
    plt.scatter(int(x0), int(y0), s=15, c='r')      # 绘制城市坐标点 C(n-1)
    plt.plot([x1, x0], [y1, y0], c='b')             # 绘制旅行路径 C(n-1)~C(0)
    for i in range(num - 1):
        x0, y0 = City[tour[i]]
        x1, y1 = City[tour[i + 1]]
        plt.scatter(int(x0), int(y0), s=15, c='r')  # 绘制城市坐标点 C(i)
        plt.plot([x1, x0], [y1, y0], c='b')         # 绘制旅行路径 C(i)~C(i+1)
    plt.xlabel("Total mileage of the tour:{:.1f}".format(value))
    plt.title("Optimization tour of TSP{:d}".format(num))  # 设置图形标题
    plt.show()


# 交换操作算子
def mutateSwap(tourGiven, nCities):
    # 在 [0,n) 产生 2个不相等的随机整数 i,j
    i = np.random.randint(nCities)          # 产生第一个 [0,n) 区间内的随机整数
    while True:
        j = np.random.randint(nCities)      # 产生一个 [0,n) 区间内的随机整数
        if i!=j: break                      # 保证 i, j 不相等
    tourSwap = tourGiven.copy()             # 将给定路径复制给新路径 tourSwap
    tourSwap[i], tourSwap[j] = tourGiven[j],tourGiven[i] # 交换 城市 i 和 j 的位置————简洁的实现方法
    return tourSwap


def SA_TSP(coordinates ):
    # 模拟退火算法参数设置
    tInitial,tFinal,alfa,nMarkov = initParameter()      # 调用子程序,获得设置参数
    nCities = coordinates.shape[0]              # 根据输入的城市坐标 获得城市数量 nCities
    distMat = getDistMat(nCities, coordinates)  # 调用子程序,计算城市间距离矩阵
    nMarkov = nCities                           # Markov链 的初值设置
    tNow = tInitial                          # 初始化 当前温度(current temperature)
    # 初始化准备
    tourNow  = np.arange(nCities)   # 产生初始路径,返回一个初值为0、步长为1、长度为n 的排列
    valueNow = calTourMileage(tourNow,nCities,distMat) # 计算当前路径的总长度 valueNow
    tourBest = tourNow.copy()                          # 初始化最优路径,复制 tourNow
    valueBest = valueNow                                # 初始化最优路径的总长度,复制 valueNow
    recordBest = []                                     # 初始化 最优路径记录表
    recordNow = []                                     # 初始化 最优路径记录表
    # 开始模拟退火优化过程
    iter = 0                        # 外循环迭代次数计数器
    while tNow >= tFinal:           # 外循环,直到当前温度达到终止温度时结束
        # 在当前温度下,进行充分次数(nMarkov)的状态转移以达到热平衡
        for k in range(nMarkov):    # 内循环,循环次数为Markov链长度
            # 产生新解:
            tourNew = mutateSwap(tourNow, nCities)      # 通过 交换操作 产生新路径
            valueNew = calTourMileage(tourNew,nCities,distMat) # 计算当前路径的总长度
            deltaE = valueNew - valueNow
            # 接受判别:按照 Metropolis 准则决定是否接受新解
            if deltaE < 0:                          # 更优解:如果新解的目标函数好于当前解,则接受新解
                accept = True
                if valueNew < valueBest:            # 如果新解的目标函数好于最优解,则将新解保存为最优解
                    tourBest[:] = tourNew[:]
                    valueBest = valueNew
            else:                                   # 容忍解:如果新解的目标函数比当前解差,则以一定概率接受新解
                pAccept = math.exp(-deltaE/tNow)    # 计算容忍解的状态迁移概率
                if pAccept > random.random():
                    accept = True
                else:
                    accept = False
            # 保存新解
            if accept == True:                      # 如果接受新解,则将新解保存为当前解
                tourNow[:] = tourNew[:]
                valueNow = valueNew
        # 平移当前路径,以解决变换操作避开 0,(n-1)所带来的问题,并未实质性改变当前路径。
        tourNow = np.roll(tourNow,2)                # 循环移位函数,沿指定轴滚动数组元素
        # 完成当前温度的搜索,保存数据和输出
        recordBest.append(valueBest)                # 将本次温度下的最优路径长度追加到 最优路径记录表
        recordNow.append(valueNow)                  # 将当前路径长度追加到 当前路径记录表
        print('迭代次数i:{}, 当前温度(i):{:.2f}, 当前路径长度:{:.1f}, 最优路径长度:{:.1f}'.format(iter+1, tNow, valueNow, valueBest))
        # 缓慢降温至新的温度,
        iter = iter + 1
        tNow = tNow * alfa                              # 指数降温曲线:T(k)=alfa*T(k-1)
    return tourBest


def Draw_City(City, X):
    X = list(X)
    X.append(X[0])  # 再最后再回到出发的城市
    coor_x = []
    coor_y = []
    for i in X:
        i = int(i)
        coor_x.append(City[i][0])  # 按照最优路径顺序将所有城市的x轴坐标写入coor_x中
        coor_y.append(City[i][1])

    plt.figure(1)
    for i in range(len(X) - 1):
        plt.quiver(coor_x[i], coor_y[i], coor_x[i + 1] - coor_x[i], coor_y[i + 1] - coor_y[i],
                   color='r', width=0.005, angles='xy', scale=1, scale_units='xy')
    plt.quiver(coor_x[-1], coor_y[-1], coor_x[0] - coor_x[-1], coor_y[0] - coor_y[-1],
               color='r', width=0.005, angles='xy', scale=1, scale_units='xy')
    plt.title('基于模拟退火算法的TSP')
    plt.show()

    
if __name__ == '__main__':
    # 随机生成城市信息
    nCity = 50
    City = np.random.uniform(0, 2000, [nCity, 2])  # uniform()生成nCity个二维数组,数值范围是0到2000
    # 执行算法
    Best_X = SA_TSP(City)
    # 画路径图
    Draw_City(City, Best_X)

3.运行结果


        参考资料来源:CSDN、百度搜索、维基百科等

        文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者关注VX公众号:Rain21321,联系作者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1500753.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kubernetes-2

Kubernetes学习第二天 k8s-21、Kubernetes的核心组件2、pod2.1、什么是pod 3、3种启动pod的方式3.1、命令行启动pod3.1.1、执行下面命令&#xff0c;背后发生了什么&#xff1f; 3.2、启动一个pod背后发生了什么3.3、使用yml文件3.3.1、标准的pod3.3.2、使用部署控制器启动pod3…

web服务,C/S框架,单设备登陆实现方案

背景: 原登陆接口,校验密码通过后,使用springsession记录会话信息,将信息存入在redis中 基于原逻辑进行多设备登陆开发,默认的时候多设备登陆开关开启,即按原来逻辑处理,只要密码登陆校验成功之后,都会将当前的会话信息存入redis中. 当多设备开关关闭时候,同一个账号同一时间只…

Linux运维:实现光盘开机自动挂载、配置本地yum源教程

Linux运维&#xff1a;实现光盘开机自动挂载、配置本地yum源教程 一、光盘开机自动挂载1、检查光驱设备2、创建挂载点3、编辑/etc/fstab文件4、测试挂载 二、配置本地yum源(挂载光盘或ISO文件)1、挂载ISO文件2、创建YUM仓库配置文件3、清理YUM缓存并测试 &#x1f496;The Begi…

《TCP/IP详解 卷一》第13章 TCP连接管理

目录 13.1 引言 13.2 TCP连接的建立与终止 13.2.1 TCP半关闭 13.2.2 同时打开与关闭 13.2.3 初始序列号 13.2.4 例子 13.2.5 连接建立超时 13.2.6 连接与转换器 13.3 TCP 选项 13.3.1 最大段大小选项 13.3.2 选择确认选项 13.3.3 窗口缩放选项 13.3.4 时间戳选项与…

专业140+总430+电子科技大学858信号与系统考研经验成电电子信息与通信工程,电科大,真题,大纲,参考书。

今年考研成绩出来&#xff0c;初试专业课858信号与系统140&#xff0c;总分430&#xff0c;其余各门分数都比较平稳&#xff0c;总分好于自己估分&#xff0c;应群里很多同学要求&#xff0c;我总结一下自己的复习经验。首先我是一个大冤种&#xff0c;专业课资料学长给了一套&…

【工作实践-07】uniapp关于单位rpx坑

问题&#xff1a;在浏览器页面退出登录按钮上“退出登录”字样消失&#xff0c;而在手机端页面正常;通过查看浏览器页面的HTML代码&#xff0c;发现有“退出登录”这几个字&#xff0c;只不过由于样式问题&#xff0c;这几个字被挤到看不见了。 样式代码中有一行为&#xff1a…

TQ15EG开发板教程:创建运行petalinux2019.1

工程网盘链接&#xff1a;https://pan.baidu.com/s/1vFRpzmbifXt7GypU9aKjeg 提取码&#xff1a;0ylh 首先需要使用与petalinux相同版本的vivado创建工程&#xff0c;与之前不同的是在创建硬件设计时需要勾选上添加bit文件&#xff0c;所以要在生成bit文件之后再创建硬件设计…

爬虫入门到精通_框架篇13(PySpider框架基本使用及抓取TripAdvisor实战)_PySpider下载安装,项目实战

1 PySpider框架基本用法 PySpider框架&#xff1a; 去重处理PyQuery提取错误重试多进程处理代理简洁JavaScript渲染结果监控WebUI管理 安装PySpider: pip install pyspider报错&#xff1a; 主要是async是python3.7的保留字&#xff0c;pyspider库中的有些文件与之重复而出…

仿牛客项目Day02:http、调试、日志、git

http状态码 后端调试 f8&#xff1a;逐行执行 f7&#xff1a;进入语句内部 f9&#xff1a;执行到下一个断点 前端调试 f10&#xff1a;逐行调试 f11&#xff1a;进入语句内部 f8&#xff1a;执行到下一个断点 日志 按照级别开启日志 日志的测试类 比如把application里…

如何在Windows系统部署Jellyfin Server并实现公网访问内网影音文件

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

Hypermesh2019使用Include文件

1、打开模型&#xff0c;在Includes View查看各个零部件,可以单独显示&#xff1b;最后按D进入display面板显示整个模型&#xff1b; 2、创建新的Include文件&#xff0c;并将车门移入到新创建的Include File中&#xff08;Originize includes)&#xff1b; 3、创建新的Include…

qnx启动中控屏黑屏

bmetrics_service boot metrics service, 用于记录统计启动性能信息,读取/dev/bmetrics可以获取到这些信息 # use memorydump memorydump Sets the debug cookies, copies MMU info into reset_info asinfo, sets the secure monitor(TZ) dump buffer, starts tracelogger Usa…

【好书推荐-第十期】《AI绘画教程:Midjourney使用方法与技巧从入门到精通》

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公众号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…

怎么会有这么多的Windows系统版本,傻傻分不清!

Win10的各个版本之间的区别主要体现在功能、适用场景和面向的用户群体上。以下是各版本之间的详细区别: Windows 10 家庭版(Windows 10 Home) 主要功能:包含Windows 10的基本功能,如全新的开始菜单、Edge浏览器、Windows Hello生物特征认证登录以及虚拟助手Cortana。此外,…

吴恩达deeplearning.ai:数据增强数据合成迁移学习

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 让我们看看为你的程序添加数据的技巧。在构建神经网络的时候&#xff0c;我们总是想要更多的数据&#xff0c;但是获取更多的数据往往是十分昂贵又缓慢的。相反地&#xff0c;添加数据的另一…

ssGSEA -- 学习记录

文章目录 biref统计学原理其他注意事项代码实现部分 biref 前情提要链接&#xff1a; https://blog.csdn.net/jiangshandaiyou/article/details/136536349 https://blog.csdn.net/jiangshandaiyou/article/details/134457515 相比起GSA&#xff0c;GSEA不再关注于差异基因&…

蓝色经典免费wordpress模板主题

蓝色经典配色的免费wordpress建站主题&#xff0c;万能的wordpress建站主题。 https://www.wpniu.com/themes/24.html

Java注解介绍

Java注解 注解介绍元注解RetentionTargetDocumentedInherited接口类测试结果 注解介绍 Java注解&#xff08;Annotation&#xff09;是一种元数据&#xff08;Metadata&#xff09;的形式&#xff0c;它可以被添加到Java代码中的类、方法、变量、参数等元素上&#xff0c;以提…

GSA、GSEA、ssGSEA、GSVA用到的统计学知识点

文章目录 概率密度函数&#xff08;probability density function&#xff0c;PDF&#xff09;分布函数&#xff08;Cumulative Distribution Function&#xff0c;CDF&#xff09;核密度估计&#xff08;KDE&#xff09;经验累计分布函数&#xff08;Empirical Cumulative Dis…

C++ Qt开发:QFileSystemWatcher文件监视组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QFileSystemWatcher组件实现对文件或…