图与网络——TSP问题精解

news/2024/12/27 14:53:51/文章来源:https://www.cnblogs.com/haohai9309/p/18410905

旅行商问题(Travelling Salesman Problem, TSP)是组合优化领域中的经典问题之一。TSP的概念最早可以追溯到18世纪,瑞士数学家欧拉在解决柯尼斯堡七桥问题时首次提出了关于图中遍历的问题。不过,作为一个优化问题,TSP在19世纪才开始形成系统的研究。1920年代,TSP被德国数学家卡尔·孟格尔首次形式化提出,他称之为"最短汉密尔顿路径问题"。1954年,数学家D.R. Fulkerson、S. Dantzig和S.M. Johnson通过线性规划的方法为TSP问题提供了新的求解思路,他们在美国兰德公司首次使用计算机对TSP问题进行求解,为TSP的现代研究奠定了基础。TSP是NP-hard问题,这意味着随着城市数量的增加,求解问题的难度呈指数级增长。

34城市TSP 漫画TSP

一、TSP问题描述

旅行推销员问题(Travelling salesman problem, 简记为TSP)是这样一个问题:给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。
1978年,波恩大学的一位数学家面临一个经典的旅行商问题(TSP),该问题的背景是在当时的西德,旅行商需要访问120个有铁路连接的城市,并找到一条最短的回路,使得旅行商经过每个城市一次并返回起点。这一问题不仅在当时具有理论研究价值,也对实际应用产生了深远影响,尤其是在交通运输、物流规划和网络设计等领域。该问题的复杂性来源于城市之间的距离以及路径选择的数量。西德的120个城市之间的距离可以通过铁路局提供的精确数据获得。在这种情况下,问题变得更加具体:在这些城市之间,如何找到一条最短的路径,使得每个城市都被访问一次,并最终回到起点?从数学角度来看,这就是一个典型的TSP问题,涉及到如何优化路径长度。由于有120个城市,这意味着每个城市之间有120x119/2=7,140条可能的连接路径(变量)。在这种规模下,TSP问题的复杂性呈现指数增长,因此不能通过简单的蛮力方法去解答。每新增一个城市,可能的路径数量都会急剧增加,这使得该问题成为了当时计算能力的重大挑战。

1.1 TSP问题的数学模型

对于有向图 \(G=(V, A)\) ,其中\(V=U \cup\{0\},|A|=|V| \times(|V|-1), c_{i j},(i, j) \in A\) 为弧泊的成本,TSP可以被分解为以下四个约束和其目标函数。首先最后一个约束指变量 \(x_{ij}\) 为二进制变量, 其等于 1 当仅当弧\(ij\)属于解回路。约束(1)和(2)用于定义闭合回路,即任意须点 \(/ \mathrm{i}\) ,解回路中必有且仅有一条弧以其作为起点的同时有且仅有一条弧以其作为终点。但是仅仅有约束 (1) 和 (2) 是不足以定义TSP,因为虽然保证了解为闭合回路却没有保证解仅含一条闭合回路、因此我们需要约束(3)以防止小圈的出现。约束 (3) 可以理解为对于任意点集合S(非\(V\)且含2个顶点及以上),必定存在至少有一个S中的顶点与S外的顶点相连接。综上, 我们可以得到以下数学模型:

\[\begin{aligned} & \operatorname{Min} \sum_{(i, j) \in A} c_{i j} x_{i j} \\ & \text { subject to } \\ & \qquad \sum_{i \in V \backslash(j\}} x_{i j}=1, j \in V \\ & \sum_{j \in V \backslash\{i\}} x_{i j}=1, i \in V \\ & \sum_{i \in S} \sum_{j \in S} x_{i j} \geq 1, S \subsetneq V,|S| \geq 2 \\ & x_{i j} \in\{0,1\},(i, j) \in A \end{aligned} \]

其中约束(3)尽管可被简化但仍使得该问题成为了NP问题。

1.2 应用场景

尽管TSP问题看似抽象,但其应用非常广泛,几乎涵盖了众多实际生活和工业领域。以下是一些典型的应用场景:

物流与运输:TSP的最直接应用是在货物配送和路径规划中。企业需要在最短的时间内将货物从仓库运送到不同的地点,规划最优的运输路线可以极大地节省成本。著名的"快递员路径问题"就是TSP的一个变种。
芯片设计与制造:在电子电路设计中,电路板上的布线问题可被看作是TSP的一种形式。芯片制造商希望找到最短的布线路径,以减少信号传输时间,提高芯片效率。
数据分析与机器学习:在聚类分析中,TSP可以用于解决数据分类和特征选择的问题。它可以帮助研究人员寻找最优的聚类中心点排列,降低数据处理中的误差。
DNA测序:TSP也应用于生物信息学领域。在DNA测序过程中,通过TSP模型可以优化基因片段的排列顺序,以减少实验步骤。
机器人路径规划:TSP在自动化领域,尤其是机器人路径规划中,也有重要应用。机器人在执行任务时需要依次访问多个工作点,而TSP可以用于设计最优访问路径,从而提升工作效率。

二、TSP问题的Python求解

2.1 示例1

给出下面6个顶点的TSP环游路径。

\(c_{ij}\) 0 1 2 3 4 5
0 0 11 8.4 5.2 4.8 2.6
1 11 0 7.4 4.2 10.2 12
2 8.4 7.4 0 3.2 6.4 11
3 5.2 4.2 3.2 0 6 7.8
4 4.8 10.2 6.4 6 0 10.2
5 2.6 12 11 7.8 10.2 0
import numpy as np
import matplotlib.pyplot as plt# Greedy TSP算法
def greedy_tsp(distance_matrix, start_point):n = len(distance_matrix)visit_mask = [False] * npath = [start_point]visit_mask[start_point] = Truecurrent_point = start_pointtotal_distance = 0  # 初始化总路径长度while len(path) < n:next_point = Nonemin_dist = float('inf')for i in range(n):if not visit_mask[i] and distance_matrix[current_point][i] < min_dist:min_dist = distance_matrix[current_point][i]next_point = ipath.append(next_point)visit_mask[next_point] = Truetotal_distance += min_dist  # 累加路径长度current_point = next_point# 返回起点,并累加最后一段的距离total_distance += distance_matrix[current_point][start_point]path.append(start_point)  return path, total_distance# 距离矩阵
distance_matrix = [[0, 11, 8.4, 5.2, 4.8, 2.6],[11, 0, 7.4, 4.2, 10.2, 12],[8.4, 7.4, 0, 3.2, 6.4, 11],[5.2, 4.2, 3.2, 0, 6, 7.8],[4.8, 10.2, 6.4, 6, 0, 10.2],[2.6, 12, 11, 7.8, 10.2, 0]
]# 开始点为0
start_point = 0
tour_path, tour_length = greedy_tsp(distance_matrix, start_point)print("访问路径:", tour_path)
print("路径总长度:", tour_length)# 绘制路径
def draw_tsp_path_with_distances(path, distance_matrix):# 生成节点的坐标,均匀分布在圆上n = len(distance_matrix)theta = np.linspace(0, 2 * np.pi, n, endpoint=False)radius = 10x = radius * np.cos(theta)y = radius * np.sin(theta)plt.figure(figsize=(12, 12))  # 图像放大至12x12# 绘制节点,扩大节点大小plt.scatter(x, y, c='lightblue', s=3000, edgecolors='black', zorder=2)  # 节点扩大3倍# 标注节点编号,扩大字号for i in range(n):plt.text(x[i], y[i], str(i), fontsize=36, ha='center', va='center', color='black')  # 字号扩大3倍# 绘制路径并标注距离for i in range(len(path) - 1):start, end = path[i], path[i+1]plt.plot([x[start], x[end]], [y[start], y[end]], 'r-', lw=6, zorder=1)  # 线宽扩大3倍# 计算线的中点,标注距离mid_x = (x[start] + x[end]) / 2mid_y = (y[start] + y[end]) / 2dist = distance_matrix[start][end]plt.text(mid_x, mid_y, f'{dist}', fontsize=30, color='blue', ha='center', va='center')  # 距离标注字号扩大3倍# 标注起点,扩大节点大小plt.scatter(x[start_point], y[start_point], c='red', s=3000, edgecolors='black', zorder=3)  # 起点扩大3倍plt.title("TSP Path with Distances (Scaled)", fontsize=24)  # 标题字号扩大plt.gca().set_aspect('equal', adjustable='box')plt.axis('off')  # 关闭坐标轴plt.show()# 绘制TSP路径
draw_tsp_path_with_distances(tour_path, distance_matrix)  # 包括最后一个回到起点的0
TSP顶点布局 路径和长度
访问路径: [0, 5, 3, 2, 4, 1, 0] ;路径总长度: 41.2

2.2 示例2

距离(公里) C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 门店
C1 - 2.6 3.7 4.1 3.1 3.5 1.7 4.2 1.7 3.5 2.7
C2 2.6 - 1.6 2.7 3.2 1.1 2.6 2.2 1.2 2.4 1.5
C3 3.7 1.6 - 1.3 2.8 0.7 3.0 0.6 2.4 1.4 1.3
C4 4.1 2.7 1.3 - 2.1 2.1 2.9 0.9 3.1 0.7 1.5
C5 3.1 3.2 2.8 2.1 - 3.2 1.4 2.8 2.7 1.5 1.8
C6 3.5 1.1 0.7 2.1 3.2 - 3.1 1.3 2.1 2.0 1.5
C7 1.7 2.6 3.0 2.9 1.4 3.1 - 3.3 1.4 2.2 1.7
C8 4.2 2.2 0.6 0.9 2.8 1.3 3.3 - 2.9 1.3 1.6
C9 1.7 1.2 2.4 3.1 2.7 2.1 1.4 2.9 - 2.5 0.9
C10 3.5 2.4 1.4 0.7 1.5 2.0 2.2 1.3 2.5 - 9.0
门店 2.7 1.5 1.4 0.7 1.5 1.5 1.7 1.6 1.6 9.0 -

基于上表提供的距离矩阵来求解TSP,并绘制相应的网络图,我们可以使用以下步骤:

TSP问题建模:根据给出的距离矩阵,构建一个完整图,其中节点表示各个城市(C1, C2, ..., C10,门店),边的权重表示城市之间的距离。
求解TSP问题:使用网络X库或其他优化算法来解决TSP问题,寻找从门店出发,经过所有城市后返回门店的最短路径。
绘制网络图:绘制图形,将门店置于中心,其他节点围绕门店排列,并标注城市之间的距离。

import numpy as np
import matplotlib.pyplot as plt# Greedy TSP算法
def greedy_tsp(distance_matrix, start_point):n = len(distance_matrix)visit_mask = [False] * npath = [start_point]visit_mask[start_point] = Truecurrent_point = start_pointtotal_distance = 0  # 初始化总路径长度while len(path) < n:next_point = Nonemin_dist = float('inf')for i in range(n):if not visit_mask[i] and distance_matrix[current_point][i] < min_dist:min_dist = distance_matrix[current_point][i]next_point = ipath.append(next_point)visit_mask[next_point] = Truetotal_distance += min_dist  # 累加路径长度current_point = next_point# 返回起点,并累加最后一段的距离total_distance += distance_matrix[current_point][start_point]path.append(start_point)  return path, total_distance# 距离矩阵
distance_matrix = [[0, 2.6, 3.7, 4.1, 3.1, 3.5, 1.7, 4.2, 1.7, 3.5, 2.7],[2.6, 0, 1.6, 2.7, 3.2, 1.1, 2.6, 2.2, 1.2, 2.4, 1.5],[3.7, 1.6, 0, 1.3, 2.8, 0.7, 3.0, 0.6, 2.4, 1.4, 1.3],[4.1, 2.7, 1.3, 0, 2.1, 2.1, 2.9, 0.9, 3.1, 0.7, 1.5],[3.1, 3.2, 2.8, 2.1, 0, 3.2, 1.4, 2.8, 2.7, 1.5, 1.8],[3.5, 1.1, 0.7, 2.1, 3.2, 0, 3.1, 1.3, 2.1, 2.0, 1.5],[1.7, 2.6, 3.0, 2.9, 1.4, 3.1, 0, 3.3, 1.4, 2.2, 1.7],[4.2, 2.2, 0.6, 0.9, 2.8, 1.3, 3.3, 0, 2.9, 1.3, 1.6],[1.7, 1.2, 2.4, 3.1, 2.7, 2.1, 1.4, 2.9, 0, 2.5, 0.9],[3.5, 2.4, 1.4, 0.7, 1.5, 2.0, 2.2, 1.3, 2.5, 0, 9.0],[2.7, 1.5, 1.3, 1.5, 1.8, 1.5, 1.7, 1.6, 0.9, 9.0, 0]
]# 开始点为“门店”(即索引10)
start_point = 10
tour_path, tour_length = greedy_tsp(distance_matrix, start_point)print("访问路径:", tour_path)
print("路径总长度:", tour_length)# 绘制路径
def draw_tsp_path_with_distances(path, distance_matrix):# 生成节点的坐标,均匀分布在圆上n = len(distance_matrix)theta = np.linspace(0, 2 * np.pi, n, endpoint=False)radius = 10x = radius * np.cos(theta)y = radius * np.sin(theta)plt.figure(figsize=(12, 12))  # 图像放大至12x12# 绘制节点,扩大节点大小plt.scatter(x, y, c='lightblue', s=3000, edgecolors='black', zorder=2)  # 节点扩大3倍# 标注节点编号,扩大字号labels = ["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", "门店"]for i in range(n):plt.text(x[i], y[i], labels[i], fontsize=16, ha='center', va='center', color='black')# 绘制路径并标注距离for i in range(len(path) - 1):start, end = path[i], path[i+1]plt.plot([x[start], x[end]], [y[start], y[end]], 'r-', lw=2, zorder=1)# 计算线的中点,标注距离mid_x = (x[start] + x[end]) / 2mid_y = (y[start] + y[end]) / 2dist = distance_matrix[start][end]plt.text(mid_x, mid_y, f'{dist}', fontsize=12, color='blue', ha='center', va='center')# 标注起点,扩大节点大小plt.scatter(x[start_point], y[start_point], c='red', s=3000, edgecolors='black', zorder=3)plt.title("TSP Path with Distances", fontsize=24)plt.gca().set_aspect('equal', adjustable='box')plt.axis('off')plt.show()# 绘制TSP路径
draw_tsp_path_with_distances(tour_path, distance_matrix)
访问路径: [10, 8, 1, 5, 2, 7, 3, 9, 4, 6, 0, 10]
路径总长度: 13.40

总结

随着计算能力的提升和优化算法的发展,TSP问题的求解已经取得了显著进展。在小规模问题(几十到几百个城市)的情况下,现代计算机和算法已经能够在合理时间内找到精确解。而对于中等规模问题(上千个城市),元启发式算法如模拟退火、遗传算法、蚁群算法等,可以在较短时间内得到接近最优解的结果。此外,研究人员也提出了一些基于特定领域的近似算法,这些算法在特定应用场景下表现出色。例如,在物流配送中,结合实际地理因素和交通情况的TSP模型可以生成更接地气的近似解,而不是仅依赖理论上的最短路径。在大规模问题(几千到上万城市)的场景下,虽然精确求解仍然极具挑战,但通过并行计算、分布式系统以及云计算的手段,可以极大地提升求解效率。近年来,随着量子计算的兴起,研究人员也开始探索如何利用量子计算加速TSP的求解。虽然量子计算的实用性还有待验证,但这一方向为未来的TSP研究带来了新的希望。TSP问题作为NP完全问题,虽然无法通过经典计算方法在多项式时间内获得全局最优解,TSP不仅是一个理论上的挑战,也是推动算法研究和实际应用发展的重要问题之一。

参考资料

  1. 算法设计分析(Kruskal构造最小生成树)
  2. 最小生成树(最小支撑树)算法

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[NLP/AIGC] 大语言模型:零一万物

1 概述:零一万物 - 首款开源中英双语大模型 公司背景公司名称:零一万物(01.AI) 创始人:李开复博士(知名投资人、创新工场董事长兼CEO)产品介绍产品名称:Yi 系列大模型Yi-6B:数据参数量为60亿的双语(英文/中文)开源模型 Yi-34B:数据参数量为340亿的双语(英文/中文)…

jackson 原生反序列化触发 getter 方法

jackson 原生反序列化触发 getter 方法jackson的POJONode方法可以任意调用getterjackson序列化会任意调用getter分析 jackson 序列化会调用任意 getter 方法,jackson 反序列化也会任意调用 getter ,这两个都不需要多说什么了,在前面的 jackson 反序列化中的 TemplatesImpl 链…

urllib发送get请求_中文传参问题

GET请求是HTTP协议中的一种基本方法,当需要在GET请求中传递中文参数时需要额外对中文进行编码(英文不需要),因为url中只能包含ascii字符。 可以使用urllib.parser.urlencode()或urllib.parse.quote()方法对中文转码。 详细查官方文档: https://docs.python.org/3.12/libra…

jackson 反序列化学习

jackson 反序列化学习 jackson 介绍 Jackson 是一个用于处理 JSON 数据的开源 Java 库。Spring MVC 的默认 json 解析器便是 Jackson。 Jackson 优点很多。 Jackson 所依赖的 jar 包较少,简单易用。与其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比…

LeetCode算法—递归

纵有疾风起;人生不言弃!一:递归 1、定义:函数直接或者间接的调用自己 2、四个要素 (1)接受的参数 (2)返回的值 (3)终止条件 (4)如何拆解 二:LeetCode 509 斐波那契数列 def func(n):if n<2:return nelse:return func(n-1)+func(n-2)n=int(input()) print(func(…

2024年9月最新Youtube转WAV高质量音频最新教程

​1.利用在线转换工具(最推荐的一种方式): YoutubeToWAV:打开浏览器,访问 https://www.youtubetowav.cc/的官方网站。在 YouTube 网站上找到您想要转换的视频,复制该视频的链接。回到网页,将复制的 YouTube 视频链接粘贴到指定的输入框中。点击Convert默认为audio标签的格…

[GDOUCTF 2023]ez_ze!

这题是一个jinja2的ssti模板注入,经过测试过滤了 _ {{}} . [] os popen getitem 输入{% print(lipsum|string|list) %}或者{% print(config|string|list) %}从这里面获取我们需要的字符 获取下划线和空格 {% set pop=dict(pop=1)|join %} {% set xia=(lipsum|string|list)|at…

dc-4

靶机下载地址:dc-4 找靶机 nmap -sV 192.168.6.0/24查看一下端口服务很明显就只是一个简单的登录框 我们爆破一下 最终得到用户名:admin 密码:happy 进去看看怎么个事很明显是一个选择既定的命令进行命令执行 我们可以抓包然后即可使用我们自己设计的命令直接拿shell然后再实现…

java方法:什么是方法?

java方法是语句的集合,它们在一起执行一个功能:方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 例如:即 ______()是方法 设计方法的原则:方法的本意时功能块,就是实现某个功能块,就是实现某个功能的语句块的集合,所以…

pediatrics_llm_qa:儿科问诊小模型

项目简介 本项目开源了基于儿科医疗指令微调的问诊模型:pediatrics_llm_qa(GitHub - jiangnanboy/pediatrics_llm_qa),目前模型的主要功能如下:智能问诊:问诊后给出诊断结果和建议。更新[2024/09/11] 开源了基于Qwen2-1.5B-instruct lora指令微调的儿科问诊模型开源模型模型…

WPF 已知问题 包含 NaN 的 Geometry 几何可能导致渲染层抛出 UCEERR_RENDERTHREADFAILURE 异常

本文记录一个 WPF 已知问题,当传入到渲染的 Geometry 几何里面包含了 NaN 数值,将可能让应用程序收到从渲染层抛上来的 UCEERR_RENDERTHREADFAILURE 异常,且此异常缺乏必要信息,比较难定位到具体错误逻辑此问题是小伙伴报告给我的,详细请看 https://github.com/dotnet/wpf…

WPF 尝试使用 WinML 做一个简单的手写数字识别应用

最近我看了微软的 AI 训练营之后,似乎有点了解 Windows Machine Learning 和 DirectML 的概念,于是我尝试实践一下,用 WPF 写一个简单的触摸手写输入的画板,再使用大佬训练好的 mnist.onnx 模型,对接 WinML 实现一个简单的手写数字识别应用最近我看了微软的 AI 训练营之后…