Pulp求解TSP问题介绍及程序实现

简介: Pulp求解TSP问题介绍及程序实现

pulp是一个Python库,用于创建和求解线性规划和整数规划问题。它提供了一个简洁的语法,可以方便地定义决策变量、目标函数和约束条件,并调用不同的求解器来得到最优解或可行解。pulp的主要特点有:

  • 支持多种类型的决策变量,包括连续型、整数型、二进制型等。
  • 支持多种内置或外部的求解器,包括CBC、GLPK、CPLEX、Gurobi等
  • 支持导出和导入问题的LP格式或MPS格式 支持基于字典或列表的创建规划问题,适用于大规模的问题 。
  • 支持对问题进行修改和重新求解,以实现灵活的建模 pulp可以用于解决各种实际应用中的优化问题,例如生产计划问题、运输分配问题等。

pulp基础介绍

用pulp定义一个简单的问题,需要以下几个步骤:

  • 导入pulp模块 创建一个LpProblem对象,
  • 指定问题的名称和目标函数的方向(最大化或最小化) 创建一些LpVariable对象,
  • 指定变量的名称、类型和上下界
  • 添加目标函数和约束条件到LpProblem对象中
  • 调用solve方法求解问题,并打印结果
  • 例如,如果你想求解这样一个线性规划问题,可以按照如下代码实现:
import pulp
# 创建一个问题,名为"example",目标是最小化
prob = pulp.LpProblem('example', sense=pulp.LpMinimize)
# 创建两个变量,x和y,取值范围为[0, +∞)
x = pulp.LpVariable('x', lowBound=0)
y = pulp.LpVariable('y', lowBound=0)
# 添加目标函数
prob += x + 2 * y
# 添加约束条件
prob += x + y <= 4
prob += x - y >= 1
# 求解问题
status = prob.solve()
# 打印结果
print(pulp.LpStatus[status]) # Optimal
print(pulp.value(x)) # 1.5
print(pulp.value(y)) # 2.5
print(pulp.value(prob.objective)) # 6.0

pulp解决TSP问题

TSP也可以按照混合整数规划来建模,其建模是一个比较复杂的例子。对于小规模TSP问题,pulp可以快速求解。

TSP问题介绍

TSP问题,即旅行商问题,是数学领域中著名的组合优化问题之一。它的问题原型是:有一个旅行商要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是使总的旅行距离或花费最小。TSP问题可以用图论来表示:给定一个带权完全无向图,其中顶点表示城市,边表示两个城市之间的距离或花费,求一个权值最小的哈密尔顿回路。TSP问题是一个NP完全问题,也就是说没有已知的多项式时间算法可以解决它。因此,在实际应用中,大规模TSP问题,人们通常采用启发式算法或近似算法来寻找较优或次优解;小规模TSP问题可以用精确算法求解。以下介绍TSP问题用pulp搭建模型的基本求解步骤。

导入基础数据

# 导入pulp库
import pulp
# 定义城市节点和距离矩阵
dist = [[0, 10, 15, 20, 25, 30],
        [10, 0, 35, 25, 20, 15],
        [15, 35, 0 ,30 ,35 ,20],
        [20 ,25 ,30 ,0 ,15 ,30],
        [25 ,20 ,35 ,15 ,0 ,35],
        [30 ,15 ,20 ,30 ,35 ,0]]
cities=["城市"+str(i) for i in range(len(dist[0]))]

设定变量和目标

# 创建一个LpProblem对象,指定名称和目标类型
prob = pulp.LpProblem("TSP Problem", pulp.LpMinimize)
# 创建一个二维字典来存储决策变量x_ij,表示是否从i到j
x = pulp.LpVariable.dicts("x",((i,j) for i in cities for j in cities),cat=pulp.LpBinary)
# 添加目标函数,即最小化总距离
prob += pulp.lpSum(dist[cities.index(i)][cities.index(j)] * x[(i,j)]for i in cities for j in cities)

约束条件1:每个节点只能被访问一次

# 添加约束条件1:每个节点只能被访问一次
for i in cities:
    prob += pulp.lpSum(x[(i,j)] for j in cities) == 1

约束条件2:对角线不能选到

# 添加约束条件2:对角线不能选到
for i in cities:
    prob += x[(i,i)] ==0

约束条件3:每个节点只能被离开一次

# 添加约束条件3:每个节点只能被离开一次
for j in cities:
    prob += pulp.lpSum(x[(i,j)] for i in cities) == 1

约束条件:消除子回路(MTZ方法)

# 添加约束条件4:消除子回路(使用Miller-Tucker-Zemlin方法)
u = pulp.LpVariable.dicts("u", cities[1:], lowBound=0, upBound=len(cities)-1,cat=pulp.LpInteger)
for i in cities[1:]:
    for j in cities[1:]:
        if i != j:
            prob += u[i] - u[j] + (len(cities) -1) * x[(i,j)] <= len(cities) -2

求解,并整理打印结果

# 求解问题,并打印结果
status = prob.solve()
print("Status:", pulp.LpStatus[status])
print("总距离:", pulp.value(prob.objective))
data={}
dis_data=[]
for i in cities:
    for j in cities:
        if pulp.value(x[(i,j)]) == 1:
            data[i]=j
route=['城市0']
while len(route)<len(cities):
    route.append(data[route[-1]])    
route=route+['城市0']
print(route)

完整代码

导入pulp库
import pulp
# 定义城市节点和距离矩阵
dist = [[0, 10, 15, 20, 25, 30],
        [10, 0, 35, 25, 20, 15],
        [15, 35, 0 ,30 ,35 ,20],
        [20 ,25 ,30 ,0 ,15 ,30],
        [25 ,20 ,35 ,15 ,0 ,35],
        [30 ,15 ,20 ,30 ,35 ,0]]
cities=["城市"+str(i) for i in range(len(dist[0]))]
# 创建一个LpProblem对象,指定名称和目标类型
prob = pulp.LpProblem("TSP Problem", pulp.LpMinimize)
# 创建一个二维字典来存储决策变量x_ij,表示是否从i到j
x = pulp.LpVariable.dicts("x",((i,j) for i in cities for j in cities),cat=pulp.LpBinary)
# 添加目标函数,即最小化总距离
prob += pulp.lpSum(dist[cities.index(i)][cities.index(j)] * x[(i,j)]for i in cities for j in cities)
# 添加约束条件1:每个节点只能被访问一次
for i in cities:
    prob += pulp.lpSum(x[(i,j)] for j in cities) == 1
# 添加约束条件2:对角线不能选到
for i in cities:
    prob += x[(i,i)] ==0 
# 添加约束条件3:每个节点只能被离开一次
for j in cities:
    prob += pulp.lpSum(x[(i,j)] for i in cities) == 1
# 添加约束条件4:消除子回路(使用Miller-Tucker-Zemlin方法)
u = pulp.LpVariable.dicts("u", cities[1:], lowBound=0, upBound=len(cities)-1,cat=pulp.LpInteger)
for i in cities[1:]:
    for j in cities[1:]:
        if i != j:
            prob += u[i] - u[j] + (len(cities) -1) * x[(i,j)] <= len(cities) -2
# 求解问题,并打印结果
status = prob.solve()
print("Status:", pulp.LpStatus[status])
print("总距离:", pulp.value(prob.objective))
data={}
dis_data=[]
for i in cities:
    for j in cities:
        if pulp.value(x[(i,j)]) == 1:
            data[i]=j
route=['城市0']
while len(route)<len(cities):
    route.append(data[route[-1]])    
route=route+['城市0']
print(route)

如果城市变多,试一试看看代码运行时间,城市调整到17个,距离矩阵更新成17*17。data换成以下数据。

dist = [[0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354,468, 776, 662],
[548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 674,1016, 868, 1210],
[776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164,1130, 788, 1552, 754],
[696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822,1164, 560, 1358],
[582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708,1050, 674, 1244],
[274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628,514, 1050, 708],
[502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856,514, 1278, 480],
[194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320,662, 742, 856],
[308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662,320, 1084, 514],
[194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388,274, 810, 468],
[536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764,730, 388, 1152, 354],
[502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114,308, 650, 274, 844],
[388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194,536, 388, 730],
[354, 674, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0,342, 422, 536],
[468, 1016, 788, 1164, 1050, 514, 514, 662, 320, 274, 388, 650, 536,342, 0, 764, 194],
[776, 868, 1552, 560, 674, 1050, 1278, 742, 1084, 810, 1152, 274,388, 422, 764, 0, 798],
[662, 1210, 754, 1358, 1244, 708, 480, 856, 514, 468, 354, 844, 730,536, 194, 798, 0]]

不到1秒就能求解,求解速度还可以。如果TSP模型的城市数量继续增加,可能pulp无法短时间内求解,后续大家可以尝试其他启发式方法。

目录
相关文章
|
存储 算法 API
遗传算法解决经典运输问题
欢迎关注我的微信公众号:Python学习杂记
408 0
|
人工智能 供应链 程序员
# 2026智能体元年爆发:不仅是效率革命,更是六大核心行业的“基因重组”
当我们在2026年讨论Agent(智能体)时,我们不再讨论它“是什么”,而是关注它“改变了什么”。从软件开发的“端到端交付”到医疗健康的“全生命周期管理”,智能体正在从走向千行百业,将行业渗透率从15%推至全球60%。本文将深度解析智能体如何引发新的激动人心的产业革命。
232 0
|
人工智能 文字识别 自然语言处理
多模态数据信息提取解决方案测评报告
《多模态数据信息提取解决方案测评报告》概述了该方案在部署、操作界面、文档、函数模板及官方示例等方面的表现。其功能强大,涵盖OCR、NLP、物体检测等五大核心能力,适用于多种应用场景。系统运行稳定,尤其在图像识别方面表现出色,但在处理长篇文档和低质量音视频时有改进空间。尽管存在一些小问题,如配置复杂性和依赖库兼容性,整体用户体验良好,推荐给企业和开发者使用。
232 9
|
机器学习/深度学习 人工智能 数据挖掘
《C++数据降维之道:PCA 与 t - SNE 助力信息留存》
在大数据与人工智能时代,数据维度的爆炸式增长给存储、传输和处理带来了巨大挑战。数据降维技术如主成分分析(PCA)和 t-分布随机邻域嵌入(t-SNE)成为关键解决方案。本文探讨了如何在 C++ 中运用这些方法,有效减少数据维度并保留关键信息,为数据分析和机器学习提供支持。
326 19
|
供应链 监控 数据可视化
采购策略在电商供应链中的应用
随着电商的蓬勃发展,供应链管理成为提升企业竞争力的关键。本文从需求预测与库存管理、采购与供应商管理、物流与配送管理及信息技术应用四个方面,探讨了电商供应链管理的挑战与对策,特别介绍了板栗看板在供应链管理中的应用,强调其在提高供应链透明度、协同效率和响应速度上的重要作用。
|
前端开发 JavaScript C++
Marp 教程:实现幻灯片动画效果
Marp 是一个基于 Markdown 的幻灯片制作工具,结合 VSCode 的强大编辑功能,可以让你的 PPT 制作更加高效和专业。本教程详细介绍了如何在 Marp 中使用 CSS 和 JavaScript 实现幻灯片的动画效果,包括淡入、滑动、旋转等基本动画,以及交互式动画和图表动画等高级效果。通过这些技巧,你可以制作出更加生动、吸引眼球的演示文稿。
|
Java Docker 微服务
微服务架构已成为Java Web开发的新趋势,它通过将应用分解为独立、可部署的服务单元,提升了系统的灵活性与可维护性。
微服务架构已成为Java Web开发的新趋势,它通过将应用分解为独立、可部署的服务单元,提升了系统的灵活性与可维护性。每个服务负责特定功能,通过轻量通信机制协作。利用Spring Boot与Spring Cloud等框架可简化开发流程,支持模块化设计、独立部署、技术多样性和容错性,适应快速迭代的需求。
239 1
|
安全 Linux 网络安全
MS17-010永恒之蓝漏洞利用,win32安装,windows 7 32位(一)
MS17-010永恒之蓝漏洞利用,win32安装,windows 7 32位
1183 0
MS17-010永恒之蓝漏洞利用,win32安装,windows 7 32位(一)
|
Ubuntu Java Linux
开源线性规划求解器(Linear Programming solver)LP_Solve和CLP的PK
开源线性规划求解器(Linear Programming solver)LP_Solve和CLP的PK
3065 0
开源线性规划求解器(Linear Programming solver)LP_Solve和CLP的PK