如何解决Google ortools CP-SAT 最大化耗时太长
我正在尝试解决日程安排问题。作为输入,我有工人数量和班次数量。
之后我放置了所有约束,包括:
- 每个人都必须处理相同数量的轮班,
- 每个人都必须休完所有给定的假期,
- ...等
然后我尝试最大化给定的移位序列。
例如,我可以接收序列: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。
给定序列的约束是 OnlyEnforced,如果为该序列创建的 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,shift - 2 - 3)] for d in range(dayz,first_range))+
sum(shifts[(w,shift - 2 - 2)] for d in range(first_range,second_range))+
sum(shifts[(w,shift - 2 - 1)] for d in range(second_range,third_range))+
sum(shifts[(w,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]))))
解决方法
对于每个开始日期 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
的数量。
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,1)] == 1).OnlyEnforceIf(required_sequence_bools[w][d])
elif letter == '3':
model.Add(shifts[(w,2)] == 1).OnlyEnforceIf(required_sequence_bools[w][d])
elif letter == 'B':
model.Add(shifts[(w,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:工人是指在我的算法中工作的人,而不是处理器核心。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。