调度优化以最大程度地减少时隙数量(有约束)

时间:2018-08-26 01:50:06

标签: python algorithm linear-programming job-scheduling minimization

我正在研究一个调度优化问题,在该问题中,我们有一组任务需要在特定时间范围内完成。

每个任务都有一个时间表,该时间表指定了可以执行的时隙列表。每个任务的时间表可能会有所不同,具体取决于工作日。

这里是一个小样本(减少了任务和时隙的数量):

task_availability_map = {
    "T1" : [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    "T2" : [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    "T3" : [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    "T4" : [0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    "T5" : [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    "T6" : [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
    "T7" : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
    "T8" : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
    "T9" : [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
    "T10": [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]
}

约束是在同一时隙(如果它们重叠)中最多只能并行执行N个任务。无论执行1还是N,并行任务组始终花费相同的时间。

目标是最大程度地减少时隙数。

我尝试了一种蛮力方法,该方法会生成所有时隙索引排列。对于给定排列中的每个索引,获取所有可以安排的任务,并将它们添加到要在下次迭代中排除的任务列表。给定排列的所有迭代完成后,将时隙数和索引组合添加到列表中。

def get_tasks_by_timeslot(timeslot, tasks_to_exclude):
    for task in task_availability_map.keys():
        if task in tasks_to_exclude:
            continue
        if task_availability_map[task][timeslot] == 1:
            yield task

total_timeslot_count = len(task_availability_map.values()[0]) # 17
timeslot_indices = range(total_timeslot_count)

timeslot_index_permutations = list(itertools.permutations(timeslot_indices))

possible_schedules = []

for timeslot_variation in timeslot_index_permutations:
    tasks_already_scheduled = []
    current_schedule = []
    for t in timeslot_variation:
        tasks = list(get_tasks_by_timeslot(t, tasks_already_scheduled))
        if len(tasks) == 0:
            continue
        elif len(tasks) > MAX_PARALLEL_TASKS:
            break
        tasks_already_scheduled += tasks
        current_schedule.append(tasks)

    time_slot_count = np.sum([len(t) for t in current_schedule])
    possible_schedules.append([time_slot_count, timeslot_variation])

...

按时隙数排序可能的时间表,这就是解决方案。但是,该算法的复杂度随着时隙数量的增加而呈指数增长。鉴于有数百个任务和数百个时隙,我需要一种不同的方法。

有人建议使用LP MIP(例如Google OR Tools),但我对此不太熟悉,并且很难在代码中表达约束。非常感谢LP或其他任何可以帮助我朝正确方向入门的解决方案的帮助(不必是Python,甚至可以是Excel)。

1 个答案:

答案 0 :(得分:1)

我对MIP模型的建议:

介绍二进制变量:

x(i,t) = 1 if task i is assigned to slot t
         0 otherwise

y(t) = 1 if slot t has at least one task assigned to it
       0 otherwise

进一步让:

N = max number of tasks per slot
ok(i,t) = 1 if we are allowed to assign task i to slot t
          0 otherwise

然后模型可以如下所示:

minimize sum(t,y(t))                    (minimize used slots)    
sum(t, ok(i,t)*x(i,t)) = 1   for all i  (each task is assigned to exactly one slot)      
sum(i, ok(i,t)*x(i,t)) <= N  for all t  (capacity constraint for each slot)
y(t) >= x(i,t)  for all (i,t) such that ok(i,t)=1
x(i,t),y(t) in {0,1}                    (binary variables)

使用N=3,我得到一个类似的解决方案:

----     45 VARIABLE x.L  assignment

                s5          s6          s7         s13

task1                    1.000
task2                                1.000
task3                    1.000
task4                    1.000
task5        1.000
task6                                            1.000
task7                                            1.000
task8                                            1.000
task9                                1.000
task10                               1.000

该模型非常简单,使用您最喜欢的MIP求解器进行编码和求解并不难。您要确保的一件事是,x(i,t)时仅存在变量ok(i,t)=1。换句话说,请确保在ok(i,t)=0时变量不会出现在模型中。可以将分配约束解释为:

sum(t | ok(i,t)=1, x(i,t)) = 1   for all i  (each task is assigned to exactly one slot)      
sum(i | ok(i,t)=1, x(i,t)) <= N  for all t  (capacity constraint for each slot)

其中|意思是“这样”或“在哪里”。如果您做对了,您的模型应该有50个变量x(i,t)而不是10 x 17 =170。此外,我们可以放宽y(t)在0和1之间连续。它将自动为0或1 。取决于可能影响性能的求解器。

我没有理由相信这更容易建模为约束编程模型,或者更容易解决。我的经验法则是,是否易于建模为MIP坚持MIP。如果我们需要经过很多步骤才能使其成为正确的MIP,并且CP配方使生活更轻松,请使用CP。在许多情况下,此简单规则非常有效。