# Zadanie 7 - Lista 2
## Treść:
<img style="width: 100%" src="https://i.imgur.com/c6JxTR6.png" alt="Zadanie 7 Lista 2 2021">
## Rozwiązanie
### Algorytm:
1. Zainicjalizuj dwie zmienne $l=0,r=n-1$
2. Utwórz dwa zbiory:
2. Zbiór $O=\emptyset$ który będzie przechowywał optymalną kolejność wykoywania zadań
3. Zbiór $Z=\{a_i,b_i\},|Z|=n$ który będzie przechowywał pary z czasem wykonywania $i$-tego zadania na maszynach $A$ i $B$.
3. Dopóki zbiór $Z\ne \emptyset$:
3. Znajdź najkrótszy czas wykonywania pewnego zadania na dowolnej z maszyn, i zapisz go jako $p$ (W przypadku gdy takich zadań jest więcej niż jedno, weź pierwsze takie zadanie w zbiorze $Z$).
4. Dla każdego $i$ w zbiorze $Z$ wstaw za parę $\{a_i,b_i\}$, $\{a_i-p,b_i-p\}$
5. Dla każdego $i$ w zbiorze $Z$ jeżeli para jest postaci:
5.1 $\{0,b_i\}$ Dodaj tą parę do zbioru $O$ na indeksie $l$, usuń parę ze zbioru $Z$, $l+=1$.
5.2 $\{a_i,0\}$ Dodaj tą parę do zbioru $O$ na indeksie $r$, usuń parę ze zbioru $Z$, $r-=1$.
### Przykładowy przebieg algorytmu:
<div style="width: 100%; display: table;">
<div style="display: table-row">
<div style="display: table-cell">
1.
| $Z_i$ | $a_i$ | $b_i$ |
| -------- | -------- | -------- |
| a | **1** | 5 |
| b | 6 | 9 |
| c | 7 | 2 |
| d | 2 | 3 |
| e | 4 | 5 |
$O=\{\_,\_,\_,\_,\_\}$
</div>
<div style="display: table-cell">
2.
| $Z_i$ | $a_i$ | $b_i$ |
| -------- | -------- | -------- |
| _ | _ | _ |
| b | 5 | 8 |
| c | 6 | **1** |
| d | 1 | 2 |
| e | 3 | 4 |
$O=\{a,\_,\_,\_,\_\}$
</div>
<div style="display: table-cell">
3.
| $Z_i$ | $a_i$ | $b_i$ |
| -------- | -------- | -------- |
| _ | _ | _ |
| b | 4 | 7 |
| _ | _ | _ |
| d | **0** | 1 |
| e | 2 | 3 |
$O=\{a,\_,\_,\_,c\}$
</div>
<div style="display: table-cell">
4.
| $Z_i$ | $a_i$ | $b_i$ |
| -------- | -------- | -------- |
| _ | _ | _ |
| b | 4 | 7 |
| _ | _ | _ |
| _ | _ | _ |
| e | **2** | 3 |
$O=\{a,d,\_,\_,c\}$
</div>
<div style="display: table-cell">
5.
| $Z_i$ | $a_i$ | $b_i$ |
| -------- | -------- | -------- |
| _ | _ | _ |
| b | 2 | 5 |
| _ | _ | _ |
| _ | _ | _ |
| _ | _ | _ |
$O=\{a,d,e,b,c\}$
</div>
</div>
</div>
### Poprawność:
Algorytm ten korzysta z czterech lematów:
1. Mając pewne ustawienie zadań, zmiana ich kolejności w taki sposób aby kolejność wykonywania zadań na maszynie $A$ i na maszynie $B$, daje nam kolejność nie mniej optymalną niż oryginalna.
1.1 $\implies$ Kolejność wykonywania zadań na maszynie $A$ i $B$ będzie taka sama.
2. Mając pewne optymalne rozwiązanie dla zbioru $Z=\{a_i-p,b_i-p\}$ będzie ono również optymalne dla zbioru $Z=\{a_i,b_i\}$.
2.1$\implies$ Możemy uzyskać rozwiązanie optymalne poprzez odejmowanie w każdym kroku minimalnej wartości w zbiorze $Z$ od każdego z jego elementów.
3. Mając w $k$-tej pętli algorytmu dla pewnego zadania $i$, czasy $\{0,b_i\}$ możemy dodać to zadanie na pierwszą wolną pozycję w optymalnej kolejności wykonywania zadań, nie pogarszając optymalności naszego rozwiązania.
4. Mając w $k$-tej pętli algorytmu dla pewnego zadania $i$, czasy $\{a_i,0\}$ możemy dodać to zadanie na ostatnią wolną pozycję w optymalnej kolejności wykonywania zadań, nie pogarszając optymalności naszego rozwiązania.
3.1 $\land$ 4.1 $\implies$ Możemy rozwiązanie optymalne uzyskać poprzez budowanie go "od końców", wstawiajać odpowiednio zadania typu $3$ na pierwszej wolnej pozycji w zbiorze $O$, oraz zadania typu $4$ na ostatniej wolnej pozycji w zbiorze $O$.
### Dowody lematów:
#### Lemat 1:
Załóżmy, że mamy pewne rozwiązanie w którym na maszynie $A$ i na maszynie $B$, zadania nie są wykonywane w takiej samej kolejności, tj. istnieją pewne zadania $i$ oraz $j$ takie, że zadanie $j$ zostanie wykonane na maszynie $A$ wcześniej niż zadanie $j$, a na maszynie $B$ zadanie $j$ zostanie wykonane przed zadaniem $i$.
W takim wypadku możemy łatwo zauważyć, że zamieniając kolejność na maszynie $B$, tak aby zadanie $j$ wykonało się po zadaniu $i$, w żaden sposób nie wpłynie negatywnie na optymalność naszego rozwiązania. W najgorszym wypadku czas wykonywania zadań na maszynie $B$ nie ulegnie zmianie ($a_j$=0), w innych wypadkach zmniejszymy czas oczekiwania na maszynie $B$ w rezultacie uzyskując rozwiązanie bardziej optymalne.
```mermaid
gantt
title Przykład:
section Maszyna A
a_i :a1, 2023-04-30, 3d
a_k : 2d
a_j : 5d
section Maszyna B
b_k :2023-05-5, 4d
b_j :2023-05-10,4d
b_i : 8d
section A
a_i :a1, 2023-04-30, 3d
a_k : 2d
a_j : 5d
section B po zamianie
b_i : 2023-05-03, 8d
b_k : 2023-05-11, 4d
b_j :4d
```
#### Lemat 2:
Załóżmy, że mamy pewne optymalne rozwiązanie dla zadań o czasach $\{a_i,b_i\}$. Głównym czynnikiem wpływającym na optymalność naszego rozwiązania jest czas oczekiwania na maszynie $B$.
Korzystając z **Lematu 1** wiemy, że kolejność wykonywania zadań na maszynach $A$ i $B$ będzie taka sama.
Aby pokazać, że zwiększenie czasu wykonywania wszystkich zadań o pewne $p$, w żaden sposób nie wpłynie na czasy oczekiwania na maszynie $B$, rozważmy przypadek w którym wykonujemy kolejno zadania $i,j,k$ oraz $a_j>b_i$. Oznaczmy kolejno:
1. $\textrm{DUR}_{a_j}$ jako czas wykonywania zadania $a_j$, który będzie również czasem rozpoczęcia zadania $b_j$
2. $\textrm{DUR}_{b_i}$ jako czas wykonywania zadania $b_i$
3. $m$ jako czas oczekiwania na maszynie $B$ między zadaniami $b_i$ oraz $b_j$, gdzie wiemy, że $m=\textrm{DUR}_{a_j}-\textrm{DUR}_{b_i}$
Zwiększmy zatem czas wykonywania wszystkich zadań o $p$. Możemy zauważyć, zatem, że:
$\textrm{DUR}_{a_j+p}=\textrm{DUR}_{a_j}+p$
$\textrm{DUR}_{b_i+p}=\textrm{DUR}_{b_i}+p$
$m'=\textrm{DUR}_{a_j+p}-\textrm{DUR}_{b_i+p}=\textrm{DUR}_{a_j}+p-\textrm{DUR}_{b_i}-p=\textrm{DUR}_{a_j}-\textrm{DUR}_{b_i}=m$
Zatem możemy zauważyć, że zwieksząjąc czasy wykonywania wszystkich zadań o $p$, czas oczekiwania na maszynie $B$ nie ulegnie zmianie, $\implies$ rozwiązanie optymalne dla zadań postaci $\{a_i,b_i\}$ będzie nadal optymalne dla zadań postaci $\{a_i+p,b_i+p\}$.
> Czas wykonywania takich zadań wzrośnie dokładnie o $(n+1)\cdot p$, ponieważ czas wykonywania każdego zadania zwiększamy o $p$ $(+n\cdot p)$, oraz na rozpoczęcie wykonywania zadań na maszynie $B$ będziemy musieli dodatkowo poczekać $p$ $(+p)$.
```mermaid
gantt
title Przykład:
section Maszyna A
a_i :a1, 2020-04-30, 3d
a_j : 2020-05-03, 5d
a_k : 8d
section Maszyna B
b_i :2020-05-3,4d
m : 1s
b_j :2020-05-8,9d
b_k :2d
section Maszyna A + p
a_i+p :a1, 2020-04-30, 5d
a_j+p : 7d
a_k+p : 10d
section Maszyna B + p
b_i+p :2020-05-5,6d
m : 1s
b_j+p :2020-05-12,11d
b_k+p :4d
```
#### Lemat 3:
Załóżmy, że mamy zadanie $i$ postaci $\{0,b_i\}$. Niech $a_j$ będzie pierwszym zadaniem wykonywanym na maszynie $A$.
Rozpatrzmy 2 przypadki:
1. $b_i\leq a_j$ : W takim wypadku, ustawiając $i$-te zadanie jako pierwsze, w żaden sposób nie opóźnimy czasu rozpoczęcia wykonywania kolejnych zadań na maszynie $B$. (A zatem i czasu skończenia wykonywania zadań na maszynie $B$).
2. $b_i>a_j$ : W takim wypadku, ustawiając $i$-te zadanie jako pierwsze, mimo, że wydłużymy czas wykonywania wszystkich zadań na maszynie $B$, nadal jak najbardziej zwiększamy swoje szanse na "załatanie dziur" na maszynie $B$. Zatem istnieje szansa, że zmniejszymy sume czasów oczekiwania na maszynie $B$ i tym samym w możliwie najmniejszym stopniu wydłużymy czas działania maszyny $B$.
##### Dlaczego w k-tej pętli takie zadanie powinno trafić na pierwszą wolną pozycję w zbiorze przechowującą optymalną kolejność? (Budowanie $O$ "od końców")
Należy pamiętać o tym, że w $k$-tej pętli naszego algorytmu, zadanie postaci $\{a_i=0,b_i\}$ uzyskamy na skutek odejmowania kolejnych minimów ze zbioru $Z$.
Oznacza to, że $\{0,b_i\}=\{a_{\textrm{og }i}-p_1-p_2- \dots -p_k,b_{\textrm{ og }i}-p_1-p_2\dots -p_k\}$ gdzie oryginalnymi czasami wykonywania $i$-tego zadania było $\{a_{\textrm{og }i},b_{\textrm{og }i}\}\in Z$.
Zatem w $k$-tej pętli algorytmu będzie zachodzić $\forall \{a_j,b_j\} \in Z\setminus O: a_i<a_j$. Zatem $i$-te zadanie powinno zostać wykonane przed wszystkimi innymi zadaniami które pozostały w zbiorze $Z$, ponieważ zależy nam na jak najmniejszym opóźnieniu czasu rozpoczęcia wykonywania zadań na maszynie $B$.
Zatem ustawiając takie zadanie na pierwszej wolnej pozycji zbioru $O$, na mocy **lematu 2** uzyskamy również optymalne rozwiązanie dla oryginalnego zbioru $Z$.
```mermaid
gantt
title Przykład:
section Maszyna A
a_j :a1, 2020-04-30, 5d
a_k : 7d
a_l : 10d
section Maszyna B
b_j :2020-05-5,6d
b_k :2020-05-12,10d
b_l :4d
section A + ai=0
a_i :2020-04-30,1h
a_j :a1, 2020-04-30, 5d
a_k : 7d
a_l : 10d
section B + bi<aj
b_i :2020-04-30,3d
b_j :2020-05-5,6d
b_k :2020-05-12,10d
b_l :4d
section B + bi>aj
b_i :2020-04-30,7d
b_j :2020-05-7,6d
b_k :2020-05-13,10d
b_l :4d
```
#### Lemat 4:
Załóżmy, że mamy zadanie $i$ postaci $\{a_i,0\}$. Niech $b_l$ będzie ostatnim zadaniem wykonywanym na maszynie $B$.
Rozpatrzmy 2 przypadki:
1. $a_i\leq b_l$ : W takim wypadku, ustawiając $i$-te zadanie jako ostatnie, w żaden sposób nie opóźnimy czasu rozpoczęcia wykonywania kolejnych zadań na maszynie $B$. (A także ponieważ $b_i=0$ : czasu skończenia wykonywania zadań na maszynie $B$).
2. $a_i>b_l$ : W takim wypadku, ustawiając $i$-te zadanie jako ostatnie, mimo, że wydłużymy czas wykonywania wszystkich zadań na maszynie $B$, w żaden sposób nie opóźnimy czasu rozpoczęcia wykonywania wcześniejszych zadań na maszynie $B$, zatem w możliwie najmniejszym stopniu wydłużymy czas wykonywania zadań na maszynie $B$.
##### Dlaczego w k-tej pętli takie zadanie powinno trafić na ostatnią wolną pozycję w zbiorze przechowującą optymalną kolejność? (Budowanie $O$ "od końców")
Należy pamiętać o tym, że w $k$-tej pętli naszego algorytmu, zadanie postaci $\{a_i,b_i=0\}$ uzyskamy na skutek odejmowania kolejnych minimów ze zbioru $Z$.
Oznacza to, że $\{a_i,0\}=\{a_{\textrm{og }i}-p_1-p_2- \dots -p_k,b_{\textrm{ og }i}-p_1-p_2\dots -p_k\}$ gdzie oryginalnymi czasami wykonywania $i$-tego zadania było $\{a_{\textrm{og }i},b_{\textrm{og }i}\}\in Z$.
Zatem w $k$-tej pętli algorytmu będzie zachodzić $\forall \{a_j,b_j\} \in Z\setminus O: b_i<b_j$. Zatem $i$-te zadanie powinno zostać wykonane po wszystkich innych zadaniach które pozostały w zbiorze $Z$, ponieważ zależy nam na jak najmniejszym wydłużeniu czasu wykonywania zadań na maszynie $B$.
Zatem ustawiając takie zadanie na ostatniej wolnej pozycji zbioru $O$, na mocy **lematu 2** uzyskamy również optymalne rozwiązanie dla oryginalnego zbioru $Z$.
```mermaid
gantt
title Przykład:
section Maszyna A
a_j :a1, 2020-04-30, 5d
a_k : 7d
a_l : 10d
section Maszyna B
b_j :2020-05-5,6d
b_k :2020-05-12,10d
b_l :4d
section A + a[i]<b[l]
a_j :a1, 2020-04-30, 5d
a_k : 7d
a_l : 10d
a_i : 2d
section B + b[i]
b_j :2020-05-5,6d
b_k :2020-05-12,10d
b_l :4d
b_i=0 :1h
section A + a[i]>b[l]
a_j :a1, 2020-04-30, 5d
a_k : 7d
a_l : 10d
a_i : 5d
section B + b[i].
b_j :2020-05-5,6d
b_k :2020-05-12,10d
b_l :4d
b_i=0 :2020-05-27,2h