Google OR 工具 - 火车调度问题

2023-11-24

我试图解决的问题有点像这里的员工调度问题:

https://github.com/google/or-tools/blob/master/examples/python/shift_scheduling_sat.py

然而,有一些事情我被困住了,不知道如何将其合并到代码中。下面我就这个问题进行解释。

Problem

我有一个由 47 趟列车组成的车队,我希望每天将它们分配给 49 条路线。列车应受以下约束:

  1. 每趟列车在白天必须至少使用一次(任何列车不得全天闲置)

  2. 每趟列车必须分配至至少一条路线(最多两条路线)且必须覆盖每条路线

  3. 列车分配路线后的最终里程不得超过24800(即前一天累计里程+分配路线里程

  4. 列车一天内分配两条线路的,两条线路的时间不得重叠

我想要的另一个约束,但并不珍贵的是(假设这是一个软约束):

  1. 前一天里程数高的列车应安排在短途路线上,前一天里程数少的列车应安排在长途路线上

我有一个数据框trains看起来像这样。我可以随机选择一个日期,查看 47 趟列车中每趟列车截至前一天(即 2018 年 9 月 18 日)的累计里程:

Date      |  Day      |   Train   |  Cum_mileage_prev_day 
----------| --------- | --------- |----------------------  
19/9/18   |  WED      |   T32     |  24,300          
19/9/18   |  WED      |   T11     |  24,200
19/9/18   |  WED      |   T38     |  24,200       
 .          .               .         .            
 .          .               .         .            
19/9/18   |  WED      |   T28     |  600  
19/9/18   |  WED      |   T15     |  200   
19/9/18   |  WED      |   T24     |  100  

和一个数据框routes看起来像这样。请注意,100 公里以上的路线被定义为长路线,低于 100 公里的路线被定义为短路线。在 49 条路线中,只有 6 条路线较短(10 公里) - 请注意,下面仅显示了其中的 5 条路线:

Route  |  Start    |   End    |  Total_km   | route_type
------ | --------- | ---------|-------------|-------------   
R11    |  5:00     | 00:00    |  700        | Long    
R32    |  6:00     | 00:50    |  600        | Long   
R16    |  5:20     | 23:40    |  600        | Long   
 .          .           .         .            .
 .          .           .         .            .
R41    |  11:15    | 12:30    |   10        | Short 
R42    |  11:45    | 13:00    |   10        | Short
R43    |  12:15    | 13:30    |   10        | Short 
R44    |  12:45    | 14:00    |   10        | Short
R45    |  13:20    | 14:35    |   10        | Short 

我想要的结果是这样的,其中火车已被分配 1 或 2 条路线,并且显示当天结束时的总里程(假设分配的路线由火车完成):

Date   |  Day  |   Train|  Cum_mil_prev_day | first_assign | second_assign | total_km_day_end
-------| ------| -------|-------------------|--------------|---------------|----------------
19/9/18|  WED  |   T32  |  24,300           | R41          | R44           | 24,320 
19/9/18|  WED  |   T11  |  24,200           | R42          | R45           | 24,220
19/9/18|  WED  |   T38  |  24,200           | R43          |               | 24,210
 .          .               .         .                  .              .
 .          .               .         .                  .              .
19/9/18|  WED  |   T28  |  600              | R11          |               | 1300
19/9/18|  WED  |   T15  |  200              | R32          |               | 800
19/9/18|  WED  |   T24  |  100              | R16          |               | 700

编辑/更新 (2/8/19):

(注意:下面的代码显示了该问题的简化版本,其中 6 趟列车分配给 8 条路线。我还在代码中包含了约束 5。)

非常感谢斯特拉迪瓦里和洛朗对这一作品的帮助。

from itertools import combinations
from ortools.sat.python import cp_model


def test_overlap(t1_st, t1_end, t2_st, t2_end):

    def convert_to_minutes(t_str):
        hours, minutes = t_str.split(':')
        return 60*int(hours)+int(minutes)

    t1_st = convert_to_minutes(t1_st)
    t1_end = convert_to_minutes(t1_end)
    t2_st = convert_to_minutes(t2_st)
    t2_end = convert_to_minutes(t2_end)

    # Check for wrapping time differences
    if t1_end < t1_st:
        if t2_end < t2_st:
        # Both wrap, therefore they overlap at midnight
            return True
        # t2 doesn't wrap. Therefore t1 has to start after t2 and end before
        return t1_st < t2_end or t2_st < t1_end

    if t2_end < t2_st:
        # only t2 wraps. Same as before, just reversed
        return t2_st < t1_end or t1_st < t2_end

    # They don't wrap and the start of one comes after the end of the other,
    # therefore they don't overlap
    if t1_st >= t2_end or t2_st >= t1_end:
        return False
    # In all other cases, they have to overlap
    return True



def main():
    model = cp_model.CpModel()
    solver = cp_model.CpSolver()

    # data
    route_km = {
        'R11': 700,
        'R32': 600,
        'R16': 600,
        'R41': 10,
        'R42': 10,
        'R43': 10,
        'R44': 10,
        'R45': 10}


    train_cum_km = {
        'T32': 24_300,
        'T11': 24_200,
        'T38': 24_200,
        'T28': 600,
        'T15': 200,
        'T24': 100}


    route_times = {
        'R11': ('05:00', '00:00'),
        'R32': ('06:00', '00:50'),
        'R16': ('05:20', '23:40'),
        'R41': ('11:15', '12:30'),
        'R42': ('11:45', '13:00'),
        'R43': ('12:15', '13:30'),
        'R44': ('12:45', '14:00'),
        'R45': ('13:20', '14:35')}



    trains = list(train_cum_km.keys())
    routes = list(route_km.keys())
    num_trains = len(trains)
    num_routes = len(routes)

    assignments = {(t, r): model.NewBoolVar('assignment_%s%s' % (t, r))
               for t in trains for r in routes}


    # constraint 1: each train must be used
    for r in routes:
        model.Add(sum(assignments[(t, r)] for t in trains) == 1)

    # constraint 2: each train must do at least one (max two) routes
    for t in trains:
        model.Add(sum(assignments[(t, r)] for r in routes) >= 1)
        model.Add(sum(assignments[(t, r)] for r in routes) <= 2)


    # constraint 3: ensure the end of day cum km is less than 24_800
    # create a new variable which must be in the range (0,24_800)
    day_end_km = {
        t: model.NewIntVar(0, 24_800, 'train_%s_day_end_km' % t)
        for t in trains
    }

    for t in trains:
        # this will be constrained because day_end_km[t] is in domain [0, 24_800]
        tmp = sum(assignments[t, r]*route_km[r] for r in routes) + train_cum_km[t]   
        model.Add(day_end_km[t] == tmp)

    # constraint 4: where 2 routes are assigned to a train, these must not overlap
    for (r1, r2) in combinations(routes, 2):
            if test_overlap(route_times[r1][0], route_times[r1][1], route_times[r2][0], route_times[r2][1]):
                 for train in trains:
                    model.AddBoolOr([assignments[train, r1].Not(), assignments[train, r2].Not()])


    # constraint 5: trains with high cum km should be assigned short routes 
    # and trains with low mileage to long routes

    score = {
              (t,r): route_km[r] + train_cum_km[t]
              for t in trains for r in routes
             }

    for r in routes:
        model.Minimize(sum(assignments[t,r]*score[t,r] for t in trains))


    status = solver.Solve(model)
    assert status in [cp_model.FEASIBLE, cp_model.OPTIMAL]

    for t in trains:
        t_routes = [r for r in routes if solver.Value(assignments[t, r])]
        print(f'Train {t} does route {t_routes} '
              f'with end of day cumulative kilometreage of '
              f'{solver.Value(day_end_km[t])}')


if __name__ == '__main__':
    main()

Output:

Train T32 does route ['R42', 'R45'] with end of day cumulative kilometreage of 24320
Train T11 does route ['R41', 'R44'] with end of day cumulative kilometreage of 24220
Train T38 does route ['R43'] with end of day cumulative kilometreage of 24210
Train T28 does route ['R16'] with end of day cumulative kilometreage of 1200
Train T15 does route ['R32'] with end of day cumulative kilometreage of 800
Train T24 does route ['R11'] with end of day cumulative kilometreage of 800

从我的头顶上下来,不确定这是否是最好的方法:

assignments = {
    (route, train): model.NewBoolVar('')
    for route in routes
    for train in all_trains
}

每趟列车必须分配至至少一条路线(最多两条路线)

for train in all_trains:
    model.Add(sum(assignments[route, train] for route in routes) >= 1)
    model.Add(sum(assignments[route, train] for route in routes) <= 2)

列车最终里程一旦分配给路线,不得超过 24,800

创建一个包含每条路线的里程的字典:route_km = {'R11': 700, 'R16': 600}以及每趟列车的累计里程cum_mileage = {0: 24_320, 3: 24_220}

for train in all_trains:
    model.Add(cum_mileage[train]+sum(
        assignments[route, train]*route_km[route]
        for route in routes
    ) <= 24_800)

列车一天内分配两条线路的,两条线路的时间不得重叠

创建一个返回的函数True如果两条路线重叠

python中有效的日期范围重叠计算?

进而:

from itertools import combinations
for (r1, r2) in combinations(routes, 2):
    if not conflicts(r1, r2):
        continue
    for train in all_trains:
        model.AddBoolOr([assignments[r1, train].Not(), assignments[r2, train].Not()])
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Google OR 工具 - 火车调度问题 的相关文章

  • 将打开关闭的 Google Chrome 浏览器添加到 Selenium linkedin_scraper 代码中

    我正在尝试抓取一些知名人士的 LinkedIn 个人资料 该代码获取一堆 LinkedIn 个人资料 URL 然后使用Selenium and scrape linkedin收集信息并将其作为 json 文件保存到文件夹中 我遇到的问题是
  • 如何在 Django 管理中以表格格式显示添加模型?

    我刚刚开始使用 Django 编写我的第一个应用程序 为我的家庭设计的家务图表管理器 在本教程中 它向您展示了如何添加相关对象 http docs djangoproject com en dev intro tutorial02 cust
  • 有条件填写 pandas 数据框

    我有一个数据框df列中包含浮点值A 我想添加另一列B这样 B 0 A 0 for i gt 0 B i if np isnan A i then A i else Step3 B i if abs B i 1 A i B i 1 lt 0
  • Django 如何从 ManyToManyField 序列化并列出全部

    我正在使用 Django 1 9 1 开发移动应用程序后端 我实现了关注者模型 现在我想列出用户的所有关注者 但目前我不得不这样做 我还使用 Django Rest 框架 这是我的 UserProfile 模型 class UserProf
  • Python 使用 M2Crypto 通过 S/MIME 对消息进行签名

    我现在花了几个小时 但找不到我的错误 我想要一个简单的例程来创建 S MIME 签名消息 稍后可以与 smtplib 一起使用 这是我到目前为止所拥有的 usr bin python2 7 coding utf 8 from future
  • 使用opencv计算深度视差图

    我无法使用 opencv 从视差图计算深度 我知道两个立体图像中的距离是用以下公式计算的z baseline focal disparity p 但我不知道如何使用地图计算视差 我使用的代码如下 为我提供了两个图像的视差图 import n
  • 对图像使用 Pixellib 自定义训练时出现 input_image 元形状错误

    我正在使用 Pixellib 来训练自定义图像实例分割 我创建了一个数据集 可以在下面的链接中看到 数据集 https drive google com drive folders 1MjpDNZtzGRNxEtCDcTmrjUuB1ics
  • 理解@property装饰器和继承[重复]

    这个问题在这里已经有答案了 这里是 Python 3 以防万一它很重要 我试图正确理解如何实现继承 property使用 我已经搜索了 StackOverflow 并阅读了大约 20 个类似的问题 但无济于事 因为他们试图解决的问题略有不同
  • Selenium Webdriver - Python - leboncoin - pb 选择带重音的按钮

    我正在尝试在以下网站上自动填写表格 https www leboncoin fr https www leboncoin fr 我用 Selenium IDE 录制了一个脚本 我有一个通过单击 Se 连接器 按钮并填写我的密码和用户名来自动
  • Pandas Pivot_Table :非数字值的行计算百分比

    这是我在数据框 df 中的数据 Document Name Time SPS2315511 A 1 HOUR SPS2315512 B 1 2 HOUR SPS2315513 C 2 3 HOUR SPS2315514 C 1 HOUR S
  • 向 Python 2.6 添加 SSL 支持

    我尝试使用sslPython 2 6 中的模块 但我被告知它不可用 安装OpenSSL后 我重新编译2 6 但问题仍然存在 有什么建议么 您安装了 OpenSSL 开发库吗 我必须安装openssl devel例如 在 CentOS 上 在
  • 在ansible中合并字典

    我目前正在构建一个使用 ansible 安装 PHP 的角色 并且在合并字典时遇到一些困难 我尝试了多种方法来做到这一点 但我无法让它像我想要的那样工作 A vars file my default values key value my
  • 管理文件字段当前 url 不正确

    在 Django 管理中 只要有 FileField 编辑页面上就会有一个 当前 框 其中包含指向当前文件的超链接 但是 此链接会附加到当前页面 url 因此会导致 404 因为不存在这样的页面 例如 http 127 0 0 1 8000
  • 模块“tensorflow”没有属性“random_uniform”

    我尝试执行一些深度学习应用程序 并收到模块 tensorflow 没有属性 random uniform 错误 在 CPU 上 代码运行良好 但速度非常慢 为了在 GPU 上运行代码 我需要更改一些定义 下面是我的代码 有任何想法吗 def
  • 对数据框的行进行排序

    我有以下数据框 adjusted RFC df Node Feature Indicator Scaled Class Direction True False 0 0 km lt 0 181 class 4 0 gt 1 NA 125 1
  • psutil:测量特定进程的CPU使用率

    我正在尝试测量进程树的 cpu 使用率 目前获取进程 没有子进程 的 cpu usage 就可以了 但我得到了奇怪的结果 import psutil p psutil Process PID p cpu percent 还给我float g
  • 从 Python 中编译的正则表达式中提取命名组正则表达式模式

    我有一个 Python 正则表达式 其中包含多个命名组 但是 如果先前的组已匹配 则可能会错过与一组匹配的模式 因为似乎不允许重叠 举个例子 import re myText sgasgAAAaoasgosaegnsBBBausgisego
  • 使 matplotlib 图形默认看起来像 R?

    Is there a way to make matplotlib behave identically to R or almost like R in terms of plotting defaults For example R t
  • python中匹配3个或更多相同的字符

    我正在尝试使用正则表达式在字符串中查找三个或更多相同的字符 例如 你好 不匹配 噢 会的 我尝试过做类似的事情 re compile 1 3 a zA Z re compile w 1 5 但似乎都不起作用 w 1 2 是您正在寻找的正则表
  • Shap - 颜色条不显示在摘要图中

    显示summary plot时 不显示颜色条 shap summary plot shap values X train 我尝试过改变plot size 当绘图较高时 会出现颜色条 但它非常小 看起来不应该 shap summary plo

随机推荐

  • 如何在 SwiftUI 中设置清晰/透明背景的导航栏?

    我试图弄清楚如何为自定义导航栏编写代码以显示清晰 透明的栏而不是 白色 栏 看这个截图 这是我的代码 import SwiftUI struct ContentView View init UINavigationBar appearanc
  • Zend Framework 2:如何在应用程序到达控制器之前将重定向放入模块中

    假设我们有一个名为 Cart 的模块 并且希望在满足某些条件时重定向用户 我想在应用程序到达任何控制器之前在模块引导阶段放置重定向 所以这是模块代码 我想使用Url控制器插件 但似乎现阶段控制器实例不可用 至少我不知道如何获取它 提前致谢
  • 如何在IdentityServer4中进行多步登录?

    我们使用 IdentityServer3 隐式授权并且登录由多个屏幕组成 在 IdentityServer3 中 内置了对此类多步骤登录工作流程的支持 例如接受 EULA 双因素登录等 该功能称为 部分登录 甚至还有一个例子 https g
  • 在 Eclipse 中使用 Ant 的类路径

    我有一只蚂蚁build xml文件在命令行上运行得很好 它编译 构建 JAR 并且我能够从 JAR 中很好地执行 main 方法 这build xml文件引用了分散在各处的几个第三方库 构建 JAR 时 脚本不会将所有第三方库包含到 JAR
  • 这是从数组哈希中获取公共元素的最佳方法吗?

    我正在尝试从 Ruby 中的一组数组中获取一个公共元素 通常 您可以使用 运算符来比较两个数组 返回两个数组中存在或共有的元素 这一切都很好 除非您试图从多个对象中获取共同元素two数组 但是 我想从未知的动态数组数量 它们存储在哈希中 我
  • 如何处理原始可空类型的 Spark UDF 输入/输出

    问题 1 如果输入是包含以下内容的原始类型列 Spark 不会调用 UDFnull inputDF show x null 1 0 inputDF withColumn y udf x Double gt 2 0 apply x will
  • 禁用 jQuery 中的按钮

    我的页面创建多个按钮id rbutton i 下面是我的代码
  • Eclipse 错误:无法确定 /project-path/ 的 URI

    我在 VirtualBox 中的 Ubuntu 12 0 4 上使用 Windows 8 主机运行 Eclipse Luna 每隔一段时间 我就会启动 Ubuntu 并打开 Eclipse 来查找以下内容 我的项目应该列在包资源管理器中 但
  • python的“in”语言构造对于列表来说是线程安全的吗?

    Is obj in a list线程安全的同时a list可能会在不同的线程中修改 这是一个全面但非详尽的示例列表 of list操作以及它们是否是线程安全的 但是我找不到任何参考in语言构造 在 python 实现方面 我使用 CPyth
  • 简单的 html dom 抓取大型 html 文件

    我需要抓取一个大的 html 文件 例如 http www indianrail gov in mail express trn list html 使用简单的 html dom 我从一个简单的脚本开始 它什么也没显示 只是一个空白页 其中
  • 查找 TabStrip 索引

    是否可以在 KendoUI TabStrip 中找到选项卡的索引 我需要找到我选择的选项卡的索引 编号 并且我知道select 返回我当前的选项卡 但我不知道如何将其转换为数字 找到了解决方案 tabstrip data kendoTabS
  • VAO 是否会记住 EBO/IBO(元素或索引)和 VBO?

    我的代码正在正常工作 但这可能是一个巧合 我不想稍后再纠缠于错误 所以我试图尽可能保持它干净 我执行以下操作来初始化网格 生成并绑定 VBO 和缓冲区数据 生成并绑定 IBO 和缓冲区数据 生成并结合 VAO 绑定与之前相同的 VBO 在
  • 如何在Windows中异步打开文件

    有没有办法在 Windows 中异步打开文件 CreateFile API 函数只有 FILE FLAG OVERLAPPED 允许进一步异步读取和写入 尽管如此 文件的打开似乎是同步的 鉴于它必须访问文件系统 并可能执行昂贵的 IO 操作
  • 使用 fscanf 读取双精度

    我想从文本文件中读取双精度值 例如 31 39 9316476397222 116 113516352222 我两种都试过了 没用 我只能读取前几位十进制数字 例如39 93164 但不是 39 9316476397222 有人知道为什么吗
  • 无需光标即可在 Android Sqlite 中访问大型 BLOB

    Android 的光标窗口大小似乎有 1MB 的限制 这限制了从 SQLite 读取 BLOB 的能力 我知道您可能会说我们不应该将 BLOB 存储在数据库中 但根据定义 BLOB 被视为二进制大对象 如果不需要将它们存储在数据库中 则无需
  • 如何生成多重集的所有排列?

    多重集是一个集合 其中所有元素可能不唯一 如何枚举集合元素之间所有可能的排列 生成所有可能的排列然后丢弃重复的排列是非常低效的 存在各种算法来直接生成按字典顺序或其他类型的排序的多重集的排列 Takaoka 的算法是一个很好的例子 但 Aa
  • 编译后的 .lib 文件对于不同版本的 Microsoft Visual C++ 是否可以互换?

    有些项目为 C 以及可能的 C 不确定 库提供了一组 Windows 二进制文件 例如 请参阅右侧的链接这个 libxml 相关页面 我很确定没有办法在 VC lib 文件和 MinGW GCC a 文件之间进行转换 因此将它们称为 Win
  • 链接器命令失败,架构 i386 的符号未定义

    我正在尝试执行半页卷曲功能 这是我正在使用的代码 import
  • &**this 到底返回什么?

    这是指向调用对象的指针 它返回右值 这是一个指向调用对象的指针的指针 它返回地址的值 这是一个指向调用对象的指针的指针 这是对调用对象的指针的指针的引用 std vector
  • Google OR 工具 - 火车调度问题

    我试图解决的问题有点像这里的员工调度问题 https github com google or tools blob master examples python shift scheduling sat py 然而 有一些事情我被困住了