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

Материал из Викиконспекты
Перейти к: навигация, поиск
м (Подкорректировано доказательство)
м (rollbackEdits.php mass rollback)
 
(не показано 6 промежуточных версий 3 участников)
Строка 1: Строка 1:
==Постановка задачи==
+
<tex dpi = "200" >1 \mid r_i, d_i, p_i=1 \mid -</tex>
Дан один станок на котором нужно выполнить <tex>n</tex> работ. Для каждой работы известны моменты времени, когда можно начинать её выполнять — <tex>r_{i}</tex> и когда необходимо закончить её выполнение — <tex>d_{i}</tex>. Время выполнения <tex>p_{i}</tex> у всех работ одинаково и равно 1. Необходимо узнать, можно ли построить расписание, при котором все работы будут выполнены.
+
 
 +
{{Шаблон:Задача
 +
|definition =  
 +
Дан один станок на котором нужно выполнить <tex>n</tex> работ. Для каждой работы известны моменты времени, когда можно начинать её выполнять — <tex>r_i</tex> и когда необходимо закончить её выполнение — <tex>d_i</tex>. Время выполнения <tex>p_i</tex> у всех работ одинаково и равно 1. Необходимо узнать, можно ли построить расписание, при котором все работы будут выполнены.
 +
}}
  
 
==Алгоритм==
 
==Алгоритм==
Идея алгоритма в том, чтобы из тех работ, которые уже можно выполнить, ставить в расписание ту, у которой наименьшее <tex>d_{i}</tex>. Если эта работа уже просрочена, значит хорошее расписание построить нельзя.
+
 
 +
===Упрощенная версия===
 +
 
 +
<tex>1 \mid d_i, p_i=1 \mid -</tex>
 +
 
 +
Для начала решим более простую версию исходной задачи, когда все <tex>r_i=1</tex>.
 +
 
 +
====Описание алгоритма====
 +
 
 +
Будем выполнять работы в порядке возрастания их дедлайна <tex>d_i</tex>. Утверждается, что это оптимальное расписание.
 +
Приведем реализацию, на основе которой мы вскоре построим решение основной задачи:
 +
 
 +
====Псевдокод====
 +
 
 +
Пусть <tex>S</tex> — множество еще не включенных в расписание задач. Изначально в нем находятся все задачи.
 +
  '''function''' solve(d: '''int'''[n]): '''boolean'''
 +
      <tex>S = \{1, 2, \ldots, n\}</tex>
 +
      '''int '''<tex>time = 1</tex>
 +
      '''for''' <tex> i = 1 </tex> '''to''' <tex> n </tex>
 +
          <tex>d[k] = \min\{d[i] \mid i \in S\}</tex>
 +
          '''if''' <tex>d[k] \leqslant time</tex>
 +
              <font color=green>// расписание составить невозможно</font>
 +
              '''return''' ''false''
 +
          '''else'''
 +
              <font color=green>// выполняем работу номер k</font>
 +
              <tex>S = S \setminus \{k\}</tex>
 +
              <tex>time = time + 1</tex>
 +
      '''return''' ''true''
 +
 
 +
Сложность алгоритма <tex>\mathcal{O}(n\log n)</tex>, если в качестве <tex>S</tex> использовать структуру, которая позволяет поиск элемента с минимальным <tex>d_i</tex> и его удаление за <tex>\mathcal{O}(\log n)</tex>, например [[Двоичная_куча|двоичная куча]].
 +
 
 +
====Доказательство корректности и оптимальности====
 +
 
 +
{{Теорема
 +
|statement=
 +
Приведенный алгоритм строит оптимальное расписание для задачи <tex>1 \mid d_i, p_i=1 \mid -</tex>.
 +
|proof=
 +
Докажем от противного. Пусть в оптимальном расписании, где все работы укладываются в дедлайны, сначала идет работа <tex>i</tex>, а потом <tex>j</tex>, причем <tex>d_i > d_j</tex>. Посмотрим, что произойдет, если в расписании поменять их местами. Так как они обе выполняются за единицу времени, для всех задач, кроме <tex>i</tex>-й и <tex>j</tex>-й время их завершения <tex>C_i</tex> не поменялось. Для <tex>j</tex>-й же работы <tex>C_j</tex> стало меньше, что только лучше. <tex>C_i</tex> увеличилось и стало равно старому <tex>C_j</tex> однако, раз <tex>j</tex>-я работа раньше укладывалась в дедлайн, то <tex>C_i = C_j^{old} \leqslant d_j < d_i</tex>, а значит и <tex>i</tex>-я работа все еще укладывается в свой дедлайн, и наша замена ничего не испортила.
 +
}}
 +
 
 +
===Исходная задача===
 +
 
 +
<tex>1 \mid r_i, d_i, p_i=1 \mid -</tex>
 +
 
 +
Теперь перейдем к решению основной задачи.
 +
 
 +
====Описание алгоритма====
 +
 
 +
Теперь не все задачи доступны изначально. Однако утверждается, что очередную задачу теперь достаточно выбирать с минимальным <tex>d_{i}</tex> из всех, которые уже доступны. Если эта работа уже просрочена, значит хорошее расписание построить нельзя.
 +
 
 +
====Псевдокод====
  
 
Пусть <tex>S</tex> — множество ещё не включенных в расписание работ, к выполнению которых уже можно приступить. Изначально <tex>S</tex> пустое.
 
Пусть <tex>S</tex> — множество ещё не включенных в расписание работ, к выполнению которых уже можно приступить. Изначально <tex>S</tex> пустое.
 
Отсортируем работы по порядку их появления.
 
Отсортируем работы по порядку их появления.
  
Алгоритм <tex>1 | r_{i}, d_{i}, p_{i}=1 | -</tex>:
+
  '''function''' solve(r: '''int'''[n], d: '''int'''[n]): '''boolean'''
  <tex>S = \varnothing;</tex>
+
      <tex>S = \varnothing</tex>
  <tex>j = 1;</tex>
+
      '''int '''<tex>j = 1</tex>
  <tex>time = 1;</tex>
+
      '''int '''<tex>time = 1</tex>
  '''for''' <tex> i = 1 </tex> '''to''' <tex> n </tex>:
+
      '''for''' <tex> i = 1 </tex> '''to''' <tex> n </tex>
      '''if''' <tex>S == \varnothing</tex>:
+
          '''if''' <tex>S == \varnothing</tex>
          <tex>time=r_{j}</tex>
+
              <tex>time=r[j]</tex>
      '''while''' <tex>time==r_{j}</tex>:
+
          '''while''' <tex>time==r[j]</tex>
          <tex>S \leftarrow S \cup \{j\}</tex>
+
              <tex>S = S \cup \{j\}</tex>
          <tex>j = j + 1;</tex>
+
              <tex>j = j + 1</tex>
      <tex>d_{k} = min\{d_{i}</tex> | <tex>i \in S\}</tex>
+
          <tex>d[k] = \min\{d[i]</tex> | <tex>i \in S\}</tex>
      '''if''' <tex>d_{k} \le time</tex>:
+
          '''if''' <tex>d[k] \leqslant time</tex>
          Расписание составить невозможно
+
              <font color=green>// расписание составить невозможно</font>
      '''else''':
+
              '''return''' ''false''
          <tex>S \leftarrow S \setminus \{k\}</tex>
+
          '''else'''
          <tex>time = time + 1;</tex>
+
              <font color=green>// выполняем работу номер k</font>
 +
              <tex>S = S \setminus \{k\}</tex>
 +
              <tex>time = time + 1</tex>
 +
      '''return''' ''true''
 +
 
 +
Сложность алгоритма <tex>\mathcal{O}(n\log n)</tex>, если в качестве <tex>S</tex> использовать структуру, которая позволяет поиск элемента с минимальным <tex>d_i</tex> и его удаление за <tex>\mathcal{O}(\log n)</tex>, например [[Двоичная_куча|двоичная куча]].
  
Сложность алгоритма <tex>O(n\log n)</tex>, если в качестве <tex>S</tex> использовать структуру, которая позволяет поиск элемента с минимальным <tex>d_{i}</tex> за <tex>O(\log n)</tex>.
+
====Доказательство корректности и оптимальности====
  
==Доказательство корректности алгоритма==
+
{{Теорема
 +
|statement=
 +
Приведенный алгоритм строит оптимальное расписание для задачи <tex>1 \mid r_i, d_i, p_i=1 \mid -</tex>.
 +
|proof=
 
Пусть с помощью нашего алгоритма составить хорошее расписание не удалось. Докажем, что в этом случае хорошего расписания не существует.
 
Пусть с помощью нашего алгоритма составить хорошее расписание не удалось. Докажем, что в этом случае хорошего расписания не существует.
 
Заметим, что расписание состоит из непрерывных блоков, между которыми есть пропуски — все поступившие работы выполнены, а новых работ ещё не появилось. Расписание может состоять из одного блока.
 
Заметим, что расписание состоит из непрерывных блоков, между которыми есть пропуски — все поступившие работы выполнены, а новых работ ещё не появилось. Расписание может состоять из одного блока.
  
Рассмотрим первый блок, для которого не получилось составить расписание. Возьмем в нём первую работу, для которой не нашлось места. Пусть её индекс будет равен <tex>k</tex>. Попробуем вставить эту работу в расписание. До блока её вставить нельзя, так как <tex>r_{i}</tex> больше или равно времени начала блока. А в блоке нет пропусков, поэтому нужно поменять её с какой-то <tex>i</tex>-й, которая уже стоит в этом блоке расписания. У всех таких работ <tex>d_{i} \le d_{k}</tex>, так как в алгоритме мы каждый раз брали работу с минимальным <tex>d_{i}</tex>. Но <tex>i</tex>-ю работу нельзя выполнить после <tex>k</tex>-й. Значит <tex>k</tex>-ю работу выполнить нельзя.
+
Рассмотрим первый блок, для которого не получилось составить расписание. Возьмем в нём первую работу, для которой не нашлось места. Пусть её индекс будет равен <tex>k</tex>. Попробуем вставить эту работу в расписание. До блока её вставить нельзя, так как <tex>r_i</tex> больше или равно времени начала блока. А в блоке нет пропусков, поэтому нужно поменять её с какой-то <tex>i</tex>-й, которая уже стоит в этом блоке расписания. У всех таких работ <tex>d_i \leqslant d_k</tex>, так как в алгоритме мы каждый раз брали работу с минимальным <tex>d_i</tex>. Но <tex>i</tex>-ю работу нельзя выполнить после <tex>k</tex>-й. Значит <tex>k</tex>-ю работу выполнить нельзя.
 +
}}
 +
 
 +
== См. также ==
 +
* [[1ripipsumwu|<tex>1 \mid r_i, p_i = p \mid \sum w_i U_i</tex>]]
 +
* [[1ripi1sumf|<tex>1 \mid r_i, p_i=1 \mid \sum f_i</tex>]]
  
 
==Источники информации==
 
==Источники информации==
* P. Brucker. Scheduling Algorithms (2006), 5th edition, стр. 200
+
* P. Brucker. «Scheduling Algorithms» (2006), 5th edition.
  
[[Категория: Дискретная математика и алгоритмы]]
+
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Теория расписаний]]
 
[[Категория: Теория расписаний]]

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

[math]1 \mid r_i, d_i, p_i=1 \mid -[/math]


Задача:
Дан один станок на котором нужно выполнить [math]n[/math] работ. Для каждой работы известны моменты времени, когда можно начинать её выполнять — [math]r_i[/math] и когда необходимо закончить её выполнение — [math]d_i[/math]. Время выполнения [math]p_i[/math] у всех работ одинаково и равно 1. Необходимо узнать, можно ли построить расписание, при котором все работы будут выполнены.


Алгоритм

Упрощенная версия

[math]1 \mid d_i, p_i=1 \mid -[/math]

Для начала решим более простую версию исходной задачи, когда все [math]r_i=1[/math].

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

Будем выполнять работы в порядке возрастания их дедлайна [math]d_i[/math]. Утверждается, что это оптимальное расписание. Приведем реализацию, на основе которой мы вскоре построим решение основной задачи:

Псевдокод

Пусть [math]S[/math] — множество еще не включенных в расписание задач. Изначально в нем находятся все задачи.

 function solve(d: int[n]): boolean
     [math]S = \{1, 2, \ldots, n\}[/math]
     int [math]time = 1[/math]
     for [math] i = 1 [/math] to [math] n [/math]
         [math]d[k] = \min\{d[i] \mid i \in S\}[/math]
         if [math]d[k] \leqslant time[/math]
             // расписание составить невозможно
             return false
         else
             // выполняем работу номер k
             [math]S = S \setminus \{k\}[/math]
             [math]time = time + 1[/math]
     return true 

Сложность алгоритма [math]\mathcal{O}(n\log n)[/math], если в качестве [math]S[/math] использовать структуру, которая позволяет поиск элемента с минимальным [math]d_i[/math] и его удаление за [math]\mathcal{O}(\log n)[/math], например двоичная куча.

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

Теорема:
Приведенный алгоритм строит оптимальное расписание для задачи [math]1 \mid d_i, p_i=1 \mid -[/math].
Доказательство:
[math]\triangleright[/math]
Докажем от противного. Пусть в оптимальном расписании, где все работы укладываются в дедлайны, сначала идет работа [math]i[/math], а потом [math]j[/math], причем [math]d_i \gt d_j[/math]. Посмотрим, что произойдет, если в расписании поменять их местами. Так как они обе выполняются за единицу времени, для всех задач, кроме [math]i[/math]-й и [math]j[/math]-й время их завершения [math]C_i[/math] не поменялось. Для [math]j[/math]-й же работы [math]C_j[/math] стало меньше, что только лучше. [math]C_i[/math] увеличилось и стало равно старому [math]C_j[/math] однако, раз [math]j[/math]-я работа раньше укладывалась в дедлайн, то [math]C_i = C_j^{old} \leqslant d_j \lt d_i[/math], а значит и [math]i[/math]-я работа все еще укладывается в свой дедлайн, и наша замена ничего не испортила.
[math]\triangleleft[/math]

Исходная задача

[math]1 \mid r_i, d_i, p_i=1 \mid -[/math]

Теперь перейдем к решению основной задачи.

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

Теперь не все задачи доступны изначально. Однако утверждается, что очередную задачу теперь достаточно выбирать с минимальным [math]d_{i}[/math] из всех, которые уже доступны. Если эта работа уже просрочена, значит хорошее расписание построить нельзя.

Псевдокод

Пусть [math]S[/math] — множество ещё не включенных в расписание работ, к выполнению которых уже можно приступить. Изначально [math]S[/math] пустое. Отсортируем работы по порядку их появления.

 function solve(r: int[n], d: int[n]): boolean
     [math]S = \varnothing[/math]
     int [math]j = 1[/math]
     int [math]time = 1[/math]
     for [math] i = 1 [/math] to [math] n [/math]
         if [math]S == \varnothing[/math]
             [math]time=r[j][/math]
         while [math]time==r[j][/math]
             [math]S = S \cup \{j\}[/math]
             [math]j = j + 1[/math]
         [math]d[k] = \min\{d[i][/math] | [math]i \in S\}[/math]
         if [math]d[k] \leqslant time[/math]
             // расписание составить невозможно
             return false
         else
             // выполняем работу номер k
             [math]S = S \setminus \{k\}[/math]
             [math]time = time + 1[/math]
     return true

Сложность алгоритма [math]\mathcal{O}(n\log n)[/math], если в качестве [math]S[/math] использовать структуру, которая позволяет поиск элемента с минимальным [math]d_i[/math] и его удаление за [math]\mathcal{O}(\log n)[/math], например двоичная куча.

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

Теорема:
Приведенный алгоритм строит оптимальное расписание для задачи [math]1 \mid r_i, d_i, p_i=1 \mid -[/math].
Доказательство:
[math]\triangleright[/math]

Пусть с помощью нашего алгоритма составить хорошее расписание не удалось. Докажем, что в этом случае хорошего расписания не существует. Заметим, что расписание состоит из непрерывных блоков, между которыми есть пропуски — все поступившие работы выполнены, а новых работ ещё не появилось. Расписание может состоять из одного блока.

Рассмотрим первый блок, для которого не получилось составить расписание. Возьмем в нём первую работу, для которой не нашлось места. Пусть её индекс будет равен [math]k[/math]. Попробуем вставить эту работу в расписание. До блока её вставить нельзя, так как [math]r_i[/math] больше или равно времени начала блока. А в блоке нет пропусков, поэтому нужно поменять её с какой-то [math]i[/math]-й, которая уже стоит в этом блоке расписания. У всех таких работ [math]d_i \leqslant d_k[/math], так как в алгоритме мы каждый раз брали работу с минимальным [math]d_i[/math]. Но [math]i[/math]-ю работу нельзя выполнить после [math]k[/math]-й. Значит [math]k[/math]-ю работу выполнить нельзя.
[math]\triangleleft[/math]

См. также

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

  • P. Brucker. «Scheduling Algorithms» (2006), 5th edition.