如何使用 OR 工具通过 3 个简单的步骤编写 TSP

如何使用 OR 工具通过 3 个简单的步骤编写 TSP

一个众所周知的 OR 问题的虚拟指南

Overview of all cities that have been visited by queen Beatrix (the mother of king Willem Alexander), source: wikipedia.

荷兰是欧洲西北部的一个小国,以其众多的传统而闻名。其中之一是 Kingsday,当我们庆祝威廉·亚历山大国王的生日时。大多数人将其用于在跳蚤市场出售或购买物品,在城市中漫步或参加派对。国王带着王后和他们的三个女儿访问一两个城市。这一传统始于1981年。从那时起,我们的君主访问了许多城市,如上图所示。

作为一个 OR 粉丝,这引发了以下问题。如果国王在海牙开始和结束,如何有效地访问所有这些城市?这样的问题称为旅行商问题,是一个复杂的问题。但是,可以只用三个步骤对其进行编码。这篇文章解释了如何。

免责声明 :本文中的代码计算了所有城市之间的欧几里得距离,这可能与实际距离有很大差异。

步骤 0:导入库

首先需要导入三个库:

  • Pandas 可用于将数据存储在数据框中。

  • Geopy 使我们能够找到城市的坐标。

  • 或工具 是一个来自谷歌的库,可以快速解决经典的 OR 工具。

    将熊猫导入为 pd
    从 geopy.geocoders 导入 Nominatim
    导入 geopy.distance
    从 ortools.constraint_solver 导入 routing_enums_pb2
    从 ortools.constraint_solver 导入 pywrapcp

第 1 步:找到坐标

首先,我在一个文本文件中列出了所有城市,用换行符分隔。之后,我运行下面的代码来找到对应的坐标。有时,地理定位器找不到城市。在这种情况下,我不得不手动添加坐标。最后,一个数据框包含所有城市及其坐标。

 使用 open("cities", "r") 作为 f:  
 行 = f.readlines() 地理定位器 = 名称(user_agent="TSP") df = pd.DataFrame()  
 对于行中的行:  
 城市 = line.replace("\n", "")  
 位置 = geolocator.geocode(城市)  
 row = {“City”:城市,“Lat”:location.latitude,“Lon”:location.longitude}  
 df = df.append(行,ignore_index=True) df.set_index("城市", inplace = True)

第 2 步:计算距离矩阵

使用距离矩阵求解 TSP,其中包含所有可能的城市组合之间的距离。下面的代码显示了如何计算这个矩阵,存储在数据框中。请注意,geopy 仅计算欧几里得距离。此外,它有时无法找到两个城市之间的距离。相应的单元格获取字符串“error”作为值。最后,我保存了仓库城市的索引,在本例中为海牙。

 df2 = pd.DataFrame(列=df.index,索引=df.index) 对于 df2.columns 中的 cityA:  
 coords_1 = (df.at[cityA,"Lat"], df.at[cityA,"Lon"])  
 对于 df2.index 中的 cityB:  
 coords_2 = (df.at[cityB, "Lat"], df.at[cityB, "Lon"])  
 尝试:  
 距离 = geopy.distance.geodesic(coords_1, coords_2).km  
 除了:  
 print(f"{cityA} 和 {cityB} 出错")  
 距离 = “错误”  
 df2 . 在 [ cityA , cityB ] = 距离 距离列表 = [] 对于 i,df2.iterrows() 中的行:  
 distance_list.append(row.values.tolist())  
 index_den_haag = df2.index.values.tolist().index("海牙")

第 3 步:解决 TSP

为了解决 TSP 本身,我们可以使用 OR tools,一个来自 Google 的库。我找到了以下代码 这里 并通过将海牙(荷兰语中的海牙)作为仓库进行了调整。代码的输出是行驶的总距离和城市的顺序。数字列表显示后者。要打印城市的实际名称,您可以使用第二个代码块中给出的代码。

 定义创建数据模型():  
 """存储问题的数据。"""  
 数据 = {}  
 数据['distance_matrix'] = distance_list  
 数据['num_vehicles'] = 1  
 数据['仓库'] = index_den_haag  
 返回数据 def print_solution(经理,路由,解决方案):  
 """在控制台上打印解决方案。"""  
 print('目标:{} 公里'.format(solution.ObjectiveValue()))  
 索引 = 路由。开始(0)  
 plan_output = '车辆 0 的路线:\n'  
 路线距离 = 0  
 路线列表 = [] 而不是routing.IsEnd(index):  
 route_list.append(索引)  
 plan_output += ' {} ->'.format(manager.IndexToNode(index))  
 previous_index = 索引  
 index = solution.Value(routing.NextVar(index))  
 route_distance += routing.GetArcCostForVehicle(previous_index, index, 0)  
 plan_output += ' {}\n'.format(manager.IndexToNode(index)) 打印(计划输出)  
 plan_output += '路线距离:{}公里\n'.format(route_distance) 返回路由列表 定义主():  
 """程序的入口点。"""  
 # 实例化数据问题。  
 数据 = 创建数据模型() # 创建路由索引管理器。  
 manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),  
 数据['num_vehicles'],数据['depot']) # 创建路由模型。  
 路由 = pywrapcp.RoutingModel(manager) def distance_callback(from_index, to_index):  
 """返回两个节点之间的距离。"""  
 # 从路由变量Index转换为距离矩阵NodeIndex。  
 from_node = manager.IndexToNode(from_index)  
 to_node = manager.IndexToNode(to_index)  
 返回数据['distance_matrix'][from_node][to_node]  
      
 transit_callback_index = routing.RegisterTransitCallback(distance_callback) # 定义每条弧的成本。  
 routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) # 设置第一个解决方案启发式。  
 search_parameters = pywrapcp.DefaultRoutingSearchParameters()  
 search_parameters.first_solution_strategy = (routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC) # 解决这个问题。  
 解决方案 = routing.SolveWithParameters(search_parameters) # 在控制台打印解决方案。  
 如果解决方案:  
 sol = print_solution(经理,路由,解决方案)  
 返回溶胶 溶胶 = 主要()

下面的代码打印城市的序列。请注意,我手动添加了海牙的打印声明以标记游览的结束。

 对于 sol 中的 city_nr:  
 城市​​​= df2.index.tolist()[city_nr]  
 打印(城市)  
 print("海牙")

而已!

那么,国王应该遵循的最佳路线是什么?你可以在下面找到它。希望这篇文章能帮助您解决简单的 TSP 问题,或者为您提供探索 OR 世界的灵感。让我知道是否有!

 海牙  
 席凡宁根  
 莱顿  
 卡特维克  
 维尔森  
 成熟的  
 卡兰苏格  
 安娜保洛娜  
 弗利兰  
 哈林根  
 弗兰克  
 马库姆  
 斯内克  
 埃默洛德  
 乌尔克  
 泽沃尔德  
 阿珀尔多伦  
 花瓶  
 战争  
 吉内梅登  
 梅佩尔  
 格罗宁根  
 华夫  
 洛珀苏  
 祖德拉伦  
 霍格文  
 兹沃勒  
 我们他  
 代文特  
 洛赫姆  
 聚特芬  
 杜斯堡  
 德恩  
 梅耶尔  
 刺  
 西塔  
 艾斯登  
 马斯特里赫特  
 韦尔特  
 埃因霍温  
 斯海尔托亨博斯  
 布伦  
 雷嫩  
 维嫩达尔  
 阿默斯福特  
 苏斯特戴克宫  
 阿尔梅勒  
 地面  
 阿姆斯特尔芬  
 破损  
 乌得勒支  
 豪顿  
 库伦堡  
 乌德里赫姆  
 蒂尔堡  
 布雷达  
 卑尔根奥普变焦  
 圣马丁代克  
 韦梅尔丁格  
 米德尔堡  
 维尔  
 善良  
 老北京  
 多德雷赫特  
 鹿特丹  
 海牙

更多内容在 ** 纯英语.io** .注册我们的 ** 免费每周通讯** .跟着我们 ** 推特** , ** 领英** , ** YouTube** , 和 ** 不和谐** .

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明

本文链接:https://www.qanswer.top/1768/09243017

posted @ 2022-08-30 17:10  哈哈哈来了啊啊啊  阅读(157)  评论(0编辑  收藏  举报