Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pa:laboratoare:laborator-09 [2022/05/03 13:53]
darius.neatu
pa:laboratoare:laborator-09 [2024/05/09 18:24] (current)
radu.nichita
Line 1: Line 1:
-Laborator 09: Drumuri minime în grafuri: ​sursă ​/ destinație unică. (1/2)+====== ​Laborator 09: Drumuri minime în grafuri: ​surse / destinații multiple. (2/2) ======
  
-$insert_partnership 
  
-## Obiective laborator+{{:​pa:​new_pa:​partners:​adobe-logo.png?​155 |}} 
 +\\ \\ \\ Changing the world through digital experiences is what Adobe’s all about. We give everyone - from emerging artists to global brands - everything they need to design and deliver exceptional digital experiences! We’re passionate about empowering people to create beautiful and powerful images, videos, and apps, and transform how companies interact with customers across every screen.
  
-În laboratorul 9 vom introduce contextul pentru **Shortest-paths problem** și vom studia **Single-source shortest-paths problem**, iar în laboratorul 10 vom continua cu **All-pairs shortest-paths problem**.+===== Obiective laborator =====
  
-Înțelegerea conceptelor de cost asociat unei muchii, relaxare a unei muchii. +În laboratorul anterior am introdus contextul pentru **Shortest-paths problem** și **Single-source shortest-paths problem**. În laboratorul 10 vom continua cu **All-pairs shortest-paths problem**.
-Prezentarea problemei drumului de cost minim (diverse variante). +
-Prezentarea algoritmilor pentru calculul drumurilor minime.+
  
-## Shortest-paths problem: single-source/​destination+  * Înțelegerea conceptelor de cost asociat unei muchii, relaxare a unei muchii. 
 +  * Prezentarea problemei drumului de cost minim (diverse variante). 
 +  * Prezentarea algoritmilor pentru calculul drumurilor minime.
  
-Vă rugăm să parcugeți [Shortest-paths problem](https://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​shortest-paths-problempentru a vă familiariza cu contextul, problema și notațiile folosite.+===== Shortest-paths problem: all-pairs ===== 
 + 
 +Vă rugăm să parcugeți [[https://​ocw.cs.pub.ro/​courses/​pa/​laboratoare/​shortest-paths-problem|Shortest-paths problem]] ​pentru a vă familiariza cu contextul, problema și notațiile folosite.
  
 Concepte necesare: Concepte necesare:
  
-* **cost muchie** / **edge cost**+  ​* **cost muchie** / **edge cost** 
 +  * **cost drum** / **path cost** 
 +  * **problema drumurilor minime: surse / destinații multiple** / **all-pairs shortest-paths problem** 
 +  * **relaxarea unei muchii** / **edge relaxation** 
 +  * **reconstruirea unui drum** / **RebuildPath**
  
-* **cost drum** / **path cost**+===== Algoritmi =====
  
-* **problema drumurilor minime: sursă / destinație unică** / **single-source/​destination ​shortest-paths problem**+În acest laborator vom studia ​**all-pairs shortest-paths problem**. Pentru această problemă, vom prezenta 2 algoritmi:
  
-* **relaxarea unei muchii** ​**edge relaxation**+  ​* **Roy-Floyd**:​ eficient pentru grafuri **dense** ($m >> n$ sau $|E| >> |V|$ - a.k.a. număr de muchii ​mult mai decât număr de noduri). 
 +  ​* **Johnson**: eficient pentru grafuri ​**rare** ($n >> m$ sau $|V| >> |E|$ - a.k.a. număr de noduri mult mai mare decât număr de muchii).
  
-* **reconstruirea unui drum** / **RebuildPath**+Vom prezenta fiecare algoritm, îl vom analiza, iar la final vom vedea când îl vom folosi pe fiecare.
  
-## Algoritmi +Puteți consulta capitolul ​**All-Pairs Shortest Paths** din **Introduction to Algorithms** [0] pentru mai multe detalii despre acești ​algoritmi.
-În acest laborator vom studia ​**single-source/​destination shortest-paths problem**. Pentru această problemă, vom prezenta 2 algoritmi:+
  
-* **Dijkstra**:​ presupune ca toate costurile din graf sunt nenegative. +===== Roy-Floyd =====
-* **Bellman-Ford**: permite costuri negative în graf, dar presupune că nu există cicluri de costuri negative.+
  
-Vom prezenta fiecare algoritm, îl vom analiza, iar la final vom vedea când îl vom folosi pe fiecare. +Algoritmul [[https://​en.wikipedia.org/​wiki/​Floyd%E2%80%93Warshall_algorithm|Roy-Floyd]] ​(**Roy-Floyd** / **Floyd–Warshall** algorithm) rezolvă **shortest-paths problem** în grafuri **G = (V, E)** care sunt **dense**.
- +
-Puteți consulta capitolul **Single-Source Shortest Paths** din **Introduction to Algorithms** [0] pentru mai multe detalii despre acești algoritmi. +
- +
-## Dijkstra +
-Algoritmul ​lui [Edsger Wybe **Dijkstra**](https://​en.wikipedia.org/​wiki/​Edsger_W._Dijkstra) ​(**Dijkstra'​s algorithm**) rezolvă **shortest-paths problem** în grafuri **G = (V, E)** cu costurile muchiilor ​**nenegative** ($w[u][v] \ge 0$).+
  
 +==== Roy-Floyd - Pseudocod ====
  
-### Dijsktra - Pseudocod +<​code ​cpp> 
-```cpp +// apply Roy-Floyd's algorithm ​for all-pairs shortest-paths problem
-// apply Dijkstra's algorithm ​from source+
 // //
-// source ​   = the source for the computing distances 
 // nodes     = list of all nodes from G // nodes     = list of all nodes from G
 // adj[node] = the adjacency list of node // adj[node] = the adjacency list of node
-//             ​example:​ adj[node] = {..., neigh, ...} => edge (node, neigh)+//             ​example:​ adj[node] = {..., neigh, ...} => edge (node, neigh) ​of cost w[node][neigh] 
 +//
 // //
 // returns: d, p // returns: d, p
-//          d = distance ​vector +//          d = distance ​matrix 
-//          p = parent ​vector+//          p = parent ​matrix
 // //
-Dijsktra(source, ​G=(nodes, adj)) { +Roy-Floyd(G=(nodes, adj)) { 
-  // STEP 0: initialize results +  // STEP 1: initialize results 
-  // d[node] = distance from source ​to node +  // d[i][j] = minimum ​distance from to j 
-  // p[node] = parent of node on the shortest path from source ​to node +  // p[i][j] = parent of node j, on shortest path from to j 
-  ​foreach ​(node in nodes) { +  ​for (i in nodes) { 
-      d[node] = +oo;                          // distance not yet computed +    for (j in nodes) { 
-      p[node] = null;                         ​// parent ​not yet found+      d[i][j] = w[i][j];                          // edge cost (or infinity if missing) 
 +      p[i][j] = (w[i][j] != infinity ? i : null); // parent ​(or null if missing) 
 +    }
   }   }
  
-  ​// STEP 1: initialize a priority queue +  // STEP 2: For each intermediar node k, 
-  pq = {}; +  // try to update shortest path from to j. 
- +  ​for (k in nodes{ 
-  ​// STEP 2: add the source(s) into q +    ​for ​(i in nodes{ 
-  ​d[source] = 0;                              ​// distance ​from source ​to source +      ​for ​(j in nodes) { 
-  ​p[source] = null;                           // source never has parent +        // Is (i -> ..-> kreunion with (k -> ... -> jshorter than (i -> ... -> j)? 
-  pq.push(source, d[source]); +        if (d[i][k] + d[k][j] < d[i][j]) { 
- +          d[i][j] = d[i][k] + d[k][j]; 
-  // STEP 3: start relaxation loop using the node(sfrom pq +          p[i][j] = p[k][j];
-  while (!pq.empty()) { +
-    // STEP 3.1: extract the next node (having the minimum estimate distance) +
-    node = pq.pop_min(); +
- +
-    // [optional] STEP 3.2: print/use the node +
- +
-    // STEP 3.3: relax all edges (node, neigh) +
-    foreach ​(neigh in adj[node]{ +
-        if (d[node] + w[node][neigh] < d[neigh]) {        // try to relax edge (node, neigh) +
-          d[neigh] = d[node] + w[node][neigh];            // update the new distance from source to neigh +
-          p[neigh] = node;                                // save parent +
- +
-          pq.push( (neigh, d[neigh]) )                    // replace distance for neigh in pq+
         }         }
 +      }
     }     }
   }   }
Line 93: Line 83:
  
 // Usage example: // Usage example:
-d, p = Dijkstra(source, ​G=(nodes, ​edges));+d, p = Roy-Floyd(G=(nodes, ​adj));
 // 1. Use distances from d // 1. Use distances from d
-// (e.g. d[node] = distance from source to node) 
-// 
 // 2. Rebuild path from node to source using parents (p) // 2. Rebuild path from node to source using parents (p)
 RebuildPath(source,​ destination,​ p); RebuildPath(source,​ destination,​ p);
-```+</​code>​ 
 +==== Exemple ====
  
-Acest algoritm menține un set de noduri (de exemplu, un min heap / priority queue) cu distanța minimă față de sursă fiind estimată. Cât timp mai există noduri pentru care distanța minimă față de sursă nu este finalizată,​ se extrage nodul $node$ pentru care estimarea este minimă și această se consideră finalizată. Deoarece acum distanța de la nodul sursă la $u$ este finală, se relaxează toate muchiile care pornesc din $node$, care sunt de forma $(node, neigh)$.+=== Exemplu Roy-Floyd ===
  
 +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-royfloyd-example.png?​512| Exemplu Roy-Floyd}}
  
-Pentru ca acest algoritm alege mereu "cel mai apropiat nod de sursă"​ spunem că **Algoritmul lui Dijsktra** este de tip greedy. Demonstrația se poate găsi în **Introduction to algorithms**.+Drumurile minime calculate ​de algoritmul Roy-Floyd sunt:
  
-### Exemple+|-|1|2|3|4| \\ 
 +|1|0|-1|-2|0| \\ 
 +|2|4|0|2|4| \\ 
 +|3|5|1|0|2| \\ 
 +|4|3|-1|1|0| \\
  
-#### Exemplu Dijkstra 
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-example.png?​512| Exemplu Dijkstra}} 
  
-Drumurile minime calculate de algoritmul lui Dijkstra sunt:+<spoiler Explicație pas cu pas> În exemplul atașat, avem un graf **orientat** cu următoare configurație:
  
-|node|1|2|3|4|5|6|7|8|9| #newrow +  * ''​%%n = 4%%'',​ ''​%%m = 5%%''​ 
-|d[node]|0|1|1|3|3|1|3|4|$+∞$| #newrow +  * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii. 
-|p[node]|null|1|1|3|4|1|6|5|null| #newrow+  * Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf. 
 +  * Construim tabloul **d** al distanțelor minime: 
 +    * **d[u][v] = w[u][v]**, dacă există muchia ​$(u, v)$. 
 +    * **d[u][v] = +∞, dacă **NU%%**%% există muchia ​$(u, v)$. 
 +    * **d[u][u] = 0**, convenție. 
 +    * Obținem matricea **d_0**:
  
-<spoiler Explicație pas cu pas> +|-|1|2|3|4| \\ 
-În exemplul atașat, avem un graf **orientat** cu următoare configurație:​+|1|0|+∞|-2|+∞| \\ 
 +|2|4|0|3|+∞| \\ 
 +|3|+∞|+∞|0|2| \\ 
 +|4|+∞|-1|+∞|0| \\
  
-* `n = 9`, `m = 10` 
-* Funcția de cost `w` are valorile menționate pe muchii. 
-* Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf. 
-* Alegem $source = 1$ și rulăm pas cu pas algoritmul. 
-* STEP 0: 
-  * $d[node] = +∞$, pentru $node = 1:9$ 
-  * $p[node] = null$, pentru $node = 1:9$ 
-* STEP 1: 
-  * $pq = \{\}$ (coadă de priorități în care vom băga elemente $(node, d[node])$, deci vom ordona nodurilor **crescător după distanța minimă față de sursă** cunoscută în acel moment). 
-* STEP 2: initializări pentru sursă 
-  * $d[1] = 0$ 
-  * $p[1] = null$ 
-  * $pq.push( (1, 0) )$ (inițial avem în coadă doar sura **1** cu distanța **0**). 
-* STEP 3: scoate câte un nod la fiecare pas și relaxăm toate muchiile care pornesc din acesta; ne oprim când coada e goală. 
-  * $pq = \{ (1, 0) \}$ => $pq.{pop\_min}()$ scoate $node = 1$. Relaxăm muchiile care pornesc din **1**. 
-    * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 + 1 < +∞$). **DA**, atunci **relaxarea muchiei are loc**: 
-      * $d[2] = d[1] + w[1][2] = 0 + 1 = 1$ (actualizăm distanța) 
-      * $p[2] = 1$ (actualizăm părintele de pe drum) 
-      * $pq.push( (2, 1) )$ (adăugăm nodul cu noua distanță, pentru a putea fi folosit la un pas ulterior) 
-    * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 + 1 < +∞$). DA, atunci relaxarea muchiei are loc: 
-      * $d[3] = 0 + 1 = 1$ 
-      * $p[3] = 1$ 
-      * $pq.push( (3, 1) )$ 
-    * $(1, 6)$: Verificăm dacă $d[1] + w[1][6] < d[6]$ (adică $0 + 1 < +∞$). DA, atunci relaxarea muchiei are loc: 
-      * $d[6] = 0 + 1 = 1$ 
-      * $p[6] = 1$ 
-      * $pq.push( (6, 1) )$ 
-  * $pq = \{ (2, 1); (3, 1); (6, 1);  \}$ => $pq.{pop\_min}()$ scoate $node = 2$. Relaxăm muchiile care pornesc din **2**. 
-    * $(2, 8)$: Verificăm dacă $d[2] + w[2][8] < d[8]$ (adică $1 + 10 < +∞$). DA, atunci relaxarea muchiei are loc: 
-      * $d[8] = 1 + 10 = 11$ 
-      * $p[8] = 2$ 
-      * $pq.push( (8, 11) )$ 
-  * $pq = \{ (3, 1); (6, 1); (8, 11);  \}$ => $pq.{pop\_min}()$ scoate $node = 3$. Relaxăm muchiile care pornesc din **3**. 
-    * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $1 + 2 < +∞$). DA, atunci relaxarea muchiei are loc: 
-      * $d[4] = 1 + 2 = 3$ 
-      * $p[4] = 3$ 
-      * $pq.push( (4, 3) )$ 
-  * $pq = \{ (6, 1); (4, 3) (8, 11);  \}$ => $pq.{pop\_min}()$ scoate $node = 6$. Relaxăm muchiile care pornesc din **6**. 
-    * $(6, 7)$: Verificăm dacă $d[6] + w[6][7] < d[7]$ (adică $1 + 2 < +∞$). DA, atunci relaxarea muchiei are loc: 
-      * $d[7] = 1 + 2 = 3$ 
-      * $p[7] = 6$ 
-      * $pq.push( (7, 3) )$ 
-  * $pq = \{ (4, 3); (7, 3) (8, 11);  \}$ => $pq.{pop\_min}()$ scoate $node = 4$. Relaxăm muchiile care pornesc din **4**. 
-    * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[5]$ (adică $3 + 0 < +∞$). DA, atunci relaxarea muchiei are loc: 
-      * $d[5] = 3 + 0 = 3$ 
-      * $p[5] = 4$ 
-      * $pq.push( (5, 3) )$ 
-  * $pq = \{ (5, 3); (7, 3) (8, 11);  \}$ => $pq.{pop\_min}()$ scoate $node = 5$. Relaxăm muchiile care pornesc din **5**. 
-    * $(5, 8)$: Verificăm dacă $d[5] + w[5][8] < d[8]$ (adică $3 + 1 < 11$). DA, atunci relaxarea muchiei are loc: 
-        * $d[8] = 3 + 1 = 4$ 
-        * $p[8] = 5$ 
-        * $pq.push( (8, 4) )$ (înlocuim nodul 8 din coadă ținînd cont de noua distanță!) 
-  * $pq = \{ (7, 3) (8, 4);  \}$ => $pq.{pop\_min}()$ scoate $node = 7$. Relaxăm muchiile care pornesc din **7**. 
-    * $(7, 8)$: Verificăm dacă $d[7] + w[7][8] < d[8]$ (adică $3 + 2 < 4$). **NU**, atunci relaxarea muchiei **NU** are loc (nu se trece la următorul pas). 
-  * $pq = \{ (8, 4);  \}$ => $pq.{pop\_min}()$ scoate $node = 8$. Nu există muchii care pornesc din **8**, nu avem ce relaxa la acest pas. 
-  * $pq = \{ \}$ => Coada este goală. STOP! 
-* Drumurile minime sunt finale (cele menționate anterior). 
  
-Observații:​+  * Rulăm algoritmul Roy-Floyd, vom scrie matricea pentru fiecare $ k = 1, 2, 3, 4$. 
 +    * $d_1$ (matricea dupa primul pas din algoritm; se modifică doar **d[2][3]**)
  
-Distanța minimă față de sursă pentru un nod poate fi recalculată de mai multe ori (de exemplu, nodul **8**). De fiecare dată nodul se reintroduce în coadă (împreună cu noua distanță). Pentru eficiență,​ înlocuim elementul curent, deoarece are asociată o distanță mai mare și nu ar mai fi folosită.+|-|1|2|3|4| \\ 
 +|1|0|+∞|-2|+∞| \\ 
 +|2|4|0|**2**|+∞| \\ 
 +|3|+∞|+∞|0|2| \\ 
 +|4|+∞|-1|+∞|0| \\
  
-* Mereu se extrage nodul cel mai apropiat de sursă. Distanța asociată cu el în acel moment este finală. În acest exemplu nodurile sunt scoase în ordinea: $1, 2, 3, 6, 4, 5, 7, 8$. 
  
-Nodul **9** nu este accesibil, datele pentru el rămân ca la inițializare.+  ​$d_2$ (matricea dupa primul pas din algoritm; se modifică doar **d[4][3]** și **d[4][1]**)
  
-</​spoiler>​+|-|1|2|3|4| \\ 
 +|1|0|+∞|-2|+∞| \\ 
 +|2|4|0|2|+∞| \\ 
 +|3|+∞|+∞|0|2| \\ 
 +|4|**3**|-1|**1**|0| v
  
-#### [Studiu de cazExemplu Dijkstra: costuri negative, rezultate eronate+  * $d_3$ (matricea dupa primul pas din algoritm; se modifică doar **d[1][4]** și **d[2][4]**)
  
-Un nod scos din coadă în algoritmul lui Dijkstra are distanță calculată și finală. Ca această abordare greedy să fie corectă, trebuie ca toate costurile din graf să fie **nenegative**.+|-|1|2|3|4| \\ 
 +|1|0|+∞|-2|**0**| \\ 
 +|2|4|0|2|**4**| \\ 
 +|3|+∞|+∞|0|2| \\ 
 +|4|3|-1|1|0| \\
  
-<spoiler Exemplu rezultate eronate aplicare Dijkstra pe graf cu costuri negative>​ 
  
-Vom analiza ​exemple. Pentru simplitatepresupunem ca $source = 1$.+  * $d_4$ (matricea dupa primul pas din algoritm; se modifică doar **d[1][2]****d[3][1]** și **d[3][2]**)
  
-În figura următoare avem o topologie simplă, pentru care se întâmplă ca algortimul Dijkstra sa producă vectorul de distanțe corect.+|-|1|2|3|4| \\ 
 +|1|0|**-1**|-2|0| \\ 
 +|2|4|0|2|4| \\ 
 +|3|**5**|**1**|0|2| \\ 
 +|4|3|-1|1|0| \\
  
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-negative-costs-example01.png?​512| Dijkstra cu costuri negative - exemplu 01}} 
  
-Dacă complicăm puțin topologia, de exemplu să adăugăm muchii astfel încât să forțăm actualizarea distanței de la sursă la sursă, obținem următoarea figură:+  * Drumurile minime sunt finale (cele menționate anterior - $d_4$).
  
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-negative-costs-example02.png?​512| Dijkstra cu costuri negative - exemplu 02}}+</spoiler> \\
  
-Când nodul **5** este scos din coadă la un moment dat, acesta va relaxa muschi $(5, 1)$ (pentru că $d[5] + w[5][1] = 1 - 2 = -1 < d[1]$). Acest lucru duce la $d[1] = -1$, adică reactualizarea distanței pentru un nod, care anterior a fost scos din coadă, deci distanța calculată era finală. Deci ajunge la o contradicție. 
  
-</​spoiler>​+==== Complexitate ====
  
-### Complexitate+  * **complexitate temporală**:​ $T = O(n^3)\ sau\ O(|V|^3)$ 
 +  * **complexitate spațială** : $S = O(1)$
  
-* **complexitate temporală**: $T = O(m * log n)\ sau\ O(|E| * log |V|)$+<spoiler Detalii (analiză + optimizări)>
  
-* **complexitate spațială** : $S = O(n)$+  * **complexitate temporală**:​ Se aplică recurența discutată anterior care pentru fiecare nod intermediar **k**, încearcă să actualizeze drumul minim de la **i** la **j**. Cele 3 foruri dau complexitatea temporară. 
 +  ​* **complexitate spațială** : Nu stocăm tablouri auxilare.
  
-<​spoiler ​Detalii (analiză + optimizări)>+</spoiler> ​\\
  
-* **complexitate temporală**:​ Se încearcă relaxarea tuturor celor **m** muchii din graf, care poate presupune o inserare în set / coada de priorități - $O(log n)$, deci $O(m * log n)$ în total pentru relaxări. În plus, se fac $n$ ștergeri, adică $O(n * log n)$, însă acest termen se poate neglija (presupunem că $n << m$). 
  
-* **complexitate spațială** : Se ține o coadă de priorități / un set cum maximum **n** noduri.+===== Johnson =====
  
-* **optimizare**:​ După cum se poate vedea, complexitatea este dată de numărul de relaxări ($m$ - de care nu putem scăpa, deoarece vrem să relaxăm toate muchiile ca să fim siguri că am găsit drumurile de lungime minimă) și de complexitatea operațiilor de ștergere / căutare / inserare în structura de date de tip priority queue. Putem încerca mai multe tipuri de heapuri - [Heap: Comparison of theoretic bounds for variants](https://​en.wikipedia.org/​wiki/​Heap_(data_structure)#​Comparison_of_theoretic_bounds_for_variants). +Algoritmul lui [[https://​en.wikipedia.org/​wiki/​Johnson%27s_algorithm|Johnson]] ​(**Johnson’s algorithm**) rezolvă **shortest-paths problem** în grafuri ​**G = (V, E)** care sunt **rare**.
-  ​* **Dijkstra cu heap binar** - $O(m log n)$: Soluția bazată pe un priority queue clasic de mai sus. +
-  ​* **Dijkstra cu heap Fibonacci** - $O(n logn + m)$: Cea mai rapidă implementare ​care se poate obține. Exemple în [Fibonacci Heaps and Dijkstra'​s Algorithm - A Visualization](https://​kbaile03.github.io/​projects/​fibo_dijk/​fibo_dijk.html).+
  
-</​spoiler>​+Ideea de la care pornește acest algoritm este de a rula cel mai rapid algorithm pentru **shortest-paths single source**, adică algoritmul lui Dijkstra, pentru fiecare sursă (nod) din graf. Dacă toate costurile sunt pozitive, putem face direct acest lucru. Dacă însă există costuri negative, nu putem aplica Dijkstra pe acest graf. Algoritmul lui Johnson face o preprocesare (în **ComputeH**) și calculează un graf echivalent, în care toate costurile sunt pozitive. Pe acest graf se poate aplica Dijkstra și să se afle toate distanțele. Ulterior se face translatarea inversă și se obțin **distanțele în graful inițial**.
  
-## Bellman-Ford +Observație: Dacă se știe că toate costurile ​din graf sunt pozitive ​(nenegative), algoritmul lui Johnson se poate înlocui cu rularea directă a algoritmului Dijkstra pentru toate nodurile din graf.
-Algoritmul Bellman-Ford a fost inițial propus de Alfonso Shimbel (1955), dar publicat (1956-1958) și numit ulterior după [Richard E. **Bellman**](https://​en.wikipedia.org/​wiki/​Richard_E._Bellman)  ​și [Lester Randolph **Ford** Jr. ](https://​en.wikipedia.org/​wiki/​L._R._Ford_Jr.). Acest algoritm rezolvă **shortest-paths problem** în grafuri **G = (V, E)** cu costurile ​muchiilor oarecare ​($w[u][v]$ poate fi și negativ), dar **fără** cicluri de cost negativ (în grafurile de cost negativ nu putem defini costul unui drum care conține un ciclu).+
  
-Algoritmul **detectează** prezența ciclurilor de costuri negative în graf. Dacă nu există cicluri de cost negativ în graf, acesta furnizează vectorul de distanțe $d$ și vectorul de părinți $p$ (ca și Dijkstra).+==== Johnson - Pseudocod ====
  
-### Bellman-Ford - Pseudocod +<​code ​cpp> 
-```cpp +// apply Johnson's algorithm ​for all-pairs shortest-paths problem
-// apply Bellman-Ford's algorithm ​from source+
 // //
-// source ​   = the source for the computing distances 
 // nodes     = list of all nodes from G // nodes     = list of all nodes from G
-// edges     = list of all edges from G+// adj[node] ​the adjacency ​list of node 
 +//             ​example:​ adj[node] = {..., neigh, ...} => edge (node, neigh) of cost w[node][neigh]
 // //
 // returns: has_cycle, d, p // returns: has_cycle, d, p
 //          has_cycle = negative cycle detection flag (true if found) //          has_cycle = negative cycle detection flag (true if found)
-//          d = distance ​vector ​(defined only if has_cycle == false) +//          d = distance ​matrix ​(defined only if has_cycle == false) 
-//          p = parent ​vector ​(defined only if has_cycle == false)+//          p = parent ​matrix ​(defined only if has_cycle == false)
 // //
-Bellman-Ford(source, ​G=(nodes, ​edges)) { +Johnson(G=(nodes, ​adj)) { 
-  // STEP 0initialize results +  // STEP 1Compute adjustment distances h (using Bellmand-Ford). 
-  ​// d[node] ​distance from source to node +  ​has_cycle, h ComputerH(G);​ 
-  ​// p[node] = parent of node on the shortest path from source to node +  ​if (has_cycle) { 
-  foreach ​(node in nodes) { +    ​return true, null, null;
-      ​d[node] = +oo;                          // distance not yet computed +
-      p(node) = null;                         // parent not yet found+
   }   }
  
-  ​// STEP 1: set distance and parent for source node +  // STEP 2: Update ​all costs in G to obtain all costs nonnegative. 
-  d[source] = 0;                              // distance from source to source +  foreach ((uv) in edges) { 
-  p[source] = null;                           // source never has parent +    if (w[u][v!= infinity) { 
- +      w[u][vw[u][v+ (h[u- h[v]);
-  ​// STEP 2: do |nodes| - 1 relaxations for all edges in G +
-  ​for (i = 1 : |nodes| ​ - 1)  { +
-    ​foreach ((nodeneigh) in edges) { +
-      if (d[node] + w[node][neigh] < d[neigh]) {   // try to relax edge (node, neigh) +
-        d[neigh= d[nodew[node][neigh];       // update the new distance from source to neigh +
-        p[neigh= node                          // save parent +
-      }+
     }     }
   }   }
  
-  // STEP 3: check if edge relaxations ​can still be made +  // STEP 3: Now all costs are nonnegative,​ so we can apply Dijsktra. 
-  foreach ((nodeneighin edges{ +  // Start Dijkstra for each source u, saving just distances. 
-    ​if (d[node+ w[node][neigh< d[neigh]) {   // try to relax edge (node, neigh) +  foreach (u in nodes) { 
-      ​// negative cycle detected! +    d_dijkstra, p_dijkstra = Dijkstra(uG)
-      return true, null, null;+ 
 +    // STEP 4: Compute distance (u, von initial graph. 
 +    ​foreach ​(v in nodes) { 
 +      ​d[u][v= d_dijkstra[v+ (h[v] - h[u]); 
 +      ​p[u][v] = p_dijkstra[v];
     }     }
   }   }
  
-  ​// STEP 4: no cycle detected +  return false, d, p;  // no negative cycles detected
-  ​return false, d, p;+
 } }
 +
 +ComputeH(G=(nodes,​ adj)){
 +  // STEP 0: Create a new **temporary** graph
 +  // * add a new node
 +  source = new node;
 +  new_nodes = nodes + { source };
 +  new_adj = adj;
 +  // * add a new edge (source, node) with cost 0 for all existing nodes
 +  for (node in nodes) {
 +    new_adj[source].push_back(node);​
 +    w[source][node] = 0;
 +  }
 +
 +  // STEP 1: Run Bellman-Ford on the new graph. Save just flag and distances.
 +  has_cycle, h, _ = Bellmann-Ford(source,​ new_G=(new_nodes,​ new_adj)))
 +  if (has_cycle) {
 +    return true, null; // negative cycle detected
 +  }
 +
 +  return false, d;
 +}
 +
  
 // Usage example: // Usage example:
-has_cycle, d, p = Bellman-Ford(source, G=(nodes, ​edges));+has_cycle, d, p = Johnson(source, G=(nodes, ​adj));
 if (has_cycle) { if (has_cycle) {
   print "Has Cycle!"​   print "Has Cycle!"​
Line 288: Line 257:
   // (e.g. d[node] = distance from source to node)   // (e.g. d[node] = distance from source to node)
   //   //
-  // 2. Rebuild path from node to source using parents (p)+  // 3. Rebuild path from node to source using parents (p)
   RebuildPath(source,​ destination,​ p);   RebuildPath(source,​ destination,​ p);
 } }
-```+</​code>​ 
 +==== Exemple ====
  
-### Exemple+=== Exemplu Johnson ===
  
-#### Exemplu Bellman-Ford+În această secțiune exemplificăm grafic cum rulează algoritmul lui Johnson pe un graf dat.
  
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-bellman-example01.png?​512| Exemplu ​Bellman-Ford}}+<spoiler Explicație pas cu pas> ​{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example01.png?​512| Exemplu ​Johnson 1/3}}
  
-Drumurile minime calculate de algoritmul lui Bellman-Ford sunt:+În exemplul atașat, avem un graf **orientat** cu următoare configurație:
  
-|node|1|2|3|4|5| #newrow +  * ''​%%n = 5%%'',​ ''​%%m = 8%%''​ 
-|d[node]|0|-2|-2|-3|-2| #newrow +  * Funcția de cost ''​%%w%%''​ are valorile menționate pe muchii. 
-|p[node]|null|4|1|3|4| #newrow+  * Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf. (Observăm că nu avem ciclu de cost negativ, deci are sens să rulăm un algoritm de drumuri minime). 
 +  * ''​%%STEP ​1%%'':​ Adăugăm un nod fictiv (exemplu nodul ''​%%6%%''​). Îl vom uni de fiecare nod din graful inițial (1, 2345) cu muchie de cost 0. Obținem graful din figura următoare:
  
-<spoiler Explicație pas cu pas> +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example02.png?​512| Exemplu Johnson - 2/3}}
-În exemplul atașat, avem un graf **orientat** cu următoare configurație:+
  
-`n = 5`, `m = 6` +  ​Pe acest graf putem rula o dată algoritmul ​Bellman-Fordconsiderând ​sursă ​noul nod adăugat
-* Funcția de cost `w` are valorile menționate pe muchii. +  * Obținem vectorul de distanț$h[node] = distanțade\ la\ nodul\ fictiv\ ​(6)\ la\ nodul\ node$
-* Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf+
-* Alegem $source = 1$ și rulăm pas cu pas algoritmul+
-* STEP 0: +
-  * $d[node] = +∞$pentru $node = 1:5n$ +
-  * $p[node] = null$, pentru $node = 1:5$ +
-* STEP 1: initializări pentru ​sursă +
-  * $d[1] = 0$ +
-  * $p[1] = null$ +
-* STEP 2: Avem 5 noduri, relaxăm de 4 ori ($n - 1$ ori) toate muchiile din graf. Ordinea nu contează, pentru simplitate vom relaxa muchiile în ordine lexico-grafică:​ $(1, 2); (1, 3); (2, 3); (3, 4); (4, 2); (4, 5)$+
-  * **iterație relaxare #1**: +
-    * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < +∞$). **DA**, atunci **relaxarea muchiei are loc**: +
-      * $d[2] = d[1] + w[1][2] = 0 - 1 = -1$ (actualizăm ​distanța+
-      * $p[2] = 1$ (actualizăm părintele ​de pe drum) +
-    * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < +∞$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[3] = 0 - 2 = -2$ +
-      * $p[3] = 1$ +
-    * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-1 + 0 < -2). **NU**, atunci relaxarea muchiei **NU** are loc. +
-    * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $-2 - 1 < +∞$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[4] = -2 - 1 = -3$ +
-      * $p[4] = 3$ +
-    * $(4, 2)$: Verificăm dacă $d[4] + w[4][2] < d[2]$ (adică $-3 + 1 < -1). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[2] = -3 + 1 = -2$ +
-      * $p[2] = 3$ +
-    * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[2]$ (adică $-3 + 1 < +∞$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[5] = -3 + 1 = -2$ +
-      * $p[5] = 4$ +
-  * **iterație relaxare #2**: Nu se va relaxa nicio o muchie. +
-  * **iterație relaxare #3**: Nu se va relaxa nicio o muchie. +
-* STEP 3: Se incearcă o etapă în plus (#4) de relaxări. Pentru că nu se mai poate relaxa muchii, înseamnă că distanțele găsite la `STEP 2` sunt finale și graful nu conține ciclu de cost negativ! +
-* STOP.+
  
-</​spoiler>​+|node|1|2|3|4|5|6| \\ 
 +|h[node]|-1|-7|-4|0|-2|0| \\
  
-#### [Studiu de caz] Exemplu Bellman-Ford:​ detecție ciclu de cost negativ 
  
-Dacă graful ​are un ciclu de cost negativse va putea plimba pe acel ciclu la infinit, reactulizând distanțele nodurilor implicate.+  * ''​%%STEP 2%%'':​ Revenim la graful ​inițial (cel cu 5 noduri8 muchii) și îi alterăm costurile:​ 
 +    * $w[1][4] = w[1][4] + (h[1] - h[4]) = 2 + [ (-1) - (0) ] = 1$ 
 +    * $w[2][1] = w[2][1] + (h[2] - h[1]) = 6 + [ (-7) - (-1) ] = 0$ 
 +    * $w[2][3] = w[2][3] + (h[2] - h[3]) = 3 + [ (-7) - (-4) ] = 0$ 
 +    * $w[3][1] = w[3][1] + (h[3] - h[1]) = 4 + [ (-4) - (-1) ] = 1$ 
 +    * $w[3][4] = w[3][4] + (h[3] - h[4]) = 5 + [ (-4) - (0) ] = 1$ 
 +    * $w[4][2] = w[4][2] + (h[4] - h[2]) = -7 + [ (0) - (-7) ] = 0$ 
 +    * $w[4][5] = w[4][2] + (h[4] - h[2]) = -2 + [ (0) - (-2) ] = 0$ 
 +    * $w[5][4] = w[5][4] + (h[5] - h[4]) = -1 + [ (-2) - (-4) ] = 1$ 
 +  * Obținem graful din următoarea figură:
  
-<spoiler Exemplu detecție ciclu de cost negativ cu Bellman-Ford>​ +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab10-graph-johnson-example03.png?512| Exemplu ​Johnson ​2/3}}
-{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-bellman-example02.png?512| Exemplu ​Bellman-Ford - ciclu negativ}}+
  
-În exemplul atașatavem un graf **orientat** cu următoare configurație:+  * ''​%%STEP 3%%'':​ Deoarece toate costurile sunt pozitive, putem rula Dijkstra pe rând, pentru fiecare sursă ''​%%source = 1, 2, 3, 45%%''​ din graf
 +  ​''​%%STEP 3%%'':​ $ source = 1 $ 
 +    ​Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    ​Distanțele față de nodul ''​%%1%%''​ pe graful inițial sunt: 
 +      * $d[1][1] = 0$ 
 +      * $d[1][2] = d_{dijkstra}[2] + (h[2] - h[1]) = 1 + [ (-7) - (-1) ] = -5$ 
 +      * $d[1][3] = d_{dijkstra}[3] + (h[3] - h[1]) = 1 + [ (-4) - (-1) ] = -2$ 
 +      * $d[1][4] = d_{dijkstra}[4] + (h[4] - h[1]) = 1 + [ (0) - (-1) ] = 2$ 
 +      * $d[1][5] = d_{dijkstra}[5] + (h[5] - h[1]) = 1 + [ (-2) - (-1) ] = 0$
  
-* `n = 5`, `m = 6` +|node|1|2|3|4|5| \\ 
-* Funcția de cost `w` are valorile menționate pe muchii. +|d_dijkstra[node]|0|1|1|1|1| \\
-* Avem mai multe drumuri de cost diferite între diverse perechi de noduri din graf. +
-* Alegem $source = 1$ și rulăm pas cu pas algoritmul. +
-* STEP 0: +
-  * $d[node] = +∞$, pentru $node 1:5n$ +
-  * $p[node] = null$, pentru $node = 1:5$ +
-* STEP 1: initializări pentru sursă +
-  * $d[1] = 0$ +
-  * $p[1] = null$ +
-* STEP 2: Avem 5 noduri, relaxăm de 4 ori ($n - 1$ ori) toate muchiile din graf. Ordinea nu contează, pentru simplitate vom relaxa muchiile în ordine lexico-grafică:​ $(1, 2); (1, 3); (2, 3); (3, 4); (4, 2); (45)$. +
-  * **iterație relaxare #1**: +
-    * $(1, 2)$: Verificăm dacă $d[1+ w[1][2] < d[2]$ (adică $< +∞$). **DA**, atunci **relaxarea muchiei are loc**: +
-      * $d[2] = d[1] + w[1][2] = 0 - = -1$ (actualizăm distanța) +
-      * $p[2] = 1$ (actualizăm părintele de pe drum) +
-    * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < +∞$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[3] = 0 - 2 = -2$ +
-      * $p[3] = 1$ +
-    * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-1 - 2 < -2$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[3] = -1 -2 = -3$ +
-      * $p[3] = 2$ +
-    * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $-3 - 1 < +∞$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[4] = -3 - 1 = -4$ +
-      * $p[4] = 3$ +
-    * $(4, 2)$: Verificăm dacă $d[4] + w[4][2] < d[2]$ (adică $-4 + 1 < -1$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[2] = -3 + 1 = -2$ +
-      * $p[2] = 4$ +
-    * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[2]$ (adică $-3 + 1 < +∞$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[5] = -3 + 1 = -2$ +
-      * $p[5] = 4$ +
-  * **iterație relaxare #2**: +
-    * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < -2$). **NU**, atunci relaxarea muchiei **NU** are loc. +
-    * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < -3$). **NU**, atunci relaxarea muchiei **NU** are loc. +
-    * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-2 - 2 < -2$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[3] = -2 -2 = -4$ +
-      * $p[3] = 2$ +
-    * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $-4 - 1 < -4$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[4] = -4 - 1 = -5$ +
-      * $p[4] = 3$ +
-    * $(4, 2)$: Verificăm dacă $d[4] + w[4][2] < d[2]$ (adică $-5 + 1 < -2$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[2] = -5 + 1 = -4$ +
-      * $p[2] = 4$ +
-    * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[2]$ (adică $-5 + 1 < -2$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[5] = -5 + 1 = -4$ +
-      * $p[5] = 4$ +
-  * **iterație relaxare #3**: +
-    * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < -4$). **NU**, atunci relaxarea muchiei **NU** are loc. +
-    * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < -4$). **NU**, atunci relaxarea muchiei **NU** are loc. +
-    * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-4 - 2 < -4$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[3] = -4 -2 = -6$ +
-      * $p[3] = 2$ +
-    * $(3, 4)$: Verificăm dacă $d[3] + w[3][4] < d[4]$ (adică $-6 - 1 < -5$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[4] = -6 - 1 = -7$ +
-      * $p[4] = 3$ +
-    * $(4, 2)$: Verificăm dacă $d[4] + w[4][2] < d[2]$ (adică $-7 + 1 < -4$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[2] = -7 + 1 = -6$ +
-      * $p[2] = 4$ +
-    * $(4, 5)$: Verificăm dacă $d[4] + w[4][5] < d[2]$ (adică $-7 + 1 < -4$). **DA**, atunci relaxarea muchiei are loc: +
-      * $d[5] = -7 + 1 = -6$ +
-      * $p[5] = 4$ +
-* STEP 3: Se incearcă o etapă în plus (#4) de relaxări. +
-  * **iterație relaxare #4**: +
-    * $(1, 2)$: Verificăm dacă $d[1] + w[1][2] < d[2]$ (adică $0 - 1 < -6$). **NU**, atunci relaxarea muchiei **NU** are loc. +
-    * $(1, 3)$: Verificăm dacă $d[1] + w[1][3] < d[3]$ (adică $0 - 2 < -6$). **NU**, atunci relaxarea muchiei **NU** are loc. +
-    * $(2, 3)$: Verificăm dacă $d[2] + w[2][3] < d[3]$ (adică $-6 - 2 < -6$). **DA**, atunci relaxarea muchiei **se poate face**. +
-      * În acest moment putem trage **concluzia că avem un ciclu de cost negativ în graf**, căci altfel distanțele ar fi fost finale și nu am mai fi putut relaxa ceva în `STEP 3`. +
-* STOP.+
  
-</​spoiler>​ 
  
-### Complexitate+|node|1|2|3|4|5| \\ 
 +|d[1][node]|0|-5|-2|2|0| \\
  
-* **complexitate temporală**:​ $T = O(n * m)\ sau\ O(|V| * |E|)$ 
  
-* **complexitate spațială** $O(1)$+  ​''​%%STEP 3%%'':​ $ source = 2 $ 
 +    ​Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    ​Distanțele față de nodul ''​%%2%%''​ pe graful inițial sunt: 
 +      ​$d[2][1] = d_{dijkstra}[1] + (h[1] - h[2]) = 0 + [ (-1) - (-7) ] = 6$ 
 +      ​* $d[2][2] ​0$ 
 +      * $d[2][3] = d_{dijkstra}[3] + (h[3] - h[2]) = 0 + [ (-4) - (-7) ] = 3$ 
 +      * $d[2][4] = d_{dijkstra}[4] + (h[4] - h[2]) = + [ (0- (-7) ] = 8$ 
 +      * $d[2][5] = d_{dijkstra}[5] + (h[5] - h[2]) = 1 + [ (-2) - (-7) ] = 6$
  
-<spoiler Detalii (analiză + optimizări)>​+|node|1|2|3|4|5| \\ 
 +|d_dijkstra[node]|0|0|0|1|1| \\
  
-* **complexitate temporală**:​ Algoritmul relaxează de **n** ori toate cele **m** muchii din graf. 
  
-* **complexitate spațială** :  Nu avem memorie spațială auxialiară. ATENȚIE! Vom aloca tablourile $d$ / $p$, însă acestea nu sunt specifice algoritmului.+|node|1|2|3|4|5| \\ 
 +|d[2][node]|6|0|3|8|6| \\
  
-* **optimizare**:​ Se poate obține o performanță mai bună în practică, dar cu aceeași complexitate asimptotică,​ folosind o coadă: 
-  * Dacă $d[node]$ nu a fost schimbată la un pas, folosirea muchiilor care pornesc din $node$ nu produce efecte. 
-  * Se poate ţine o coadă de noduri, la fiecare pas scoţând un element din aceasta. Se va încerca relaxarea muchiilor care pornesc din nodul scos. Nodurile cu distanțe actualizate se reintroduc în coadă. 
-  * Dacă un nod a fost scos de **n** ori din coadă, atunci graful conține cel puțin un ciclu de cost negativ. 
  
-</​spoiler>​+  * ''​%%STEP 3%%'':​ $ source = 3 $ 
 +    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    * Distanțele față de nodul ''​%%3%%''​ pe graful inițial sunt: 
 +      * $d[3][1] = d_{dijkstra}[1] + (h[1] - h[3]) = 1 + [ (-1) - (-4) ] = 4$ 
 +      * $d[3][2] = d_{dijkstra}[2] + (h[2] - h[3]) = 1 + [ (-7) - (-4) ] = -2$ 
 +      * $d[3][3] = 0$ 
 +      * $d[3][4] = d_{dijkstra}[4] + (h[4] - h[3]) = 1 + [ (0) - (-4) ] = 5$ 
 +      * $d[3][5] = d_{dijkstra}[5] + (h[5] - h[3]) = 1 + [ (-2) - (-4) ] = 3$
  
-## TLDR+|node|1|2|3|4|5| \\ 
 +|d_dijkstra[node]|1|1|0|1|1| \\
  
-* Pentru **topologii particulare** ale problemei **shortest-paths**,​ vom folosi mereu un algoritm baza pe o parcugere (de exemplu, sortare topologic la DAG). Folosirea algoritmilor Dijkstra / Bellman-Ford ar duce la o soluție ineficientă pentru aceste cazuri particulare! 
  
-* Pe **topologii generale**, pentru **single-source shortest-paths problem**, vom folosi Dijkstra / Bellman-Ford. +|node|1|2|3|4|5| \\ 
-  * **Dijkstra** are complexitate mai bună, deci este de preferat, însă acesta se poate folosi doar pe grafuri cu costuri **nenegative**. +|d[3][node]|4|-2|0|5|3| \\
-  * Dacă avem muchii cu costuri negative, suntem obligați să folosim **Bellman-Ford**, care ne poate detecta și prezența ciclurilor de cost negativ.+
  
-* Dacă avem mai multe surse / destinații,​ putem rula pentru fiecare sursă Dijkstra /​Bellman-Ford,​ obținând complexitate $O(n * m * log n)$ / $O(n^2 * m)$. Vom vedea în laboratorul următor cum putem îmbunătăți acest lucru. 
  
-## Exerciții+  * ''​%%STEP 3%%'':​ $ source = 4 $ 
 +    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    * Distanțele față de nodul ''​%%4%%''​ pe graful inițial sunt: 
 +      * $d[4][1] = d_{dijkstra}[1] + (h[1] - h[4]) = 0 + [ (-1) - (0) ] = -1$ 
 +      * $d[4][2] = d_{dijkstra}[2] + (h[2] - h[4]) = 0 + [ (-7) - (0) ] = -7$ 
 +      * $d[4][3] = d_{dijkstra}[4] + (h[3] - h[4]) = 0 + [ (-4) - (0) ] = -4$ 
 +      * $d[4][4] = 0$ 
 +      * $d[4][5] = d_{dijkstra}[5] + (h[5] - h[4]) = 0 + [ (-2) - (0) ] = 2$
  
-<​note>​ +|node|1|2|3|4|5| \\ 
-Scheletul de laborator se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab09|pa-lab::​skel/​lab09]]. +|d_dijkstra[node]|0|0|0|0|0| \\
-</​note>​+
  
-<note warning> 
-Înainte de a rezolva exercițiile,​ asigurați-vă că ați citit și înțeles toate precizările din secțiunea 
-[[https://​ocw.cs.pub.ro/​courses/​pa/​skel_graph | Precizari laboratoare 07-12]]. 
  
-Prin citirea acestor precizări vă asigurați că:+|node|1|2|3|4|5| \\ 
 +|d[4][node]|-1|-7|-4|0|2| \\
  
-* știți **convențiile** folosite 
  
-evitaț**buguri**+  ​''​%%STEP 3%%'':​ $ source = 5 $ 
 +    * Vectorul de distanțe este ''​%%d_dijkstra%%''​ atașat mai jos. 
 +    * Distanțele față de nodul ''​%%4%%''​ pe graful inițial sunt: 
 +      * $d[5][1] = d_{dijkstra}[1] + (h[1] - h[5]) = 2 + [ (-1) - (-2) ] = 3$ 
 +      ​$d[5][2] = d_{dijkstra}[2] + (h[2] - h[5]) = 2 + [ (-7) - (-2) ] = -3$ 
 +      ​$d[5][3] = d_{dijkstra}[4] + (h[3] - h[5]) = 1 + [ (-4) - (-2) ] = -1$ 
 +      ​$d[5][4] = d_{dijkstra}[5] + (h[4] - h[5]) = 2 + [ (0) - (-2) ] = 4$ 
 +      ​$d[5][5] = 0$
  
-* evitați **depunctări** la lab/​teme/​test+|node|1|2|3|4|5| \\ 
 +|d_dijkstra[node]|2|2|1|2|0| \\
  
-</​note>​ 
  
-### Dijkstra+|node|1|2|3|4|5| \\ 
 +|d[5][node]|3|-3|-1|4|0| \\
  
-Se dă un graf **orientat** cu **n** noduri și **m** arce. Graful are pe arce **costuri pozitive** (nenegative). 
  
-Folosiți **Dijkstra** pentru a găsi **costul minim** (**lungimea minimă**) a unui drum de la o sursă dată (**source**) la toate celelalte ​$n - 1noduri din graf.+  * STOP! Am obținut toate distanțele ​$d[u][v]cerute!
  
-<note warning>+</spoiler\\
  
-Restricții și precizări: 
  
-* $ n <50.000 ​+==== Complexitate ==== 
-* $ m <= 2.10^5 + 
-* $ 0 <= c <= 20.000$, unde c este costul/​lungimea unui arc +  ​**complexitate temporală**: ​$O(n * m * log (n))\ sau\ O(|V| * |E| * log (|V|))
-timp de execuție +  **complexitate spațială** : $S = O(n + m)\ sau \ O(|V| + |E|)$ 
-  ​C++: `1s` + 
-  * Java`2s` +<spoiler Detalii (analiză + optimizări)>​ 
-</note>+ 
 +  * **complexitate temporală**:​ 
 +    * **ComputeH**:​ 
 +      * Construire graf nou - $O(n + m))$. 
 +      ​Aplicare Bellman-Ford pe noul graf - $O(n * m)$. 
 +    **Update edges** - pasul se face în $O(m)$. 
 +    * Rularea Dijkstra pentru fiecare nod din graf - este complexitatea de la rulare Dijkstra pentru un singur nod sursă (a.k.a. ​$O(m log n)$)multiplicată cu numărul de noduri. 
 +    În final ajungem la un total de $O(n m * log(n) ​n * m n + m) = O(n * m + log(n))$. 
 +  * **complexitate spațială** ​Se construiește un alt graf (**new_node** - n, **new_adj** - m), se produc câțiva vectori temporari de lungime n (**h**, **d_dijkstra**,​ **p_dijkstra**). 
 +  * **optimizare**Deoarece Dijsktra se poate optimiza (vezi laborator anterior), putem obține $O(n^2 * log n + n * m)$ complexitatea pentru ultima etapă. Complexitatea finală este $O(n ^ 2 log (m) + n * m)$. 
 + 
 +</spoiler\\ 
 + 
 + 
 +===== TLDR ===== 
 + 
 +  * Pentru cazul **shortest-path single source** am studiat în laboratorul anterior algoritmul lui Dijkstra / algoritmul Bellman-Ford. 
 +  * Pentru cazul **shortest-path all-pairs**,​ discuția se facă după numărul de muchii din graf: 
 +    * **graf dens**: aplicăm Roy-Floyd și obținem $O(n ^ 3)$. 
 +    * **graf rar**: aplicăm Johnson și obținem $O(n * m * log (n))$. 
 + 
 +===== Exerciții ===== 
  
 <​note>​ <​note>​
  
-Rezultatul ​se va returna sub forma unui vector **d** cu **n + 1** elemente.+Scheletul de laborator ​se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab10|pa-lab::​skel/​lab10]].
  
-Convenție:+</​note>​ 
 +<note warning>
  
-* $d[node]$ = costul minim lungimea minimă a unui drum de la **source** la nodul **node**+Înainte de a rezolva exercițiile,​ asigurați-vă că ați citit și înțeles toate precizările din secțiunea ​[[https://​ocw.cs.pub.ro/​courses/​pa/​skel_graph | Precizari laboratoare 07-12]]. 
 + 
 +Prin citirea acestor precizări vă asigurați că:
  
-$d[source] = 0$+  ​știți **convențiile** folosite 
 +  * evitați **buguri** 
 +  * evitați **depunctări** la lab/​teme/​test
  
-* $d[node] = -1$, dacă nu se poate ajunge de la **source** la **node** 
  
-$d[0]$ nu este folosit, deci ca fi initializat cu 0! (am pastrat indexarea nodurilor de la 1). 
 </​note>​ </​note>​
  
-### RebuildPath +==== Roy-Floyd ====
-S-a rulat anterior un algoritm oarecare de calculare a drumurilor de lungime minimă într-un graf cu **n** noduri și **m** arce folosind sursa **source**. Se cunoaște vectorul de părinți $p$ rezultat:+
  
-* $p[node]$ = părintele lui node de pe drumul minim de la sursă la node. +Se dă un graf **orientat** cu **n** noduri. Graful ​are **costuri strict pozitive**
-$p[source] = 0$ - sursa nu are părinte+ 
-$p[node] = 0$ nodul $node$ nu este accesibil din $source$.+Se dă matricea ponderilor - **w**, se cere matricea drumurilor minime ​**d**, aplicând algoritmul **Roy-Floyd**.
  
-Se cere să se reconstituie drumul de la nodul **source** la nodul **destination**. 
  
 <note warning> <note warning>
Line 517: Line 448:
 Restricții și precizări: Restricții și precizări:
  
-* $ n <= 50.000 $ +  ​* $ n <= 100 $ 
-* timp de execuție +  * $ 0 <= c <= 1.000$, unde c este costul unui arc. 
-  * C++: `1s` +    * Dacă **nu există arc** între o pereche de noduri x și y, distanța de la nodul x la nodul y din **matricea ponderilor** va fi 0. 
-  * Java: `2s` +    * Dacă după aplicarea algoritmului **nu se găsește drum** pentru o pereche de noduri x și y, se va considera **distanța** dintre ele egală cu 0 (se stochează în **matricea distantelor** valoarea 0). 
-</​note>​+    * Drumul de la nodul i la nodul i are lungime 0 (prin convenție). 
 +  ​* timp de execuție 
 +    * C++: 1s 
 +    * Java: 8s
  
-<​note>​ 
- 
-Rezultatul se va returna sub forma unui vector **path**: $path = (source, ..., destination)$. 
  
 </​note>​ </​note>​
  
-### Bellman-Ford +==== Johnson ==== 
-Se dă un graf **orientat** cu **n** noduri ​și **m** arce. Graful are pe arce **costuri oarecare** (pozitive sau negative).+ 
 +Se dă un graf **orientat** cu **n** noduri. Graful are **costuri oarecare** (pot fi și negative)
 + 
 +Se dă lista de adiacență cu costurile aferente, se cere matricea drumurilor minime - **d**, aplicând algoritmul **Johnson**.
  
-Folosiți **Bellman-Ford** pentru a găsi **costul minim** (**lungimea minimă**) a unui drum de la o sursă dată (**source**) la toate celelalte $n - 1$ noduri din graf. Se va folosi aceeași convenție de reprezentare a vectorului de distanțe $d$. 
  
 <note warning> <note warning>
Line 538: Line 471:
 Restricții și precizări: Restricții și precizări:
  
-* $ n <= 50.000 ​+  ​* $ n <= 1000 
-* $ m <= 2.5 * 10^5 +  * $ m <= 25000 
-* $ -1000 <= c <= +1000$, unde c este costul/​lungimea ​unui arc +  * $ -1000 <= c <= 1.000$, unde c este costul unui arc. 
-* timp de execuție +    * Dacă **nu există arc** între o pereche de noduri x și y, distanța de la nodul x la nodul y din **matricea ponderilor** va fi 0. 
-  * C++: `1s` +    * Dacă după aplicarea algoritmului **nu se găsește drum** pentru o pereche de noduri x și y, se va considera **distanța** dintre ele egală cu 0 (se stochează în **matricea distantelor** valoarea 0). 
-  * Java: `2s`+    * Drumul de la nodul i la nodul i are lungime 0 (prin convenție). 
 +    * Dacă graful conține un ciclu de cost negativ, se va afișa mesajul: Ciclu negativ! 
 +  ​* timp de execuție 
 +    * C++: 1s 
 +    * Java: 8s 
 + 
 </​note>​ </​note>​
  
-### BONUS+==== BONUS ==== 
 La acest laborator, asistentul va alege 1-2 probleme din secțiunea extra. La acest laborator, asistentul va alege 1-2 probleme din secțiunea extra.
  
-### Extra +==== Extra ==== 
-* [infoarena/​distante](https://​infoarena.ro/​problema/​distante) + 
-* [infoarena/catun](https://​infoarena.ro/​problema/​catun) +  ​* [[https://​infoarena.ro/​problema/​rfinv|infoarena/rfinv]
-* [infoarena/lanterna](https://​infoarena.ro/​problema/​lanterna) +  * [[https://​infoarena.ro/​problema/​rf|infoarena/rf]
-* [infoarena/ciclu](https://infoarena.ro/problema/ciclu) +  * [[https://​infoarena.ro/​problema/​coach|infoarena/coach]
-* [codeforces/the-classic-probem](https://​codeforces.com/​contest/​464/problem/E) +  * [[https://codeforces.com/contest/295/​problem/​B|codeforces/greg-and-graph]
-* [codeforces/planets](https://​codeforces.com/​contest/229/problem/B) +  * [[https://​codeforces.com/​contest/​25/problem/C|codeforces/roads-in-berland]
-* [acm/​sightseeingtrip](http://poj.org/problem?id=1734)+  * [[https://​codeforces.com/​problemset/problem/21/​D|codeforces/​traveling-graph]] 
 +  * [[https://codeforces.com/​gym/​101498/problem/​L|codeforces/​the-shortest-path]]
  
-## Referințe+===== Referințe ​=====
  
 [0] Chapters **Single-Source Shortest Paths** / **All-Pairs Shortest Paths**, “Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein. [0] Chapters **Single-Source Shortest Paths** / **All-Pairs Shortest Paths**, “Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein.
  
pa/laboratoare/laborator-09.1651575208.txt.gz · Last modified: 2022/05/03 13:53 by darius.neatu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0