【问题标题】:Implementing specific constraint in Pulp在 Pulp 中实施特定的约束
【发布时间】:2021-06-30 17:45:46
【问题描述】:

我已经成功实施了一个计划,我将一周中的每一天分配 N truck driversM gathering hubs。我实施的约束是:

    • 司机不能工作超过 6 天,即休息 1 天
    • 每天不能将驱动程序分配在超过 1 个集线器中
    • 每个中心都必须满足一周中每一天的司机要求

程序运行平稳,满足总体目标,并为每对集线器驱动程序输出以下形式的时间表:

                 Monday  Tuesday  Wednesday  Thursday  Friday  Saturday  Sunday
Hub   Driver                                                                   
Hub 1 Driver_20       1        0          0         0       0         0       0
Hub 2 Driver_20       0        0          0         0       0         0       0
Hub 3 Driver_20       0        0          0         0       0         0       0
Hub 4 Driver_20       0        0          0         0       0         0       0
Hub 5 Driver_20       0        1          0         0       0         0       0
Hub 6 Driver_20       0        0          0         0       1         0       0
Hub 7 Driver_20       0        0          0         1       0         1       1 

但是,我想添加一个额外的约束,如果可能的话,强制司机在一个中心工作,而不是将他们的工作日分散在多个中心,即在将驱动程序分配到不同的集线器之前最大化一个集线器的工作。

例如,在上面的输出中,我们看到驱动程序在不同的集线器工作了 3 天,在集线器 7 工作了 3 天。我们如何编写约束以使驱动程序被分配(如果可能)在一个集线器上工作如果可能的话?

请在下面找到我的代码。

谢谢

import pulp
import pandas as pd
import numpy as np

pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', 2000)
pd.set_option('display.float_format', '{:20,.2f}'.format)
pd.set_option('display.max_colwidth', None)

day_requirement = [[2, 2, 3, 2, 5, 2, 2],
                    [2, 2, 2, 2, 2, 2, 2],
                    [2, 2, 2, 2, 2, 2, 2],
                    [3, 3, 3, 3, 3, 3, 3],
                    [2, 2, 2, 2, 2, 2, 2],
                    [2, 2, 2, 2, 2, 2, 2],
                    [4, 4, 4, 4, 4, 4, 4],
                   ]

total_day_requirements = ([sum(x) for x in zip(*day_requirement)])

hub_names = {0: 'Hub 1',
             1: 'Hub 2',
             2: 'Hub 3',
             3: 'Hub 4',
             4: 'Hub 5',
             5: 'Hub 6',
             6: 'Hub 7'}

total_drivers = max(total_day_requirements)  # number of drivers
total_days = 7  # The number of days in week
total_hubs = len(day_requirement)  # number of hubs

def schedule(drivers, days, hubs):
    driver_names = ['Driver_{}'.format(i) for i in range(drivers)]
    var = pulp.LpVariable.dicts('VAR', (range(hubs), range(drivers), range(days)), 0, 1, 'Binary')

    problem = pulp.LpProblem('shift', pulp.LpMinimize)

    obj = None
    for h in range(hubs):
        for driver in range(drivers):
            for day in range(days):
                obj += var[h][driver][day]
    problem += obj

    # schedule must satisfy daily requirements of each hub
    for day in range(days):
        for h in range(hubs):
            problem += pulp.lpSum(var[h][driver][day] for driver in range(drivers)) == \
                       day_requirement[h][day]

    # a driver cannot work more than 6 days
    for driver in range(drivers):
        problem += pulp.lpSum([var[h][driver][day] for day in range(days) for h in range(hubs)]) <= 6

    # if a driver works one day at a hub, he cannot work that day in a different hub obviously
    for driver in range(drivers):
        for day in range(days):
            problem += pulp.lpSum([var[h][driver][day] for h in range(hubs)]) <= 1

    # Solve problem.
    status = problem.solve(pulp.PULP_CBC_CMD(msg=0))

    idx = pd.MultiIndex.from_product([hub_names.values(), driver_names], names=['Hub', 'Driver'])

    col = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

    dashboard = pd.DataFrame(0, idx, col)

    for h in range(hubs):
        for driver in range(drivers):
            for day in range(days):
                if var[h][driver][day].value() > 0.0:
                    dashboard.loc[hub_names[h], driver_names[driver]][col[day]] = 1

    driver_table = dashboard.groupby('Driver').sum()
    driver_sums = driver_table.sum(axis=1)
    # print(driver_sums)

    day_sums = driver_table.sum(axis=0)
    # print(day_sums)

    print("Status", pulp.LpStatus[status])

    if (driver_sums > 6).any():
        print('One or more drivers have been allocated more than 6 days of work so we must add one '
              'driver: {}->{}'.format(len(driver_names), len(driver_names) + 1))
        schedule(len(driver_names) + 1, days, hubs)
    else:
        print(dashboard)
        print(driver_sums)
        print(day_sums)
        for driver in range(drivers):
            driver_name = 'Driver_{}'.format(driver)
            print(dashboard[np.in1d(dashboard.index.get_level_values(1), [driver_name])])


schedule(total_drivers, total_days, total_hubs)

【问题讨论】:

    标签: python optimization pulp


    【解决方案1】:

    您可以添加二进制变量z 指示驱动程序是否在集线器上处于活动状态:

    z = pulp.LpVariable.dicts('Z', (range(hubs), range(drivers)), 0, 1, 'Binary')
    

    然后将您的目标更改为(最小化在集线器上活动的驱动程序的总和):

    for h in range(hubs):
        for driver in range(drivers):
            obj += z[h][driver]
    problem += obj
    

    添加约束以连接zvar

    for driver in range(drivers):
        for h in range(hubs):
            problem += z[h][driver] <= pulp.lpSum(var[h][driver][day] for day in range(days))
            problem += total_days*z[h][driver] >= pulp.lpSum(var[h][driver][day] for day in range(days))
    

    但是,此模型更为复杂,并且似乎需要一段时间才能找到最佳解决方案。您可以设置一个超时时间(此处为 10 秒)以获得解决方案:

    status = problem.solve(pulp.PULP_CBC_CMD(msg=0, timeLimit=10)) 
    

    【讨论】:

    • 我应该将此解决方案插入到我现有的解决方案中吗?
    • 是的,调整您当前的解决方案
    • 嗨,即使我改变了目标,我仍然得到一些在多个集线器上工作的驱动程序。我做错了什么?
    • 添加for each driver in range(drivers): pulp.lpSum(z[h][driver] for h in range(hubs)) &lt;= 1
    • 感谢您的回答。问题是,当我添加这个约束时,似乎违反了 A 司机不能工作超过 6 天,即 1 天休息的约束,只有少数司机工作 6 天;他们中的大多数最终工作1-5天。这可以以某种方式修改吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-05
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 2016-12-14
    • 1970-01-01
    相关资源
    最近更新 更多