关于 OR-Tools

OR-Tools 是一个用于优化的开源软件套件,专为解决世界上最棘手的车辆路线、流程、整数和线性编程以及约束编程问题而设计。
使用您选择的编程语言建模您的问题后,您可以使用下面五种解法之一:Gurobi 或 CPLEX 等商业求解器,或使用开源解法器(例如 SCIP、GLPK 或 Google 的 GLOP 和获奖的 CP-SAT)。
该软件力求从一组可能的大量解决方案中找出某个问题的最佳解决方案。以下是 OR 工具可以解决的一些问题示例:

  • 车辆路线:根据给定的限制条件(例如,“这辆卡车承载的重量不能超过 20,000 磅”或“所有送货都必须在两小时内完成”)。
  • 调度:为一组复杂的任务找到最佳调度,其中一些任务需要先完成,然后才能在一组固定的机器或其他资源上执行。
  • 箱装:将各种不同尺寸的对象装入具有最大容量的固定数量的箱中。

在大多数情况下,此类问题具有大量可能的解决方案,计算机过多无法搜索所有解决方案。为了解决这个问题,OR-Tools 使用了先进的算法来缩小搜索集的范围,以寻找最佳(或最接近)的解决方案。
OR-Tools 包含以下解决方案:

  • 约束编程
    用于寻找可行解的方法,用于解决以约束条件表示的问题(例如,无法同时将房间用于两个事件,或者与剪裁内容的距离必须小于软管的长度,或者一次最多只能录制五个电视节目)。
  • 线性和混合整数编程
    Glop 线性优化器会在指定一组线性不等式(例如,为人员分配作业,或在尽量降低费用的情况下找到一组资源的最佳分配)后,找出线性目标函数的最佳值。也可以通过 Google Apps 脚本优化服务使用 Glop 和混合整数编程软件 SCIP。
  • 车辆路线
    用于在满足指定限制条件的情况下确定最佳车辆路线的专用库。
  • 图表算法
    此代码用于查找图表、最短费用流、最大流量和线性总和分配中的最短路径。

安装 OR-Tools

Google 使用 C++ 创建了 OR-Tools,但您也可以将其用于 Python、Java 或 C#(在 .NET 平台上)。

适用于 C++ 的 OR-Tools

您可以通过二进制文件分发或源代码安装 C++ 版 OR-Tools。二进制选项更简单,因此除非您计划修改源代码,否则我们建议您采用该选项。

在ubuntu上安装二进制发行版 C++ 版 OR-Tools

具体步骤参考:https://developers.google.com/optimization/install/cpp/binary_linux?hl=zh-cn#ubuntu_2

在ubuntu上源代码安装适用于 C++ 的 OR-Tools

具体步骤参考:
https://developers.google.com/optimization/install/cpp/source_linux?hl=zh-cn

使用OR-Tools解决车辆路线问题

概览

最常见的优化任务之一是车辆路由,其目标是为访问一组位置的车辆找到最佳路线。通常,“最佳”表示总距离或费用最低的路线。 以下是路由问题的一些示例:

  • 一家包裹配送公司希望为司机分配路线,以便快递员送货。
  • 一家有线电视公司希望为技术人员分配路线,以便拨打住宅服务电话。
  • 一家拼车公司希望为司机分配路线,以便他们接载乘客。

最著名的路线问题是旅行推销员问题 (TSP):对于需要到不同地点拜访客户并返回起点的销售人员,请找到最短路线。TSP 可以由图表表示,其中节点对应于位置,而边缘(或弧线)表示位置之间的直接行程。例如,下图显示的 TSP 只有四个位置,分别标记为 A、B、C 和 D。任意两个位置之间的距离由连接它们的边缘旁边的数字指定。

通过计算所有可能路线的距离,您可以看到最短的路线是 ACDBA,其总距离为 35 + 30 + 15 + 10 = 90。
位置越多,问题就越严重。上面的示例中只有六条路线。但是,如果有 10 个位置(不计算起点),路线数量为 362880。对于 20 个营业地点,该数量将跳转到 2432902008176640000。 对所有可能的路线进行详尽搜索可以保证找到最短,但对于几乎所有位置,这都缺乏计算能力。对于较大的问题,需要优化技术才能智能地搜索解决方案空间并找到最佳(或近乎最佳)解决方案。
更广义的 TSP 是车辆路由问题 (VRP),其中有多个车辆。在大多数情况下,VRP 都有限制:例如,车辆的承载能力或车辆承载能力可能取决于车辆的最大容量,或者驾驶员可能需要在客户要求的指定时间段内造访营业地点。

OR-Tools 可以解决许多类型的 VRP,其中包括

  • 旅行推销员问题 - 是一种经典的路由问题,其中只有一个车辆。
  • 车辆路由问题,即用多辆车对 TSP 进行泛化。
  • 具有容量限制的 VRP:车辆的最大承载能力。
  • 带有时间范围的 VRP:车辆必须在指定的时间间隔内到访相应的地点。
  • 具有资源限制条件的 VRP,例如空间或人员在停靠处的车辆上加载和卸载(路线的起点)。
  • 访问量下降的 VRP:在这种情况下,车辆并非必须访问所有地点,但必须对每个未到访的行程支付罚款。

旅行推销员问题

本部分将通过一个示例介绍如何为下方地图中显示的营业地点解决旅行推销员问题 (TSP)。

创建数据

下面的代码会为该问题创建数据。

struct DataModel {const std::vector<std::vector<int64_t>> 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},};const int num_vehicles = 1;const RoutingIndexManager::NodeIndex depot{0};
};

距离矩阵是一个数组,其 i、j 条目是从位置 i 到位置 j(以英里为单位)的距离,其中数组索引与位置对应的顺序如下:

0. New York - 1. Los Angeles - 2. Chicago - 3. Minneapolis - 4. Denver - 5. Dallas
- 6. Seattle - 7. Boston - 8. San Francisco - 9. St. Louis - 10. Houston - 11. Phoenix - 12. Salt Lake City

注意:距离矩阵中位置的顺序是任意的,与 TSP 的任何解决方案中的位置顺序无关。
这些数据还包括:

  • 出现问题的车辆数量,为 1,因为这是 TSP。(对于车辆路线问题 (VRP),车辆数量可以大于 1。)
  • 车站:路线的起点和终点。在本例中,仓库为 0,对应于纽约。

注意:由于路由求解器会使用整数执行所有计算,因此距离回调必须针对任意两个位置返回一个整数距离。如果 data[‘distance_matrix’] 的任何条目不是整数,则需要将矩阵条目或回调的返回值四舍五入为整数。

创建距离矩阵的其他方法
在此示例中,程序中明确定义了距离矩阵。 您也可以使用函数计算位置之间的距离:例如,平面平面上点的欧几里得公式。但是,与在运行时计算相比,预先计算位置之间的所有距离并将其存储在矩阵中会更加高效。

创建路由模型

程序主部分中的以下代码会创建索引管理器 (manager) 和路由模型 (routing)。方法 manager.IndexToNode 会将求解器的内部索引(可以放心地忽略)转换为位置的数字。位置编号对应于距离矩阵的索引。

DataModel data;
RoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles,data.depot);
RoutingModel routing(manager);

RoutingIndexManager 的输入如下:

  • 距离矩阵的行数,即位置数量(包括车站)。
  • 出现问题的车辆数量。
  • 与仓库对应的节点。

注意:在其他问题中,您可以为路由指定不同的起始位置和结束位置。为此,您需要传入两个矢量:第一个矢量包含开始位置,第二个矢量包含结束位置,而不是单个 depot。如需查看示例,请参阅为路线设置开始和结束位置。

创建距离回调

如需使用路由求解器,您需要创建一个距离(或公交)回调:该函数可接受任意一对位置并返回它们之间的距离。最简单的方法是使用距离矩阵。
以下函数会创建回调并将其作为 transit_callback_index 向求解器注册。

const int transit_callback_index = routing.RegisterTransitCallback([&data, &manager](int64_t from_index, int64_t to_index) -> int64_t {// Convert from routing variable Index to distance matrix NodeIndex.auto from_node = manager.IndexToNode(from_index).value();auto to_node = manager.IndexToNode(to_index).value();return data.distance_matrix[from_node][to_node];});

回调接受两个索引,from_index 和 to_index,并返回距离矩阵的相应条目。

设定旅行费用

arc cost evaluator 告诉求解器如何计算任意两个位置之间的行程成本——换句话说,就是在问题的图中连接它们的边(或弧)的成本。以下代码设置arc cost evaluator。

routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index);

在此示例中,弧线费用评估器是 transit_callback_index,它是求解器对距离回调的内部引用。这意味着,任何两个位置之间的行程费用只是它们之间的距离。不过,总体而言,费用还可能会涉及到其他因素。
此外,您还可以使用 routing.SetArcCostEvaluatorOfVehicle() 方法定义多个弧形评估器,以评估车辆在营业地点之间行驶的情况。例如,如果车辆的速度不同,那么您可以将不同地点之间的行程费用定义为距离除以车辆速度(也就是行程时间)。

设置搜索参数

以下代码会设置默认搜索参数和用于查找第一个解决方案的启发法:

RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters();
searchParameters.set_first_solution_strategy(FirstSolutionStrategy::PATH_CHEAPEST_ARC);

上述代码会将第一个解决方案策略设置为 PATH_CHEAPEST_ARC,它会通过添加权重最低且不指向之前访问过的节点(仓库除外)的边缘来为求解器创建初始路线。

添加解决方案打印器

显示求解器所返回解决方案的函数如下所示。 函数从解决方案中提取路线并将其输出到控制台。

//! @brief Print the solution.
//! @param[in] manager Index manager used.
//! @param[in] routing Routing solver used.
//! @param[in] solution Solution found by the solver.
void PrintSolution(const RoutingIndexManager& manager,const RoutingModel& routing, const Assignment& solution) {// Inspect solution.LOG(INFO) << "Objective: " << solution.ObjectiveValue() << " miles";int64_t index = routing.Start(0);LOG(INFO) << "Route:";int64_t distance{0};std::stringstream route;while (routing.IsEnd(index) == false) {route << manager.IndexToNode(index).value() << " -> ";int64_t previous_index = index;index = solution.Value(routing.NextVar(index));distance += routing.GetArcCostForVehicle(previous_index, index, int64_t{0});}LOG(INFO) << route.str() << manager.IndexToNode(index).value();LOG(INFO) << "Route distance: " << distance << "miles";LOG(INFO) << "";LOG(INFO) << "Advanced usage:";LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
}

该函数会显示最佳路线及其距离,该距离由 ObjectiveValue() 提供。

求解并打印解决方案

最后,您可以调用求解器并输出解决方案:

const Assignment* solution = routing.SolveWithParameters(searchParameters);
PrintSolution(manager, routing, *solution);

此操作会返回解决方案并显示最佳路线。

运行程序

运行程序时,它们会显示以下输出。

在此示例中,只有一条路由,因为它是 TSP。但在较为常见的车辆路由问题中,该解决方案包含多个路线。

将路线保存到列表或数组

作为直接输出解决方案的替代方案,您可以将路由(或 VRP 的路由)保存到列表或数组中。这样做的好处是,您可以在日后需要时利用这些路线。例如,您可以使用不同的参数多次运行程序,并将返回的解决方案中的路线保存到文件中进行比较。

以下函数会将解决方案中的路线保存为列表 (Python) 或数组 (C++) 中的任何 VRP(可能有多个车辆)。

std::vector<std::vector<int>> GetRoutes(const Assignment& solution,const RoutingModel& routing,const RoutingIndexManager& manager) {// Get vehicle routes and store them in a two dimensional array, whose// i, j entry is the node for the jth visit of vehicle i.std::vector<std::vector<int>> routes(manager.num_vehicles());// Get routes.for (int vehicle_id = 0; vehicle_id < manager.num_vehicles(); ++vehicle_id) {int64_t index = routing.Start(vehicle_id);routes[vehicle_id].push_back(manager.IndexToNode(index).value());while (!routing.IsEnd(index)) {index = solution.Value(routing.NextVar(index));routes[vehicle_id].push_back(manager.IndexToNode(index).value());}}return routes;
}

您可以使用这些函数获取“路由”部分中的任何 VRP 示例中的路由。

以下代码显示路由。

const std::vector⟨std::vector⟨int⟩⟩routes = GetRoutes(*solution,routing,manager);
// Display the routes.
for (int vehicle_id = 0; vehicle_id < routes.size(); ++vehicle_id) {LOG(INFO) << "Route " << vehicle_id;for (int j = 1; j < routes[vehicle_id].size(); ++j) {LOG(INFO) << routes[vehicle_id][j];}
}

对于当前示例,此代码会返回以下路由:

完整程序

完整的 TSP 计划如下所示。

#include <cmath>
#include <cstdint>
#include <sstream>
#include <vector>#include "ortools/constraint_solver/routing.h"
#include "ortools/constraint_solver/routing_enums.pb.h"
#include "ortools/constraint_solver/routing_index_manager.h"
#include "ortools/constraint_solver/routing_parameters.h"namespace operations_research {
struct DataModel {const std::vector<std::vector<int64_t>> 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},};const int num_vehicles = 1;const RoutingIndexManager::NodeIndex depot{0};
};//! @brief Print the solution.
//! @param[in] manager Index manager used.
//! @param[in] routing Routing solver used.
//! @param[in] solution Solution found by the solver.
void PrintSolution(const RoutingIndexManager& manager,const RoutingModel& routing, const Assignment& solution) {// Inspect solution.LOG(INFO) << "Objective: " << solution.ObjectiveValue() << " miles";int64_t index = routing.Start(0);LOG(INFO) << "Route:";int64_t distance{0};std::stringstream route;while (routing.IsEnd(index) == false) {route << manager.IndexToNode(index).value() << " -> ";int64_t previous_index = index;index = solution.Value(routing.NextVar(index));distance += routing.GetArcCostForVehicle(previous_index, index, int64_t{0});}LOG(INFO) << route.str() << manager.IndexToNode(index).value();LOG(INFO) << "Route distance: " << distance << "miles";LOG(INFO) << "";LOG(INFO) << "Advanced usage:";LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
}void Tsp() {// Instantiate the data problem.DataModel data;// Create Routing Index ManagerRoutingIndexManager manager(data.distance_matrix.size(), data.num_vehicles,data.depot);// Create Routing Model.RoutingModel routing(manager);const int transit_callback_index = routing.RegisterTransitCallback([&data, &manager](int64_t from_index, int64_t to_index) -> int64_t {// Convert from routing variable Index to distance matrix NodeIndex.auto from_node = manager.IndexToNode(from_index).value();auto to_node = manager.IndexToNode(to_index).value();return data.distance_matrix[from_node][to_node];});// Define cost of each arc.routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index);// Setting first solution heuristic.RoutingSearchParameters searchParameters = DefaultRoutingSearchParameters();searchParameters.set_first_solution_strategy(FirstSolutionStrategy::PATH_CHEAPEST_ARC);// Solve the problem.const Assignment* solution = routing.SolveWithParameters(searchParameters);// Print solution on console.PrintSolution(manager, routing, *solution);
}}  // namespace operations_researchint main(int /*argc*/, char* /*argv*/[]) {operations_research::Tsp();return EXIT_SUCCESS;
}

参考链接:link

Google的OR-Tools相关推荐

  1. Web 开发与设计之 Google 兵器谱

    Google 的使命是 Web,在 Google 眼中,未来的一切应用都将 Web 化,一直以来,Google 为 Web 开发与设计者推出了大量免费工具,让他们更好地创建,维护,改善他们的 Web ...

  2. 推荐前端性能优化辅助工具Google LightHouse (灯塔)

    Lighthouse的架构 Lighthouse的使用 通过npm安装lighthouse npm install -g lighthouse 使用lighthouse lighthouse --vi ...

  3. Google SSL zz

    // Google SSL // Modified from SSL Certificates Pro //z 2011-12-29 8:59 AM is2120@csdn : reader,cale ...

  4. google站长管理工具

    将网站提交给google: https://www.google.com/webmasters/tools/home?hl=zh-CN&pli=1 按照说明一步一步操作即可.完了后在googl ...

  5. Web开发与设计之Google兵器谱-Web开发与设计利器

    Google 的使命是 Web,在 Google 眼中,未来的一切应用都将 Web 化,一直以来,Google 为 Web 开发与设计者推出了大量免费工具,让他们更好地创建,维护,改善他们的 Web ...

  6. Google 微数据,快速指引。

    最后结果图: 即: Phodal | A geek's life. www.phodal.com/ 太白南路168号 陕西省 - 计算机工程师 - 西安文理学院 网页摘录会显示在这里.我们无法显示您网 ...

  7. 谷歌 console_使用Google Search Console有效增加网站流量的15条提示

    谷歌 console Google Search Console is a powerful free tool created by Google to help website owners un ...

  8. google的开源工程,真多

    http://blog.drhack.net/google-open-source-projects-you-knew-that/这个页面介绍了google的开源工程,真多,也很实用. Text Fi ...

  9. 如何将你的网站提交到Google

    Google 其实也无需把你的网站提交给Google,Google也能搜索到你的网站,不过时间上就很难说了,互联网的网站实在太多了,Google是依靠Spider来抓取网页的,什么时候轮到抓取你的网页 ...

  10. google adwords express使用心得

    1.Google Search Console 1.1.新版数据: 特点是简单直接,缺点是可以看的数据很有限. 链接:https://search.google.com/search-console/ ...

最新文章

  1. Angular CLI 使用教程指南参考
  2. 4.9一个简单的多任务内核实例
  3. 集成显卡+独立显卡实现三屏显示
  4. java dateTime + long
  5. 分布式ID自增算法 Snowflake
  6. QML笔记-JavaScript在QML中的使用(直接调用和间接调用)
  7. 微信养猫小程序源码搭建教程
  8. IntelliJ IDEA 运行你的第一个Java应用程序
  9. 关于Android工程师转vue的三两事儿(10)--原型与原型链
  10. java计算一个日子距离_java计算两地距离(公里)
  11. C语言中bzero函数
  12. 软件体系结构风格整理
  13. js中的设计模式之单例模式
  14. 大家的人工智能——学习路线总览
  15. 短域名Andy.ge 安迪哥的启用
  16. 如何解决“无法连接到文件共享,因为它不安全。 此共享需要旧的 SMB1 协议”问题
  17. 2021运维岗位现在薪资行情-大家运维都在薪资范围?
  18. 编程之路第11天:解决此前截屏权限需要手动点击确认问题(打开双线程)
  19. JSF学习笔记超详细,从入门到精通,持续更新中~
  20. TextRCNN、TextCNN、RNN

热门文章

  1. 对软件工程课程的希望和个人目标
  2. 阿里云李飞飞:帮1000家企业“去O”,完成10000套传统数据仓库上云
  3. Android 11 无线充电动画、铃声及问题分析
  4. TensorFlow 显存占用率高 GPU利用率低
  5. ES6 generator函数的详解
  6. 计算机专业生自荐信范文,计算机专业实习生自荐信范文
  7. CityEngine2018授权
  8. JackHttp -- 从原理来理解 HTTP
  9. 华为双屏手机计算机,华为双屏手机设计惊艳亮相 背面的屏幕是干嘛用的?
  10. 拨开云雾学习之虚拟内存