dijikstra 旅行商问题_建模案例 | 洒水车作业路径规划 (多旅行商问题)

6310a48bf6000341e93614ba45826df9.png

前言

最优化问题一直是国赛及各地区小比赛的热点问题。本题是线性规划、启发式算法的典型应用案例,结合地图类型数据,需要考虑的问题更加复杂。阅读本文前,建议读者掌握一定的编程基础、运筹学基础,并且配置好 python3 及 gurobi 环境。相关内容及其他案例可以参考以下文章:

Suranyi:gurobi 高效数学规划引擎 | python3 配置、使用及建模实例​zhuanlan.zhihu.com
34d35175866dc074cec8d0999bdb3ae1.png

1 问题描述

某城市共有绿化喷洒车20台,分为A、B两类。其中A、B类喷洒车分别有12辆、8辆,执行喷洒任务前平均部署在2个停靠点(D1,D2)。所属域内有6个给水站(Z01~ Z06)、60个喷洒作业点(F01~ F60),每一个喷洒作业点只需一台喷洒车进行一次作业。各给水站最多可以给八台喷洒车加水,不计加水时间。相关道路情况如图1所示(道路节点J01~J62),相关要素的坐标数据如“相关的要素名称及位置坐标数据”所示。图中红线主干道路,黑线是普通道路。A、B两类喷洒车在主干道路上的平均行驶速度分别是 60公里/小时、50公里/小时,在其他道路上的平均行驶速度分别是45公里/小时、30公里/小时。喷洒车装满水停靠在停靠点,接到喷洒任务后驶向喷洒作业点喷洒作业。一次喷洒作业A、B两类喷洒车分别需要用时20分钟、15分钟。每辆喷洒车完成一次喷洒任务后,需要到给水站加水再进行下次喷洒作业。

9f6354639f025954f123bf83e7c352ed.png

请建立数学模型研究下列任务相关问题:

(1) 任务一:每辆喷洒车只执行一次喷洒作业。请给出完成任务一的最短时间及相应的最优喷洒作业方案。

(2) 任务二:每辆喷洒车执行两次次喷洒作业。请给出完成任务二的最短时间及相应的最优喷洒作业方案。

(3) 任务三:完成所有60个喷洒作业点(F01~ F60)的喷洒任务。请给出完成任务三的最短时间及相应的最优喷洒作业方案。

(4) 如果在道路节点J01~J62中的某两个节点处分别增建一个给水站,请重新考虑问题(3)。并给出增建给水站的最佳位置。

2 问题分析

本题是路径调度方面的算法设计问题 (经典的多旅行商问题),与 98 年国赛灾情巡视问题十分相似。由于其背景是真实世界中的交通网道,需要结合图论知识进行相关的数据处理工作 (如邻接关系表示、获取最短路径、分群等)。在建模中,满足分配路径的前提下尽可能符合某些背景下的 “高效”,是本题的重点。

附件提供了图中各要素的坐标,并以图片形式描述点之间的连接情况。在图论中,常用邻接矩阵表示图结构。例如:

e0c35ba2ccd3b66ea717ab995c6bbbca.png

因此,可以考虑先将图中的节点连接信息转化为 0-1 矩阵

。这样的表示方法是对图形重现、计算图距离的基础。假如拥有这样的邻接矩阵,可以实现如下功能:
  • 在绘图时,根据附件中点的坐标可以标记出点的位置,再遍历矩阵中的元素,遇到
    ,则将点
    相连。
  • 在计算距离时,可以直接任意两点间的欧式距离,得到 “点 - 点 距离矩阵”。但这样的距离矩阵并不能真实反应两点之间的图距离。因此,在计算点
    与点
    的距离时,同时检查
    是否等于1,如果不等于1,则将该处的距离修改为 inf,即两点不相连。
  • 计算任意两点间的图距离,可以使用图论中的最短距离算法 (如Dijkstra、Floyd-Warshall算法)。题目中要求 “最短时间”,因此在给定两点
    求路径时,应该
    求这两点之间的最短路径

获取邻接矩阵是必要的工作,最简单的操作就是人工标注法 (在excel中填写,或代码写两层 for 循环 + if 标注)。此处要注意,由于主道与支道车速不同,需要构造两份邻接矩阵。

import pandas as pd

point_name = pd.read_excel('../table/相关的要素名称及位置坐标数据.xls', index_col='要素编号').index
link_matrix = pd.DataFrame(0, index=point_name, columns=point_name)

for start_index, start in enumerate(point_name):
    for end_index, end in enumerate(point_name[start_index + 1:]):
        if input(f"L({start}-{end}) = ") == "1":
            link_matrix.at[start, end] = link_matrix.at[end, start] = 1

link_matrix.to_excel("../table/边邻接矩阵.xlsx")

由于题目中涉及不同车型速度、作业时间不同,而最终的优化目标是耗费时间尽可能短。因此需要将 “距离” 统一转化为使用的时间,作为边权值,构造 “A车 时间矩阵”,“B车 时间矩阵”。

考虑问题的“时间最短”,可以从以下两个角度出发:

  • 总时间最短:
    ,它与平均用时最短是等价的;
  • 最长用时最短:

“最长用时最短“反映该地区全部洒水任务完成所需的最长时间最小化,这两种优化目标都是符合题意的,考虑到第一种优化目标可能造成部分车用时过长的问题,因此这里选择第二种作为第一级优化目标,第一种作为第二级优化目标。(多目标优化问题)

对于第一问、第二问,20辆车在60个地点中选择20个地点、40个地点进行洒水作业,可以考虑转化为指派问题,使用线性规划类方法进行求解;也可以使用遗传算法进行求解。由于本题规模较小,使用线性规划进行求解。

对于第三问,由于没有强制规定每辆车都需要洒水3个地点。引申出两种假设:

  • 洒水车没有洒水次数限制。即允许有的车前往作业点密集区执行多个洒水任务,有的车执行较少的洒水任务;
  • 每辆车都执行3次洒水任务。

显然,如果使用第一种假设,动态仿真的思路更容易进行;第二种假设更适合遗传算法、线性规划等静态类模型。此外,由于补水站只能为 48 次作业任务补水,而完成 60 个点作业任务需要 60 次补水。因此认为最后 20 次任务完成后返回停靠点。

对于第四问,结合题意及实际背景,在给水站充足下,洒水车可以完成任务后停留在给水站,等待第二天 (下一轮) 作业任务。题目可以转为在

这68个点中设置8个给水站,只是后面6个点是固定必选点。最佳给水站的设置应该满足整体完成任务的用时最短。因此,第一种想法是遍历取两个给水站,在给定了 8 个给水站下进行第三问的优化过程,以最终耗时作为评价指标,尽管这样会大大增加问题求解规模。结合实际考虑,洒水车往往不会跨越整个街区进行洒水作业,大多数车辆都只在局部地区进行洒水作业。因此,第二种想法便是转为图的聚类问题,设置合理的“聚类中心”,对其附近的作业点进行打包,使得整体用时 (类间距离) 尽可能小,具体求解方案在正文中展示。

3 数据预处理

3.1 获取边接邻矩阵

数据预处理的第一个工作是获取边邻接矩阵,我们采用基于人体 ATP 消耗的手工标记法进行 (找个学弟帮你画一下就行了!)。获得的标记数据分为邻接矩阵1、邻接矩阵2,前者是整个道路信息图,后者是主道图。两个矩阵按位进行异或运算即可得到支道图。

3.2 重构网络图

网络图 (节点图) 有多种绘制方式,Pyecharts、Matplotlib、python-igraph 都可以实现。这里我们使用 pygraphviz 进行绘图。快速入门及相关案例参考:

Suranyi:PyGraphviz (几何图形可视化工具) 简单入门​zhuanlan.zhihu.com
cf2074b410d0f2ea80fa5c6e1504ef24.png
import pandas as pd
import pygraphviz as pgv

link_matrix_1 = pd.read_excel("../table/边邻接矩阵1.xlsx", index_col=0)
link_matrix_2 = pd.read_excel("../table/边邻接矩阵2.xlsx", index_col=0)
point_location = pd.read_excel("../table/相关的要素名称及位置坐标数据.xls", index_col="要素编号")

# 绘图
G = pgv.AGraph(directed=False, concentrate=True)

# 添加节点
for point in point_location.index:
    color = "#5bc49f" if point.startswith("D") else "blue" if point.startswith("F") else "red" if point.startswith("J") else "#000000"

    G.add_node(point, shape="none", fontcolor=color, fixedsize=True, width=0.3, height=0.3,
               pos=f"{0.06 * point_location.at[point, 'X坐标(单位:km)']},{0.06 * (point_location.at[point, 'Y坐标(单位:km)'])}!")

# 添加边
for start in point_location.index:
    for end in point_location.index:
        # 支道
        if link_matrix_1.at[start, end] ^ link_matrix_2.at[start, end]:
            G.add_edge(start, end)
        # 主道
        if link_matrix_2.at[start, end]:
            G.add_edge(start, end, color="red", penwidth=2)

# 导出图形
G.layout()
G.draw("../image/地图.png")

05974af3b4c4089e2af302c5c32f905c.png
代码效果

3.3 获取代价矩阵

图的最短路径可以使用 Dijkstra算法实现,算法细节可以自行搜索,本文基于 python-igraph,实现更便捷的 Jgraph 模块 (快速入门参考:社区网络分析学习笔记 —— 算法实现及 igraph 介绍,本文使用的 Jgraph 见文末附录),调用该工具实现。将得到的A车代价矩阵、B车代价矩阵、距离矩阵分别记为

import pandas as pd

from Jgraph import Jgraph

# %% 1.导入数据
link_matrix_1 = pd.read_excel('../table/边邻接矩阵1.xlsx', index_col=0)  # 全网络图数据
link_matrix_2 = pd.read_excel('../table/边邻接矩阵2.xlsx', index_col=0)  # 主干道网络图数据
point_location = pd.read_excel('../table/相关的要素名称及位置坐标数据.xls', index_col='要素编号')
point_name = point_location.index

# %% 2. 距离矩阵
distance = pd.DataFrame(0.0, index=point_name, columns=point_name)

for start in point_name:
    for end in point_name:
        x1, y1 = point_location.at[start, 'X坐标(单位:km)'], point_location.at[start, 'Y坐标(单位:km)']
        x2, y2 = point_location.at[end, 'X坐标(单位:km)'], point_location.at[end, 'Y坐标(单位:km)']
        distance.at[start, end] = ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** (1 / 2)

A_matrix_1 = distance / 60  # A 车主干道时间
A_matrix_2 = distance / 45  # A 车支干道时间

B_matrix_1 = distance / 50  # B 车主干道时间
B_matrix_2 = distance / 30  # B 车支干道时间


# %% 3.最短路径矩阵
def shortest_matrix(matrix_1, matrix_2):
    nodes = [{
    "name": i} for i in matrix_1.index]
    links = []

    for start_index, start in enumerate(point_name):
        for end in point_name[start_index + 1:]:
            if link_matrix_1.at[start, end] ^ link_matrix_2.at[start, end]:
                links.append({
    "source": start, "target": end, "value": matrix_1.at[start, end]})

            if link_matrix_2.at[start, end]:
                links.append({
    "source": start, "target": end, "value": matrix_2.at[start, end]})

    # 生成 Jgraph 图,调用最短路径算法
    graph = Jgraph(nodes, links)
    paths = graph.shortest_paths(point_name, point_name, 'mult
  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值