Целевая функция Minizinc для пробелов в расписании

У меня есть рабочая модель Miniznic для планирования индивидуальных уроков для 1 профессора с n студентами (Задача оптимизации модели расписания отдельных уроков). Модель учитывает доступность (жесткое ограничение) и временные предпочтения (целевая функция) как профессора, так и студентов.

Теперь я хочу расширить модель и оптимизировать расписание, чтобы минимизировать перерывы между уроками.

Пример:

  Schedule  : p L p p L L . . p p L L p L L p L p p . L p L L . L p L
  Real Gaps : . L p p L L . . . . L L p L L p L . . . L p L L . L p L

куда

 `p` =  0 == Teacher available, but no Lesson
 `L` =  1 == Teacher gives lesson (to student)
 `.` = -1 == Teacher not available

Очевидно, что p в слоте 1 не следует рассматривать как пробел. Точно так же слоты 9 и 10 тоже не имеют зазоров. Удалив все ложные пробелы, Schedule должен, наконец, выглядеть как массив Real Gaps (Примечание: ложные пробелы помечены .; то же самое, что и недоступно).

Результатом будет массив промежутков [2, 1, 1, 1, 1] (для каждого промежутка, показывающего количество имеющихся в нем слотов). На основе такого массива можно было бы, например, сформулируйте цель минимизировать общий разрыв слотов.

В ruby мне удалось сформулировать алгоритм, который делает то, что я хочу:

def gap_array(schedule_arr)
  # initialize variables
  start_search = false              # flag for break start
  break_slots  = 0                  # counter of break slots
  res_arr      = []                 # resulting array
  schedule_arr.each do |slot|
    if slot == 1                    # start watching for break
      start_search = true
    end
    #
    if start_search                 
      if    slot == 0               # == break
        break_slots += 1
      elsif slot == 1               # == consecutive lesson slot
        if break_slots > 0          # number of break_slots > 0
          res_arr.append(break_slots)
          break_slots = 0
        end
      else                          # == not available
        break_slots  = 0            # any break so far is discarded
        start_search = false         
      end
    end
  end
  return res_arr
end

Как я могу сформулировать такой алгоритм в Minizinc?

Спасибо!


person andiba    schedule 19.06.2020    source источник


Ответы (1)


Один из способов сделать это в MiniZinc - расширить модель на странице Задача оптимизации модель планирования отдельных уроков следующим образом:

Первоначально рассчитайте teacher_free как интервалы, в которых учитель недоступен, в сочетании с соседними интервалами, где не проводится урок (это выполняется в два этапа, начиная с левого teacher_free_left и правого teacher_free_right соответственно, а затем объединяя результаты в teacher_free) .

На следующем этапе real_gap рассчитывается как интервалы, когда учитель не свободен и урок не проходит.

Затем в цели вводится штрафной член для real_gap, например (k2 - это штрафной вес за пробел):

int: k2 = 1;
var int: obj = sum(s in STUDENT, t in TIME)
    (active[s,t] * (prio_time[s,t] + k*prioTeacher_time[t])) - k2*sum(real_gap);

Вот все остальные расширения модели (с некоторыми дополнительными комментариями):

array[DAY,SLOT]           of var 0..1: lesson = array2d(DAY, SLOT, [sum(s in STUDENT)(active[s,time(d,z)]) | d in DAY, z in SLOT]);
array[DAY,SLOT]           of var 0..1: teacher_free_left;
array[DAY,SLOT]           of var 0..1: teacher_free_right;
array[DAY,SLOT]           of var 0..1: teacher_free;
array[DAY,SLOT]           of var 0..1: real_gap;

predicate equals_and(var 0..1: z, var 0..1: x, var 0..1: y) = 
    (z <= x /\ z <= y /\ z >= x + y - 1);

predicate equals_or(var 0..1: z, var 0..1: x, var 0..1: y) = 
    (z >= x /\ z >= y /\ z <= x + y);

% calculate teacher free left
%    first slot -> teacher free = no lesson in the slot
%    other slots -> teacher free = teacher out or (left slot teacher free and no lesson in slot)
array[DAY,SLOT]           of var 0..1: teacher_free_left_temp;

constraint forall(d in DAY)
    (teacher_free_left_temp[d,1]=1-lesson[d,1]);
    
constraint forall(d in DAY, z in 2..maxSlots)
    (equals_and(teacher_free_left_temp[d,z], teacher_free_left[d,z-1], 1-lesson[d,z]));

constraint forall(d in DAY, z in SLOT)
    (equals_or(teacher_free_left[d,z], 1 - bool2int(z in teacher[d]), teacher_free_left_temp[d,z]));
    
% calculate teacher free right
%    last slot -> teacher free = no lesson in the slot
%    other slots -> teacher free = teacher out or (right slot teacher free and no lesson in slot)
array[DAY,SLOT]           of var 0..1: teacher_free_right_temp;

constraint forall(d in DAY)
    (teacher_free_right_temp[d,maxSlots]=1-lesson[d,maxSlots]);
    
constraint forall(d in DAY, z in 1..maxSlots-1)
    (equals_and(teacher_free_right_temp[d,z], teacher_free_right[d,z+1], 1-lesson[d,z]));

constraint forall(d in DAY, z in SLOT)
    (equals_or(teacher_free_right[d,z], 1 - bool2int(z in teacher[d]), teacher_free_right_temp[d,z]));

% teacher free when teacher free left or teacher free right
constraint forall(d in DAY, z in SLOT)
    (equals_or(teacher_free[d,z], teacher_free_left[d,z], teacher_free_right[d,z]));

% real gap when teacher not free and no lesson
constraint forall(d in DAY, z in SLOT)
   (equals_and(real_gap[d,z], 1-teacher_free[d,z], 1-lesson[d,z]));
person Magnus Åhlander    schedule 22.06.2020