
运筹系列29:routing模型之VRPPD问题
发布日期:2021-05-08 09:44:37
浏览次数:19
分类:精选文章
本文共 5345 字,大约阅读时间需要 17 分钟。
Dial-A-Ride问题与Pickup and Delivery Problem
Dial-A-Ride问题是一种经典的运输问题,涉及将多个货物按顺序运输至指定地点。在这个问题中,每个货物都有特定的起点和终点,所有运输活动从一个中心仓库(depot)开始,依次完成货物的运输任务。
Pickup and Delivery Problem的扩展
在普通的Pickup and Delivery Problem中,我们可以在Dial-A-Ride的基础上进行扩展。具体来说,只需将货物的终点设置为仓库(depot),而货物的起点则设置为仓库即可。这种方式能够简化问题,同时保持核心功能不变。
问题描述与解决方案
为了实现这一目标,我们需要一个高效的运输安排方法。以下是实现这一目标的关键步骤:
数据准备:首先,我们需要定义问题的具体数据。包括:
- 距离矩阵:表示不同地点之间的距离。
- 运输请求:包含每个货物的起点和终点。
- 运输工具:在本例中,我们使用4辆车进行运输。
建模与优化:
- 使用OR-Tools(Google的一个开源优化工具)来构建和求解线路问题。
- 路由模型:定义每个车辆的路线,确保所有运输请求按时完成。
- 约束条件:包括货物的运输时间、距离限制等。
代码实现:
- 数据模型:创建一个包含所有必要数据的模型。
- 路由索引管理器:管理车辆和节点的索引。
- 路由模型:定义车辆的路线和运输成本。
- 运输请求:添加所有需要运输的货物。
- 优化参数:设置求解的搜索策略,以提高效率。
求解与结果分析:
- 使用指定的搜索参数求解问题。
- 输出求解结果,包括每辆车的路线和运输距离。
- 分析总距离和运输效率,确保问题得到最优解。
代码解析
以下是实现上述逻辑的代码片段:
from __future__ import print_functionfrom ortools.constraint_solver import routing_enums_pb2from ortools.constraint_solver import pywrapcpdef create_data_model(): data = {} data['distance_matrix'] = [ [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] ] data['pickups_deliveries'] = [ [1, 6], [2, 10], [4, 3], [5, 9], [7, 8], [15, 11], [13, 12], [16, 14] ] data['num_vehicles'] = 4 data['depot'] = 0 return datadef print_solution(data, manager, routing, assignment): total_distance = 0 for vehicle_id in range(data['num_vehicles']): index = routing.Start(vehicle_id) plan_output = 'Route for vehicle {}:\n'.format(vehicle_id) route_distance = 0 while not routing.IsEnd(index): plan_output += ' {} -> '.format(manager.IndexToNode(index)) previous_index = index index = assignment.Value(routing.NextVar(index)) route_distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id) plan_output += '{}\n'.format(manager.IndexToNode(index)) plan_output += 'Distance of the route: {}m\n'.format(route_distance) print(plan_output) total_distance += route_distance print('Total Distance of all routes: {}m'.format(total_distance))def main(): data = create_data_model() manager = pywrapcp.RoutingIndexManager( len(data['distance_matrix']), data['num_vehicles'], data['depot']) routing = pywrapcp.RoutingModel(manager) def distance_callback(from_index, to_index): from_node = manager.IndexToNode(from_index) to_node = manager.IndexToNode(to_index) return data['distance_matrix'][from_node][to_node] transit_callback_index = routing.RegisterTransitCallback(distance_callback) routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) dimension_name = 'Distance' routing.AddDimension( transit_callback_index, 0, 3000, True, dimension_name) distance_dimension = routing.GetDimensionOrDie(dimension_name) distance_dimension.SetGlobalSpanCostCoefficient(100) for request in data['pickups_deliveries']: pickup_index = manager.NodeToIndex(request[0]) delivery_index = manager.NodeToIndex(request[1]) routing.AddPickupAndDelivery(pickup_index, delivery_index) routing.solver().Add( routing.VehicleVar(pickup_index) == routing.VehicleVar( delivery_index)) routing.solver().Add( distance_dimension.CumulVar(pickup_index) <= distance_dimension.CumulVar(delivery_index)) search_parameters = pywrapcp.DefaultRoutingSearchParameters() search_parameters.first_solution_strategy = ( routing_enums_pb2.FirstSolutionStrategy.PARALLEL_CHEAPEST_INSERTION) assignment = routing.SolveWithParameters(search_parameters) if assignment: print_solution(data, manager, routing, assignment)if __name__ == '__main__': main()
总结
通过上述方法,我们成功实现了Dial-A-Ride问题的求解。代码中定义了问题的数据模型,构建了路由模型,并通过优化算法求解了最优路线。最终输出了每辆车的运输路线和总距离,确保了问题的最优解。
发表评论
最新留言
哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月18日 10时14分31秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
mysql连续聚合
2019-03-06
go等待N个线程完成操作总结
2019-03-06
消息队列 RocketMQ 并发量十万级
2019-03-06
ReactJs入门教程-精华版
2019-03-06
乐观锁悲观锁应用
2019-03-06
简单说说TCP三次握手、四次挥手机制
2019-03-06
.net Core 使用IHttpClientFactory请求
2019-03-06
多线程之旅(准备阶段)
2019-03-06
Python 之网络式编程
2019-03-06
MySql5.5安装步骤及MySql_Front视图配置
2019-03-06
springmvc Controller详解
2019-03-06
mybatis #{}和${}区别
2019-03-06
Java Objects工具类重点方法使用
2019-03-06
Java内存模型(JMM)
2019-03-06
AQS相关
2019-03-06
abp(net core)+easyui+efcore实现仓储管理系统——多语言(十)
2019-03-06