QpmtnSumCi — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (rollbackEdits.php mass rollback)
 
(не показаны 2 промежуточные версии 2 участников)
Строка 10: Строка 10:
  
 
=== Псевдокод ===
 
=== Псевдокод ===
Алгоритм принимает на вход два массива {{---}} массив с количеством обработки нужным для каждой работы и массив скоростей обработки станков, и возвращает массив длинной <tex>m</tex>, в котором на <tex>i</tex>-ой позиции содержится расписание для <tex>i</tex>-ого станка, представляющей собой вектор троек, где первый элемент является номером работы, а два оставшихся время начала и окончания обработки этой работы на этом станке.
+
Алгоритм принимает на вход два массива {{---}} массив с объемами работ и массив скоростей обработки станков, и возвращает вектор четвёрок, где первый элемент является номером станка, второй {{---}} номером работы, а два оставшихся время начала и окончания обработки этой работы на этом станке.
  '''function''' scheduling(p: '''Array'''[int], v: '''Array'''[int]) -> '''Array[Vector[(int, int, int)]]'''
+
  '''function''' scheduling(p: '''int'''[n], v: '''int'''[n]) -> '''vector<int, int, int, int>'''
 
     '''int''' time = 0 <font color=green>// текущее время на всех станках</font>
 
     '''int''' time = 0 <font color=green>// текущее время на всех станках</font>
     '''Array[Vector[(int, int, int)]]''' ans = new '''Array[Vector[(int, int, int)]]'''(m) <font color=green> // массив длинной m, куда будет записан ответ</font>
+
     '''vector<int, int, int, int>''' ans <font color=green> // вектор, куда будет записан ответ</font>
 
     sort(p) <font color=green>// сортируем времена обработки работ в невозрастающем порядке</font>
 
     sort(p) <font color=green>// сортируем времена обработки работ в невозрастающем порядке</font>
 
     sort(v) <font color=green>// сортируем скорости обработки станков в невозрастающем порядке</font>
 
     sort(v) <font color=green>// сортируем скорости обработки станков в невозрастающем порядке</font>
Строка 20: Строка 20:
 
         '''int''' dt = p[i] / v[1] <font color=green>// время обработки самой короткой доступной задачи на самом быстром станке</font>
 
         '''int''' dt = p[i] / v[1] <font color=green>// время обработки самой короткой доступной задачи на самом быстром станке</font>
 
         '''for''' j = i '''downto''' k = max(1, i - m + 1)
 
         '''for''' j = i '''downto''' k = max(1, i - m + 1)
             ans[1 + i - j].push(j, time, time + dt) <font color=green>// Назначаем работу j на станок <tex>M_{1+i-j}</tex> на время от time до time + dt</font>
+
             ans.push(1 + i - j, j, time, time + dt) <font color=green>// Назначаем на станок <tex>M_{1+i-j}</tex> работу j на время от time до time + dt</font>
 
             p[j] = p[j] - dt * v[1 + i - j] <font color=green>// уменьшаем количество обработки, оставшейся для j-ой работы</font>
 
             p[j] = p[j] - dt * v[1 + i - j] <font color=green>// уменьшаем количество обработки, оставшейся для j-ой работы</font>
 
         time = time + dt <font color=green>// обновляем текущее время</font>
 
         time = time + dt <font color=green>// обновляем текущее время</font>

Текущая версия на 19:26, 4 сентября 2022

[math] Q \mid pmtn \mid \sum C_i [/math]

Задача:
Дано [math]m[/math] станков с разной скоростью выполнения работ [math]v_j[/math] и [math]n[/math] работ с заданным временем выполнения [math]p_i[/math]. Работы можно прерывать и продолжать их выполнение на другом станке. Необходимо построить такое расписание, чтобы суммарное время окончания всех работ было минимальным.

Описание алгоритма

Пример работы алгоритма

Идея

Для решения применим правило SRPT-FM (Shortest Remaining Processing Time on Fastest Machine), которое предлагает класть работу с наименьшим оставшемся временем обработки на самый быстрый доступный станок. Отсортируем работы по времени обработки в невозрастающем порядке так, что [math]p_1 \geqslant p_2 \geqslant \ldots \geqslant p_n[/math]. Отсортируем станки по скорости обработки в невозрастающем порядке, так чтобы [math]v_1 \geqslant v_2 \geqslant \ldots \geqslant v_m[/math]. Далее назначаем [math]n[/math]-ю работу на станок [math]M_1[/math] (1-й по скорости станок) на время [math]t_1 = \cfrac{p_n}{v_1}[/math], то есть пока она полностью не выполнится. Теперь назначим [math]n-1[/math] работу сначала станок [math]M_2[/math] на время [math]t_1[/math], а затем на время от [math]t_1[/math] до [math]t_2 \geqslant t_1[/math] на станок [math]M_1[/math], пока она не завершится. С [math]n-2[/math] работой поступаем аналогично, сначала она [math]t_1[/math] времени выполняется на станке [math]M_3[/math], затем [math]t_2 - t_1[/math] времени на станке [math]M_2[/math], и, начиная с [math]t_2[/math] до [math]t_3 \geqslant t_2[/math], на станке [math]M_1[/math]. Также поступаем со всеми оставшимися работами.

Псевдокод

Алгоритм принимает на вход два массива — массив с объемами работ и массив скоростей обработки станков, и возвращает вектор четвёрок, где первый элемент является номером станка, второй — номером работы, а два оставшихся время начала и окончания обработки этой работы на этом станке.

function scheduling(p: int[n], v: int[n]) -> vector<int, int, int, int>
    int time = 0 // текущее время на всех станках
    vector<int, int, int, int> ans  // вектор, куда будет записан ответ
    sort(p) // сортируем времена обработки работ в невозрастающем порядке
    sort(v) // сортируем скорости обработки станков в невозрастающем порядке
    while p[1] > 0
       Находим наибольший i такой, что p[i] > 0
       int dt = p[i] / v[1] // время обработки самой короткой доступной задачи на самом быстром станке
       for j = i downto k = max(1, i - m + 1)
           ans.push(1 + i - j, j, time, time + dt) // Назначаем на станок [math]M_{1+i-j}[/math] работу j на время от time до time + dt
           p[j] = p[j] - dt * v[1 + i - j] // уменьшаем количество обработки, оставшейся для j-ой работы
       time = time + dt // обновляем текущее время
    return ans

Асимптотика

Сначала мы сортируем работы и станки, что занимает [math] \mathcal{O}(n\log{n})[/math] и [math] \mathcal{O}(m\log{m})[/math]. Так как при [math]m \geqslant n[/math], [math]m - n[/math] самых медленных станков можно не использовать, то в итоге сортировка займет [math] \mathcal{O}(n\log{n})[/math]. Количество прерываний ограничено числом [math](m-1) \cdot n - (1 + 2 + \ldots + (m - 1)) = (m - 1) \cdot (n - \cfrac{m}{2})[/math]. В итоге всё вместе составляет асимптотику алгоритма [math] \mathcal{O}(n\log{n} + mn)[/math].

Доказательство корректности алгоритма

Лемма:
Существует оптимальное расписание, в котором [math]C_j \leqslant C_k[/math], когда [math]p_j \leqslant p_k[/math], для всех [math]j[/math] и [math]k[/math].
Доказательство:
[math]\triangleright[/math]

Рассмотрим расписание [math]S[/math], где для некоторых [math]j[/math] и [math]k[/math] верно, что [math]C_j \gt C_k[/math], но [math]p_j \leqslant p_k[/math]. С помощью обмена частей обработки, полученной работами [math]j[/math] и [math]k[/math], мы можем изменить расписание [math]S[/math], получив новое оптимальное расписание [math]S'[/math], где обработка работ [math]j[/math] и [math]k[/math] завершается во времена [math]C'_j \leqslant C_k[/math] и [math]C'_k = C_j[/math] соответственно, при этом время завершения обработки остальных работ остается прежним. Тот факт, что в расписании [math]S'[/math] работа [math]j[/math] заканчивается раньше [math]k[/math], при этом не нарушая оптимальности расписания, свидетельствует о существовании расписания, описанного в условии леммы.

Для построения расписания [math]S'[/math] из расписания [math]S[/math] введем следующие обозначения:

  • [math]p^1_j[/math], [math]p^1_k[/math] — количество обработки, полученной одновременно [math]j[/math] и [math]k[/math], когда работа [math]j[/math] была на более быстром станке или обе работы были на одинаковых станках.
  • [math]p^2_j[/math], [math]p^2_k[/math] — количество обработки, полученной одновременно [math]j[/math] и [math]k[/math], когда работа [math]k[/math] была на более быстром станке.
  • [math]p^3_j[/math], [math]p^3_k[/math] — количество обработки, полученной [math]j[/math] и [math]k[/math] до момента [math]C_k[/math] на непересекающихся участках времени.
  • [math]p^+_j[/math] — количество обработки, полученной [math]j[/math] после момента [math]C_k[/math].

Далее существуют два случая:

  • Если [math]p^+_j \leqslant p^3_k[/math], то расписание [math]S'[/math] получается обменом обработки, полученной [math]j[/math] после момента [math]C_k[/math] с частью обработки, полученной [math]k[/math] не одновременно с [math]j[/math].
  • Если [math]p^+_j \gt p^3_k[/math], то для начала обмениваем всю обработку [math]p^3_k[/math] с частью [math]p^+_j[/math], чтобы получить новое расписание, где [math]C'_k = C_j[/math] и [math]\tilde{p}^+_j = p^+_j - p^3_k[/math] обработки [math]j[/math] после момента [math]C_k[/math]. Пусть [math]p^2_j[/math] и [math]p^2_k[/math] представлены числом [math]T[/math] непересекающихся временных интервалов длинной [math]t_i[/math] с [math]v^i_j[/math], [math]v^i_k[/math] — скоростями станков, обрабатывающих [math]j[/math] и [math]k[/math] в интервал [math]i[/math] и [math]p^{2,i}_j = v^i_j \cdot t_i[/math], [math]p^{2,i}_k = v^i_k \cdot t_i[/math] — количество обработки, полученной соответствующей задачей за этот интервал. Так как [math]p^1_j \geqslant p^1_k[/math] и [math]p_j \leqslant p_k[/math], то [math]p^2_j + \tilde{p}^+_j \leqslant p^2_k[/math]. Следовательно, существует такой [math]\tilde{p}^{+,i}_j[/math], что верно:
[math]\sum\limits_{i=1}^{T}{\tilde{p}^{+,i}_j} = \tilde{p}^+_j[/math], [math]p^{2,i}_j + \tilde{p}^{+,i}_j \leqslant p^{2,i}_k[/math], [math]i = 1 \ldots T[/math]
Для каждого [math]i = 1 \ldots T[/math] пусть [math]t^i_j = \cfrac{\tilde{p}^{+,i}_j}{v^i_k - v^i_j}[/math], [math]t^i_k = t^i_j \cdot \cfrac{v^i_j}{v^i_k}[/math]. Чтобы получить расписание [math]S'[/math] для каждого из [math]T[/math] интервалов нужно обменять:
1) обработку, полученную в начале интервала работой [math]k[/math] на более быстром станке, с обработкой [math]j[/math], полученной на медленном станке, на интервалы [math]t^i_k[/math] и [math]t^i_j[/math] соответственно
2) обработку [math]\tilde{p}^{+,i}_j[/math], полученной [math]j[/math] после момента [math]C_k[/math], с таким же количеством обработки, полученной работой [math]k[/math] на более быстром станке в этот интервал сразу после момента [math]t^i_k[/math].
[math]\triangleleft[/math]
Теорема:
Расписание, построенное по принципу SRPT-FM, оптимальное для задачи [math] Q \mid pmtn \mid \sum C_i [/math].
Доказательство:
[math]\triangleright[/math]

Без потери общности будем предполагать, что количество станков равно количеству работ [math]m = n[/math]. Если [math]m \lt n[/math], то добавим [math]n - m[/math] станков с нулевой скоростью обработки, если [math]m \gt n[/math], то удалим [math]m - n[/math] самых медленных станков.

Пусть у нас есть расписание, построенное по правилу SRPT-FM, тогда [math]C_{n} \leqslant C_{n-1} \leqslant \ldots \leqslant C_{1}[/math]. Следовательно, верны следующие равенства:

[math]v_1 \cdot C_n = p_n[/math]
[math]v_2 \cdot C_n + v_1 \cdot (C_{n-1} - C_n) = p_{n-1}[/math]
[math]v_3 \cdot C_n + v_2 \cdot (C_{n-1} - C_n) + v_1 \cdot (C_{n-2} - C_{n-1}) = p_{n-2}[/math]
[math]\vdots[/math]
[math]v_n \cdot C_n + v_{n-1} \cdot (C_{n-1} - C_n) + \ldots + v_1 \cdot (C_1 - C_2) = p_1[/math]

Проведем математические преобразования и получим следующие равенства:

[math]v_1 \cdot C_n = p_n[/math]
[math]v_2 \cdot C_n + v_1 \cdot C_{n-1} = p_n + p_{n-1}[/math]
[math]v_3 \cdot C_n + v_2 \cdot C_{n-1} + v_1 \cdot C_{n-2} = p_n + p_{n-1} + p_{n-2}[/math]
[math]\vdots[/math]
[math]v_n \cdot C_n + v_{n-1} \cdot C_{n-1} + \ldots + v_1 \cdot C_1 = p_n + p_{n-1} + \ldots + p_1[/math]

Пусть [math]S'[/math] оптимальное расписание, по предыдущей лемме верно, что [math]C'_{n} \leqslant C'_{n-1} \leqslant \ldots \leqslant C'_{1}[/math]. Самая короткая работа не может быть выполнена раньше, чем [math]\cfrac{p_n}{v_1}[/math]. Таким образом [math]C'_{n} \geqslant \cfrac{p_n}{v_1}[/math] или

[math]v_1 \cdot C'_{n} \geqslant p_n[/math]

Так как работы [math]n[/math] и [math]n-1[/math] выполнены к моменту [math]C'_n[/math] и [math]C'_{n-1}[/math] соответственно, то максимальное количество обработки, полученной этими работами составляет

[math](v_1 + v_2) \cdot C'_n + v_1 \cdot (C'_{n-1} - C'_n)[/math]

Из чего следует, что

[math]v_2 \cdot C'_n + v_1 \cdot C'_{n-1} \geqslant p_n + p_{n-1}[/math]

Продолжая в том же духе можно показать, что

[math]v_k \cdot C'_n + v_{k-1} \cdot C'_{n-1} + \ldots + v_1 \cdot C'_{n-k+1} \geqslant p_n + p_{n-1} + \ldots + p_{n-k+1}[/math]

В итоге получаем следующие неравенства

[math]v_1 \cdot C'_{n} \geqslant v_1 \cdot C_n[/math]
[math]v_2 \cdot C'_n + v_1 \cdot C'_{n-1} \geqslant v_2 \cdot C_n + v_1 \cdot C_{n-1} [/math]
[math]v_3 \cdot C'_n + v_2 \cdot C'_{n-1} + v_1 \cdot C'_{n-2} \geqslant v_3 \cdot C_n + v_2 \cdot C_{n-1} + v_1 \cdot C_{n-2}[/math]
[math]\vdots[/math]
[math]v_n \cdot C'_n + v_{n-1} \cdot C'_{n-1} + \ldots + v_1 \cdot C'_1 \geqslant v_n \cdot C_n + v_{n-1} \cdot C_{n-1} + \ldots + v_1 \cdot C_1[/math]

Если существует такой набор положительных чисел [math]a_j[/math], что умножение [math]j[/math]-го неравенства на [math]a_j[/math] и сложение всех неравенств будет давать неравенство [math]\sum{C'_j} \geqslant \sum{C_j}[/math], тогда теорема будет доказана. Можно показать, что [math]a_j[/math] должны удовлетворять следующим равенствам

[math]v_1 \cdot a_1 + v_2 \cdot a_2 + \ldots + v_n \cdot a_n = 1[/math]
[math]v_1 \cdot a_2 + v_2 \cdot a_3 + \ldots + v_{n-1} \cdot a_n = 1[/math]
[math]\vdots[/math]
[math]v_{1} \cdot a_n = 1[/math]
Так как [math]v_1 \geqslant v_2 \geqslant \ldots \geqslant v_n[/math] такой набор [math]a_j[/math] существует.
[math]\triangleleft[/math]

См. также

Источники информации