OR-Tools-TSP

https://developers.google.cn/optimization/routing/vrptw
模型学习中,如有理解不对希望大家帮忙指正。
下面的例子对原讲解案例中的目标有修改

VRP问题(经典旅行推销员问题和车辆路径问题)

概述

在大多数情况下,VRP都有限制:例如,车辆可能有能力承载其所能承载的最大重量或体积的物品,或者可能要求驾驶员在客户要求的指定时间窗口内访问位置。或工具可以解决多种类型的VRP,包括以下内容:

  • 商旅问题(TSP),一个经典的路线问题(只有一辆车)
  • 车辆路径问题,多车辆TSP的推广。
  • CVRP,具有容量限制的VRP,其中车辆对其可携带的物品具有最大容量。
  • VRPTWs,带有时间窗口的VRP,车辆必须在指定的时间间隔内访问这些位置。
  • VRPTW,具有资源限制的VRP,例如在车辆段(路线起点)装卸车辆的空间或人员。
  • 放弃访问的VRP,不要求车辆访问所有位置,但必须为每次放弃的访问支付罚款。

备注:车辆路径问题本质上是难以解决的:解决这些问题所需的时间随着问题的规模呈指数增长。对于特大问题,可能需要OR-Tools(或任何其他路由软件)数年才能找到最佳解决方案。OR-Tools有时返回的不是最优解。

一、TSP(商旅问题)

solve the TSP using OR-Tools

(一)问题概述

1.图片注释
蓝色圆圈是需要访问的节点,共16个;黑色圆圈起始位置,即车辆(商人)始发位置。
2.需求
车辆(商人)从0节点出发,要访问1-16所有节点。
3.数据说明
1)distance_matrix:每个节点间的距离,即距离矩阵,是一个数组,表示从节点i到节点j的距离(这里以米为单位,可由实际业务场景修改)
2)距离矩阵中的位置顺序是任意的,与访问顺序无关
3)num_vehicles:车辆数,在这里默认为1,因为这是一个TSP(对于车辆路径问题(VRP),车辆数量可以大于1)
4)depot:车场,即路线的起点和终点位置。在本例中,depot为0节点
在这里插入图片描述

(二) 代码逻辑

1.创建数据集
2.创建routing model

代码

from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp 

#################################
# 创建数据 #
#################################
def create_data_model():
    data = {}
    # 各节点之间的距离矩阵
    distance_matrix= [
        [0, 2451, 713, 1018, 1631, 1374, 2408, 213, 2571, 875, 1420, 2145, 1972],
        [2451, 0, 1745, 1524, 831, 1240, 959, 2596, 403, 1589, 1374, 357, 579],
        [713, 1745, 0, 355, 920, 803, 1737, 851, 1858, 262, 940, 1453, 1260],
        [1018, 1524, 355, 0, 700, 862, 1395, 1123, 1584, 466, 1056, 1280, 987],
        [1631, 831, 920, 700, 0, 663, 1021, 1769, 949, 796, 879, 586, 371],
        [1374, 1240, 803, 862, 663, 0, 1681, 1551, 1765, 547, 225, 887, 999],
        [2408, 959, 1737, 1395, 1021, 1681, 0, 2493, 678, 1724, 1891, 1114, 701],
        [213, 2596, 851, 1123, 1769, 1551, 2493, 0, 2699, 1038, 1605, 2300, 2099],
        [2571, 403, 1858, 1584, 949, 1765, 678, 2699, 0, 1744, 1645, 653, 600],
        [875, 1589, 262, 466, 796, 547, 1724, 1038, 1744, 0, 679, 1272, 1162],
        [1420, 1374, 940, 1056, 879, 225, 1891, 1605, 1645, 679, 0, 1017, 1200],
        [2145, 357, 1453, 1280, 586, 887, 1114, 2300, 653, 1272, 1017, 0, 504],
        [1972, 579, 1260, 987, 371, 999, 701, 2099, 600, 1162, 1200, 504, 0]
    ] 
    data['distance_matrix'] = distance_matrix
    # 车辆数
    data['num_vehicles'] = 1 
    # 车辆起始
    data['depot'] = 0 
    return data

#########################################################################
# 创建routing model #
#########################################################################
'''
作用:
1.创建索引管理器(manager)和 routing model (routing)
2.manager.IndexToNode 方法的作用是将求解器内部的索引转换为numbers for locations(下标?)
3.numbers for locations与距离矩阵的索引相对应
'''
data = create_data_model()
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
                                       data['num_vehicles'], 
                                       data['depot'])
routing = pywrapcp.RoutingModel(manager)

在这里插入图片描述

#############################################################
# 创建距离的callback函数
##############################################################
'''
作用:
1.使用routing solver,需要创建一个距离的回调函数:一个用于获取任意位置对并返回它们之间的距离的函数。最简单的方法是使用距离矩阵。
2.回调函数接收两个参数,from_index和to_index,并返回距离矩阵的相应条目
'''
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)
      

######################################################
# Set the cost of travel # 费用
######################################################

'''
作用:设置arc cost evaluator
arc cost evaluator告诉解算器如何计算任意两个位置之间的旅行成本,即问题的图形中连接它们的边(或圆弧)的成本。

在本例中,arc cost evaluator是transit_callback_index,它是解算器对距离回调函数的内部引用。本例中,任何两个地点之间的旅行成本只是它们之间的距离。成本也可能涉及其他因素。
'''
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)


######################################################
# 设置搜索参数 #
####################################################################
'''
作用:设置默认搜索参数和用于查找第一个解决方案的启发式方法
将第一个解决方案策略设置给PATH_CHEAPEST_ARC,
该策略通过重复添加权重最小的边来为求解器创建初始路由,这些边不会指向以前访问过的节点(车场除外)
'''
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
    routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)

在这里插入图片描述

####################################################################
#   添加解决方案printer #
####################################################################
'''
作用:
1.该函数返回并展示求解器的结,并从解决方案中提取路由并将其打印到console。
2.该函数通过ObjectiveValue()显示最佳路线及其距离,
'''
#
####################################################################
def print_solution(manager, routing, solution):
    print('Objective: {} meter'.format(solution.ObjectiveValue()))
    index = routing.Start(0)
    plan_output = 'Route for vehicle 0:\n'
    route_distance = 0
    while not routing.IsEnd(index):
        plan_output += ' {} ->'.format(manager.IndexToNode(index))
        previous_index = index
        index = solution.Value(routing.NextVar(index))
        route_distance += routing.GetArcCostForVehicle(previous_index, index, 0)
    plan_output += ' {}\n'.format(manager.IndexToNode(index))
    print(plan_output)
    plan_output += 'Route distance: {}miles\n'.format(route_distance)

####################################################################
# 求解并打印解决方案
# 最后,可以调用求解器并打印解决方案,返回解决方案并显示最佳路线
####################################################################
solution = routing.SolveWithParameters(search_parameters)
if solution:
    print_solution(manager, routing, solution)

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值