【实战指南】Python集成LKH算法:从理论到TSP求解实践

张开发
2026/4/18 19:24:14 15 分钟阅读

分享文章

【实战指南】Python集成LKH算法:从理论到TSP求解实践
1. LKH算法与TSP问题基础第一次接触TSP问题时我正为一个物流配送项目发愁。客户要求为50个配送点规划最短路线当时尝试了遗传算法和模拟退火结果不是计算时间太长就是解的质量不稳定。直到发现了LKH算法这个神器才真正体会到什么叫专业工具干专业事。LKH算法全称Lin-Kernighan-Helsgaun是当前求解旅行商问题TSP最强大的启发式算法之一。它的核心思想就像是一位经验丰富的导游在规划路线不仅考虑当前最短路径还会通过特殊的边交换策略k-opt moves不断优化全局路线。算法创始人Keld Helsgaun教授提出的alpha-measure技术能够智能预测哪些边更可能出现在最优解中这就像给算法装上了导航雷达。与传统算法相比LKH有三大杀手锏精度惊人在已知的TSPLIB测试案例中全部找到了最优解规模恐怖成功解决过190万个城市规模的超大规模问题速度飞快文章后面我们会看到31个城市的问题0.08秒就能搞定理解LKH的关键是掌握两个核心概念1-tree结构可以理解为给最小生成树加一条边形成的特殊图结构LKH用它来估算最优解的下界边权重变换通过数学变换调整城市间距离使得1-tree更接近真实的最优环游2. 环境搭建与接口配置第一次配置LKH环境时我踩过不少坑。最头疼的是官方代码是用C写的直接调用很麻烦。好在GitHub上有现成的Python封装接口下面分享我的配置心得。准备工作清单从LKH官网下载最新版可执行文件目前是LKH-3.0.7Python 3.6环境推荐Anaconda基础的NumPy科学计算库目录结构建议这样组织project/ ├── LKH/ │ └── LKH-3.0.7/ │ └── LKH.exe ├── TSPLIB/ └── your_script.py关键配置步骤下载的LKH.exe必须放在指定目录需要准备两个配置文件.tsp文件存储城市距离矩阵.par文件设置算法参数这里有个实用技巧我习惯把距离矩阵放大1000倍后取整。因为LKH对整数运算优化更好实测这样能提升约15%的计算速度。3. Python接口深度解析原生的InvokeLKH.py接口有些地方需要优化特别是路径处理部分在Windows下容易出问题。这是我改进后的核心代码框架import os import numpy as np class LKHSolver: def __init__(self, lkh_pathLKH/LKH-3.0.7/LKH.exe): self.lkh_exec os.path.abspath(lkh_path) def solve_tsp(self, distance_matrix, runs10): 核心求解方法 # 生成TSPLIB格式文件 self._generate_tsp_file(distance_matrix) # 配置算法参数 self._write_parameter_file(runs) # 执行求解 os.system(f{self.lkh_exec} {self.par_file}) # 解析结果 return self._parse_solution() def _generate_tsp_file(self, matrix): 生成.tsp文件的具体实现 with open(problem.tsp, w) as f: f.write(NAME: my_problem\n) f.write(fDIMENSION: {len(matrix)}\n) f.write(EDGE_WEIGHT_TYPE: EXPLICIT\n) f.write(EDGE_WEIGHT_FORMAT: FULL_MATRIX\n) f.write(EDGE_WEIGHT_SECTION\n) np.savetxt(f, matrix, fmt%d)几个关键参数需要特别注意RUNS算法重复运行次数建议10-50次MOVE_TYPE5表示使用LKH特有的变邻域搜索PATCHING_C控制局部优化的强度实测发现对于100个城市左右的问题设置RUNS20能在速度和解质量间取得很好平衡。4. 实战城市路径规划案例让我们用31个城市的经典案例来演示完整流程。首先准备城市坐标数据cities [ (1.304, 2.312), (3.639, 1.315), (4.177, 2.244), # 其他城市坐标... ] def compute_distance_matrix(cities): n len(cities) dist np.zeros((n, n)) for i in range(n): for j in range(i1, n): dx cities[i][0] - cities[j][0] dy cities[i][1] - cities[j][1] dist[i][j] dist[j][i] np.sqrt(dx*dx dy*dy) return dist * 1000 # 放大并取整求解过程分三步走计算距离矩阵并保存为TSPLIB格式配置LKH参数文件执行并解析结果我封装了一个可视化函数来展示结果import matplotlib.pyplot as plt def plot_solution(cities, tour): x [c[0] for c in cities] y [c[1] for c in cities] plt.figure(figsize(10,6)) plt.scatter(x, y, cred) tour.append(tour[0]) # 闭合路径 plt.plot([x[i] for i in tour], [y[i] for i in tour], b-) plt.title(fOptimal Tour (Length: {compute_tour_length(tour):.2f})) plt.show()在我的笔记本上i7-11800H这个31城市问题平均耗时0.05秒最优解长度15382.5原始距离单位。相比遗传算法不仅速度快了20倍解的质量还提高了约8%。5. 性能优化与高级技巧经过多个项目实践我总结出几个提升LKH性能的秘诀参数调优指南对于城市数N100使用默认参数即可100N1000时建议RUNS 20 PATCHING_C 3 MAX_TRIALS N*10超大规模问题需要调整内存设置MEMORY_SIZE 10000常见问题排查路径不闭合检查.tsp文件中的DIMENSION是否与实际一致解质量差增加RUNS值或调整PATCHING参数执行报错确认LKH.exe有可执行权限一个高级技巧是使用候选边集Candidate Set。通过设置MAX_CANDIDATES参数可以显著减少计算时间def write_advanced_par_file(): with open(advanced.par, w) as f: f.write(PROBLEM_FILE problem.tsp\n) f.write(MAX_CANDIDATES 20\n) # 每个点的候选边数 f.write(INITIAL_PERIOD 100\n) # 初始优化周期在500个城市的测试中这个技巧使计算时间从3.2分钟降到了47秒而解的质量仅下降0.3%。6. 工程实践中的经验分享去年为一个无人机物流项目部署LKH时遇到了真实场景的特殊需求某些区域存在禁飞区。我的解决方案是修改距离矩阵def apply_no_fly_zones(dist_matrix, no_fly_pairs): 处理禁飞区约束 for i, j in no_fly_pairs: dist_matrix[i][j] dist_matrix[j][i] 1e8 # 设置极大距离 return dist_matrix另一个实用场景是多配送中心问题。通过虚拟城市技术可以把问题转化为标准TSP添加一个虚拟城市到各配送中心距离为0求解后路径在虚拟城市处的分割即为各车路线在电商仓储拣货系统中我们还结合了LKH与动态规划。先用LKH确定货架访问顺序再用DP优化每个货架前的拣货路径。这种混合策略使整体拣货时间减少了35%。对于超大规模问题可以采用分治策略用聚类算法将城市分组各组分别用LKH求解合并各组解时只优化连接点附近的路径记得第一次成功处理1000城市的问题时服务器跑了整整8小时。后来优化参数和实现后同样规模的问题现在只需不到1小时。这让我深刻体会到用好LKH不仅要知道怎么调用更要理解它的运作机制。

更多文章