You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何NetworkX的traveling_salesman_problem未返回最短路径?

问题描述

我尝试使用NetworkX的traveling_salesman_problem寻找节点间的最短路径,但返回的路径长度却大于手动规划的路径。以下是最小复现示例:

import shapely
import networkx as nx
import matplotlib.pyplot as plt

# Make a 10x10 grid

vert = shapely.geometry.MultiLineString([[(x, 0), (x, 100)] for x in range(0, 110, 10)])
hori = shapely.affinity.rotate(vert, 90)
grid = shapely.unary_union([vert, hori])

# Turn it into a graph

graph = nx.Graph()
graph.add_edges_from([(*line.coords, {"distance": line.length}) for line in grid.geoms])

# Select nodes and visit them via TSP and manually

nodes = [(20., 20.), (30., 30.), (20., 80.), (80., 20.), (50., 50.), (60., 10.), (40., 40.), (50., 40.), (50, 30)]

tsp_path = nx.approximation.traveling_salesman_problem(
    graph,
    weight="distance",
    nodes=nodes,
    cycle=False,
    method=nx.approximation.christofides
)

tsp_path = shapely.geometry.LineString(tsp_path)

manual_path = shapely.geometry.LineString([(20, 80), (50, 80), (50, 30), (40, 30), (40, 40), (40, 30), (20, 30), (20, 20), (60, 20), (60, 10), (60, 20), (80, 20)])

# Plot results

fig, axes = plt.subplots(1, 2, figsize=(10, 5), sharey=True)

for ax in axes:
    for line in grid.geoms:
        ax.plot(*line.xy, c="k", lw=.25)
    ax.scatter(*zip(*nodes), c="k")
    ax.set_aspect("equal")
axes[0].plot(*tsp_path.xy, c="b")
axes[0].set_title(f"TSP solution length={tsp_path.length}")
axes[1].plot(*manual_path.xy, c="r")
axes[1].set_title(f"manual length={manual_path.length}")

我遗漏了什么?TSP是解决此问题的错误算法吗?

返回起点的补充测试:
当我设置cycle=True让路径返回起点,并修改手动路径为:

manual_path = shapely.geometry.LineString([(20, 80), (50, 80), (50, 30), (40, 30), (40, 40), (40, 30), (20, 30), (20, 20), (60, 20), (60, 10), (60, 20), (80, 20), (80, 80), (20, 80)])

NetworkX返回的路径仍长于手动规划路径。


问题分析与解决

1. TSP的核心约束被忽略了

旅行商问题(TSP)的核心要求是:必须访问每个目标节点,且每个节点只能被访问一次(仅cycle=True时允许起点与终点重合)。但你的手动路径明显违反了这个规则——比如重复访问了(40,30)、(60,20)这些节点,通过绕路重复节点来“缩短”总长度,这已经不是TSP要解决的问题了。

NetworkX的traveling_salesman_problem严格遵循TSP约束,返回的路径会确保每个指定节点仅被访问一次,所以两者的路径没有可比性。

2. 算法场景匹配错误

如果你的需求是允许重复访问非目标节点,甚至重复访问目标节点来获取更短的总路径,那你需要的不是TSP,而是中国邮递员问题(CPP),或者基于最短路径的多节点路径规划(比如先计算所有目标节点间的最短路径,再组合成最优路径)。

3. 正确使用TSP的方式

如果你确实需要用TSP解决“访问所有目标节点各一次的最短路径”,正确步骤如下:

  • 第一步:计算所有目标节点之间在网格图中的最短路径长度,构建一个以目标节点为顶点的完全图,边权为对应节点间的最短路径长度。
  • 第二步:在这个完全图上运行TSP算法,得到目标节点的最优访问顺序。
  • 第三步:把这个顺序对应的原图最短路径拼接起来,得到最终完整路径。

示例代码片段:

# 计算目标节点间的最短路径矩阵
dist_matrix = {}
for u in nodes:
    dist_matrix[u] = {}
    for v in nodes:
        if u != v:
            dist_matrix[u][v] = nx.shortest_path_length(graph, source=u, target=v, weight="distance")
        else:
            dist_matrix[u][v] = 0

# 构建完全图
complete_graph = nx.Graph()
for u in nodes:
    for v in nodes:
        if u < v:  # 避免重复添加边
            complete_graph.add_edge(u, v, weight=dist_matrix[u][v])

# 运行TSP
tsp_order = nx.approximation.traveling_salesman_problem(complete_graph, weight="weight", cycle=False)

# 拼接原图中的最短路径
final_path = []
for i in range(len(tsp_order)-1):
    path_segment = nx.shortest_path(graph, tsp_order[i], tsp_order[i+1], weight="distance")
    final_path.extend(path_segment[:-1])  # 避免重复添加节点
final_path.append(tsp_order[-1])

final_path_line = shapely.geometry.LineString(final_path)
print(f"优化后的TSP路径长度:{final_path_line.length}")

总结

你的手动路径不符合TSP的约束条件,不能用来和TSP结果对比。如果需求是允许重复访问节点,TSP确实不是合适的算法;如果需要严格访问每个目标节点一次,应该先构建目标节点的最短路径完全图,再运行TSP。


内容的提问来源于stack exchange,提问作者user2950747

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 06:21:05