【问题标题】:Google ortools CP-SAT maximize takes too longGoogle ortools CP-SAT 最大化耗时太长
【发布时间】:2021-01-26 10:42:08
【问题描述】:

我正在尝试解决调度问题。作为输入,我有工人的数量和轮班的数量。

之后我放置了所有的约束,包括:

  • 每个人都必须轮班工作,
  • 每个人都必须休假,
  • ...等

然后我尝试最大化给定的班次序列。

例如,我可以收到序列:123BBBB。这意味着我必须为每个工人在他们的日历中最大化这个序列的外观。

  • 1 表示工人工作 1 班,
  • 2 代表班次 2,
  • 3 表示班次 3,并且
  • B 表示休息。

我为每个工人计算全年(假设是 365 天)的时间表。我试图为每个可能的序列制作布尔值:所以对于范围(1,长度(序列)),范围(2,长度(序列)))等的天数。

然后我添加一个约束,即从今天到len(sequence) 的偏移总和是len(sequence)。因此,我也将轮班表示为布尔值 (shifts[(w, d, s)]),这意味着 shifts[(0, 3, 0)] 第 3 天的工人 1 工作轮班 1。

给定序列的约束是 OnlyEnforcedIf 为该序列创建的 bool 为真。然后我尝试最大化为每个序列创建的布尔值的总和。

问题:我尝试运行此程序,运行 8 小时后我放弃了。它为我找到了大约 62 个序列,但花了很长时间才停下来或找到另一个。我的问题是:如何更有效地做到这一点?

代码:


shift_requests = []
requiredShift = "111222333BBBBBB"
appearences_for_1 = 0
appearences_for_2 = 0
appearences_for_3 = 0
appearences_for_L = 0
for i in requiredShift:
    if i == '1':
        appearences_for_1 += 1

for i in requiredShift:
    if i == '2':
        appearences_for_2 += 1

for i in requiredShift:
    if i == '3':
        appearences_for_3 += 1

for i in requiredShift:
    if i == 'B':
        appearences_for_B += 1

print(appearences_for_1, appearences_for_2, appearences_for_3, appearences_for_B)

for w in range(worker):
    shift_requests.append([])
    poz = 0
    dayz = 1
    while dayz + len(requiredShift) <= 365:
        shift_requests[w].append(model.NewBoolVar(f'{w}_{dayz}_{dayz + len(requiredShift)}'))

        first_range = dayz + appearences_for_1
        second_range = first_range + appearences_for_2
        third_range = second_range + appearences_for_3
        fourth_range = third_range + appearences_for_B
        
        #shift = 5 ( 0 is shift 1, 1 is shift 2, 2 is shift 3, 3 is break, 4 is holiday)
       

        model.Add(sum(shifts[(w, d, shift - 2 - 3)] for d in range(dayz, first_range))+
                  sum(shifts[(w, d, shift - 2 - 2)] for d in range(first_range, second_range))+
                  sum(shifts[(w, d, shift - 2 - 1)] for d in range(second_range, third_range))+
                  sum(shifts[(w, d, shift - 2)] for d in range(third_range, fourth_range)) == len(requiredShift))\
             .OnlyEnforceIf(shift_requests[w][poz])
        dayz += 1
        poz += 1

model.Maximize(sum(shift_requests[w][poz] for w in range(worker) for poz in range(len(shift_requests[w]))))

【问题讨论】:

  • 一如既往,你设置solver.parameters.num_search_workers = 8了吗?
  • 您可能已经达到了需要数年才能产生最佳解决方案的复杂程度。您可以选择至少满足约束条件的可行解决方案,设置时间限制或尝试通过解决个别月份来降低复杂性。
  • 是的,我确实将工人设置为 8
  • 也试过 6 或更少
  • 作为一个练习,你能不能运行model.ExportToFile('some_filename.pbtxt') 看看这个文件的大小。

标签: python scheduling constraint-programming or-tools cp-sat-solver


【解决方案1】:

对于每个开始日期d,您可以有一个布尔变量s_d

想象模式是'123BBB',你可以有

s_d 表示序列从此日期开始:

model.Add(x[d] == 1).OnlyEnforceIf(s_d)
model.Add(x[d + 1] == 2).OnlyEnforceIf(s_d)
model.Add(x[d + 2] == 3).OnlyEnforceIf(s_d)
model.Add(x[d + 3] == 0).OnlyEnforceIf(s_d)  # 0 == Break
model.Add(x[d + 4] == 0).OnlyEnforceIf(s_d)  # 0 == Break
model.Add(x[d + 5] == 0).OnlyEnforceIf(s_d)  # 0 == Break

然后最大化正数s_d

【讨论】:

  • 我添加了一个响应,因为我不知道如何在这里很好地发布代码。感谢您的回复!
  • 因此,我确实使用受您在此处所说的启发而键入的解决方案多次运行它,它发现我在相同的时间内增加了约 12% 的序列(我给它 20 分钟运行时)。
【解决方案2】:
worker = 5
days = 365
required_sequence_bools = []
required_sequence = "111222333BBBBBB"
for w in range(worker):
    required_sequence_bools.append([])
    for d in range(1, days - len(required_sequence)):
        required_sequence_bools[w].append(model.NewBoolVar(f"{w}_{d}"))

for w in range(worker):
    for d in range(0, days - len(required_sequence) - 1):
        day = d + 1
        for letter in required_sequence:
            if letter == '1':
                model.Add(shifts[(w, day, 0)] == 1).OnlyEnforceIf(required_sequence_bools[w][d])
            elif letter == '2':
                model.Add(shifts[(w, day, 1)] == 1).OnlyEnforceIf(required_sequence_bools[w][d])
            elif letter == '3':
                model.Add(shifts[(w, day, 2)] == 1).OnlyEnforceIf(required_sequence_bools[w][d])
            elif letter == 'B':
                model.Add(shifts[(w, day, 3)] == 1).OnlyEnforceIf(required_sequence_bools[w][d])
            day += 1
model.Maximize(sum(required_sequence_bools[w][d] for w in range(worker) for d in range(0, days - len(required_sequence) - 1)))

这是我从你所说的内容中理解的,我认为我没有正确理解,因为这是我之前所做的。我有 5 名工人,并尝试每天从每个工人的顺序开始。很可能我不明白你想说什么,因为这很可能需要一天以上的时间才能解决 8 个内核。也很抱歉将其发布为 answear,但我不知道如何在评论部分发布代码。 P.S:我所说的工人是指在我的算法中工作的人,而不是处理器核心。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多