Differences

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

Link to this comparison view

pa:laboratoare:laborator-08 [2014/08/25 22:38]
traian.rebedea [Algoritmul lui Dijkstra]
pa:laboratoare:laborator-08 [2023/03/15 16:54] (current)
radu.nichita
Line 1: Line 1:
-====== Laborator 8: Drumuri minime ======+====== Laborator 08: Drumuri minime ​în grafuri: sursă / destinație unică. (1/2) ====== 
 + 
 + 
 +{{:​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.
  
 ===== Obiective laborator ===== ===== Obiective laborator =====
  
-  *Înțelegerea conceptelor de costrelaxare a unei muchii, drum minim +Î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**.
-  ​*Prezentarea si asimilarea algoritmilor pentru calculul drumurilor minime+
  
-===== Importanţă – aplicaţii practice =====+  * Î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.
  
-Algoritmii pentru determinarea drumurilor minime au multiple aplicații practice si reprezintă clasa de algoritmi pe grafuri cel mai des utilizata:+===== Shortest-paths problemsingle-source/​destination =====
  
-  *Rutare in cadrul unei rețele (telefonice,​ de calculatoare etc.) +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.
-  *Găsirea drumului minim dintre doua locații (Google Maps, GPS etc.+
-  *Stabilirea unei agende de zbor in vederea asigurării unor conexiuni optime +
-  *Asignarea unui peer server de fișiere in funcție de metricile definite pe fiecare linie de comunicație+
  
-===== Concepte ​=====+Concepte ​necesare:
  
-==== Costul ​unei muchii ​si al unui drum ====+  * **cost muchie** / **edge cost** 
 +  * **cost drum** / **path cost** 
 +  * **problema drumurilor minime: sursă / destinație unică** / **single-source/​destination shortest-paths problem** 
 +  * **relaxarea ​unei muchii** / **edge relaxation** 
 +  * **reconstruirea ​unui drum** / **RebuildPath**
  
-Fiind dat un graf orientat G (V, E), se considera funcția w: E -> W, numita funcție de cost, care asociază fiecărei muchii o valoare numerica. Domeniul funcției poate fi extins, pentru a include si perechile de noduri intre care nu exista muchie directa, caz in care valoarea este +∞ . Costul unui drum format din muchiile p12 p23 … p(n-1)n, având costurile w12, w23, …, w(n-1)n, este suma w w12 + w23 + … + w(n-1)n.+===== Algoritmi =====
  
-In exemplul alăturatcostul drumului de la nodul 1 la 5 este:+În acest laborator vom studia **single-source/​destination shortest-paths problem**. Pentru această problemă, vom prezenta 2 algoritmi:
  
-drumul 1w14 + w45 = 30 + 20 = 50+  * **Dijkstra**presupune ca toate costurile din graf sunt nenegative. 
 +  * **Bellman-Ford**:​ permite costuri negative în graf, dar presupune că nu există cicluri de costuri negative.
  
-drumul 2: w12 + w23 + w35 = 10 + 20 + 10 = 40+Vom prezenta fiecare algoritm, îl vom analiza, iar la final vom vedea când îl vom folosi pe fiecare.
  
-drumul 3: w13 + w35 = 50 + 10 = 60 +Puteți consulta capitolul **Single-Source Shortest Paths** din **Introduction to Algorithms** [0] pentru mai multe detalii despre acești algoritmi.
-         +
-{{:​pa:​laboratoare:​8_1.png?300|}}+
  
-==== Drumul de cost minim ====+===== Dijkstra =====
  
-Costul minim al drumului dintre doua noduri este minimul dintre costurile drumurilor existente intre cele doua noduriIn exemplul de mai sus, drumul de cost minim de la nodul 1 la 5 este prin nodurile 2 si 3. +Algoritmul lui [[https://​en.wikipedia.org/​wiki/​Edsger_W._Dijkstra|Edsger Wybe **Dijkstra**]] (**Dijkstra’s algorithm**) rezolvă **shortest-paths problem** în grafuri **G = (VE)** cu costurile muchiilor **nenegative** ($w[u][v] \ge 0$).
-Deșiin cele mai multe cazuri, costul este o funcție ​cu valori ​nenegative, exista situații in care un graf cu muchii de cost negativ are relevanta practicaO parte din algoritmi pot determina drumul corect de cost minim inclusiv pe astfel de grafuri. Totuși, nu are sens căutarea drumului minim in cazurile in care graful conține cicluri de cost negativ – un drum minim ar avea lungimea infinita, intricat costul sau s-ar reduce la fiecare reparcurgere a ciclului:+
  
-In exemplul alăturat, ciclul 1 -> 2 -> 3 -> 1 are costul -20.        {{:​pa:​laboratoare:​8_2.png?​200|}}+==== Dijsktra ​Pseudocod ====
  
-drumul 1w12 + w23 + w35 10 + 20 10 40 +<code cpp> 
 +// apply Dijkstra'​s algorithm from source 
 +// 
 +// source ​   = the source for the computing distances 
 +// nodes     = list of all nodes from G 
 +// adj[node] = the adjacency list of node 
 +//             ​exampleadj[node] = {..., neigh, ...} => edge (node, neigh) 
 +// 
 +// returns: d, p 
 +//          d = distance vector 
 +//          p = parent vector 
 +// 
 +Dijsktra(source,​ G=(nodes, adj)) { 
 +  // STEP 0: initialize results 
 +  // d[node] = distance from source to node 
 +  // p[node] = parent of node on the shortest path from source to node 
 +  foreach (node in nodes) { 
 +      d[node] ​= +oo;                          // distance not yet computed 
 +      p[node] ​null;                         // parent not yet found 
 +  }
  
-drumul 2(w12 + w23 + w31) + w12 + w23 + w35 -20 + 10 + 20 + 10 = 20 +  // STEP 1initialize a priority queue 
 +  pq {};
  
-drumul 3: (w12 + w23 + w31+ (w12 + w23 + w31) + w12 + w23 + w35 -20 + (-20+ 10 + 20 + 10 = 0 +  // STEP 2add the source(sinto q 
 +  d[source] ​0;                              // distance from source to source 
 +  p[source] = null;                           // source never has parent 
 +  pq.push(source, d[source]);
  
-==== Relaxarea unei muchii ====+  // STEP 3: start relaxation loop using the node(s) from pq 
 +  while (!pq.empty()) { 
 +    // STEP 3.1: extract the next node (having the minimum estimate distance) 
 +    node pq.pop_min();​
  
-Relaxarea unei muchii v1 - v2 consta in a testa daca se poate reduce costul ei, trecând printr-un nod intermediar u. Fie w12 costul inițial al muchiei de la v1 la v2, w1u costul muchiei de la v1 la u, si wu2 costul muchiei de la u la v2. Daca w > w1u + wu2, muchia directa este înlocuita cu succesiunea de muchii v1 - u, u - v2.+    // [optional] STEP 3.2: print/use the node
  
-In exemplul alăturat, muchia de la 1 la 3, de cost w13 = 50poate fi relaxata la costul 30, prin nodul intermediar u 2, fiind înlocuita cu succesiunea w12, w23.+    // STEP 3.3: relax all edges (nodeneigh) 
 +    foreach (neigh in adj[node]) { 
 +        if (d[node] + w[node][neigh] < d[neigh]) {        // try to relax edge (nodeneigh) 
 +          d[neigh] ​d[node] + w[node][neigh]; ​           // update the new distance from source to neigh 
 +          p[neigh] = node;                                // save parent
  
-Toți algoritmii prezentați in continuare se bazează pe relaxare pentru a determina drumul minim+          pq.push( (neigh, d[neigh]) );                     // replace distance for neigh in pq 
-{{:​pa:​laboratoare:​8_3.png?​200|}} +        } 
-===== Drumuri minime de sursa unica =====+    ​
 +  }
  
-Algoritmii din aceasta secțiune determina drumul de cost minim de la un nod sursala restul nodurilor din graf, pe baza de relaxări repetate.+  return dp; 
 +}
  
-==== Algoritmul lui Dijkstra ====+// Usage example: 
 +d, p = Dijkstra(source, G=(nodes, edges)); 
 +// 1. Use distances from d 
 +// (e.g. d[node] ​distance from source to node) 
 +// 
 +// 2. Rebuild path from node to source using parents (p) 
 +RebuildPath(source,​ destination,​ p); 
 +</​code>​ 
 +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)$.
  
-Dijkstra ​poate fi folosit doar in grafuri care au toate muchiile nenegative.+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**.
  
-Algoritmul este de tip Greedy: \\ +==== Exemple ====
-optimul local căutat este reprezentat de costul drumului dintre nodul sursa s si un nod v. Pentru fiecare nod se retine un cost estimat d[v], inițializat la început cu costul muchiei s -> v, sau cu +∞, daca nu exista muchie.+
  
-In exemplul următor, sursa s este nodul 1. Inițializarea va fi: \\ \\ +=== Exemplu Dijkstra ===
- ​{{:​pa:​laboratoare:​8_4.png?​500|}} \\ \\ +
-Aceste drumuri sunt îmbunătățite la fiecare pas, pe baza celorlalte costuri estimate.+
  
-Algoritmul selectează,​ in mod repetat, nodul u care are, la momentul respectiv, costul estimat minim (fata de nodul sursa)In continuare, se încearcă sa se relaxeze restul costurilor d[v]Daca d[v] >= d[u] + wuv , d[v] ia valoarea d[u] + wuv.+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-example.png?​512| Exemplu Dijkstra}}
  
-Pentru a tine evidenta muchiilor care trebuie relaxate, se folosesc doua structuriS (mulțimea de vârfuri deja vizitate) si Q (o coada cu priorități,​ in care nodurile se afla ordonate după distanta fata de sursa) din care este mereu extras nodul aflat la distanta minima. In S se afla inițial doar sursa, iar in Q doar nodurile spre care exista muchie directa de la sursa, deci care au d[nod] < +∞.+Drumurile minime calculate de algoritmul lui Dijkstra sunt:
  
-In exemplul de mai sus, vom inițializa S = {1} si Q = {243}.+|node|1|2|3|4|5|6|7|8|9| \\ 
 +|d[node]|0|1|1|3|3|1|3|4|$+∞$| \\ 
 +|p[node]|null|1|1|3|4|1|6|5|null| \\
  
-La primul pas este selectat nodul 2, care are d[2] = 10. \\  
-Singurul nod pentru care d[nod] poate fi relaxat este 3 : d[3] = 50 > d[2] + w23 = 10 + 20 = 30 \\ \\ 
- ​{{:​pa:​laboratoare:​8_5.png?​500|}} \\ \\ 
-După primul pas, S = {1, 2} si Q = {4, 3}. 
  
-La următorul pas este selectat nodul 4, care are d[4] = 30. \\  +<spoiler Explicație pas cu pas> În exemplul atașat, avem un graf **orientat** cu următoare configurație:
-Pe baza lui, se poate modifica d[5] d[5] = +∞ > d[4] + w45 = 30 + 20 = 50 \\ \\ +
- ​{{:​pa:​laboratoare:​8_6.png?​500|}} \\ \\ +
-După al doilea pas, S = {1, 2, 4} si Q = {3, 5}.+
  
-La următorul pas este selectat ​nodul 3, care are d[3] = 30si se modifica ​din nou d[5]: +  * ''​%%n = 9%%'',​ ''​%%m = 10%%''​ 
-d[5] = 50 > d[3] + w35 30 10 40.+  * 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ă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 < +∞$). DAatunci 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( (43) )$ 
 +    * $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ă $+ 1 < 11$). DA, atunci relaxarea muchiei are loc: 
 +        * $d[8= 3 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).
  
-Algoritmul se încheie când coada Q devine vida, sau când S conține toate nodurile. Pentru a putea determina si muchiile din care este alcătuit drumul minim căutat, nu doar costul sau final, este necesar sa reținem un vector de părinți P. Pentru nodurile care au muchie directa de la sursa, P[nod] este inițializat cu sursa, pentru restul cu null.+Observații:
  
-Pseudocodul pentru determinarea drumului ​minim de la sursa către celelalte noduri utilizând algoritmul lui Dijkstra ​este:+  * 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ă ​distanță mai mare și nu ar mai fi folosită. 
 +  * 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.
  
-<code cpp> +</spoiler\\
-Dijkstra(sursa,​ dest): +
-selectat(sursa) = true +
-foreach nod in V // V = multimea nodurilor +
-    daca exista muchie[sursa,​ nod] +
-        // initializam distanta pana la nodul respectiv +
-        d[nod] = w[sursa, nod] +
-        introdu nod in Q +
-        // parintele nodului devine sursa +
-        P[nod] = sursa +
-    altfel +
-        d[nod] = +∞ // distanta infinita +
-        P[nod] = null // nu are parinte+
  
-// relaxari succesive 
-cat timp Q nu e vida 
-    u = extrage_min (Q) 
-    selectat(u) = true 
-    foreach nod in vecini[u] // (*) 
-        /* daca drumul de la sursa la nod prin u este mai mic decat cel curent */ 
-        daca not selectat(nod) si d[nod] > d[u] + w[u, nod] 
-     // actualizeaza distanta si parinte 
-            d[nod] = d[u] + w[u, nod] 
-            P[nod] = u 
-            /* actualizeaza pozitia nodului in coada prioritara */ 
-            actualizeaza (Q,nod) 
  
-// gasirea drumului efectiv +==[Studiu de cazExemplu Dijkstra: costuri negative, rezultate eronate ===
-Initializeaza Drum {} +
-nod P[dest] +
-cat timp nod !null +
-    insereaza nod la inceputul lui Drum +
-    nod P[nod] +
-</​code>​+
  
-Reprezentarea grafului ca matrice de adiacenta duce la o implementare ineficienta pentru orice graf care nu este complet, datorita parcurgerii vecinilor nodului u, din linia (*), care se va executa ​în |V| pași pentru fiecare extragere din Q, iar pe întreg ​algoritmul ​vor rezulta |V|^2 pași. Este preferata reprezentarea grafului cu liste de adiacenta, pentru care numărul total de operații cauzate de linia (*) va fi egal cu |E|. +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**.
-Complexitatea algoritmului este O(|V|^2+|E|) în cazul în care coada cu priorități este implementata ca o căutare liniara. În acest caz funcția extrage_min se executa în timp O(|V|)iar actualizează(Q) in timp O(1).+
  
-O varianta mai eficienta este implementarea cozii ca heap binar. Funcția extrage_min se va executa în timp O(lg|V|); funcția actualizează(Q) se va executa tot în timp O(lg|V|), dar trebuie cunoscuta poziția cheii nod în heap, adică heapul trebuie sa fie indexat. Complexitatea obținută este O(|E|lg|V|) pentru un graf conex.+<spoiler Exemplu rezultate eronate aplicare Dijkstra pe graf cu costuri negative>​
  
-Cea mai eficienta implementare se obține folosind un heap Fibonacci pentru coada cu priorități:​+Vom analiza 2 exemple. Pentru simplitate, presupunem ca $source = 1$.
  
-Aceasta este structura de date complexadezvoltata în mod special ​pentru ​optimizarea algoritmului ​Dijkstra, caracterizata ​de un timp amortizat de O(lg|V|) pentru operația extrage_min si numai O(1) pentru actualizeaza(Q). Complexitatea obținută este O(|V|lg|V| + |E|), foarte bună pentru grafuri rare.+În figura următoare avem topologie simplă, pentru ​care se întâmplă ca algortimul ​Dijkstra ​sa producă vectorul ​de distanțe corect.
  
-==== Algoritmul Bellman – Ford ====+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-negative-costs-example01.png?​512| Dijkstra cu costuri negative - exemplu 01}}
  
-Algoritmul Bellman Ford poate fi folosit si pentru grafuri ce conțin muchii de cost negativdar nu poate fi folosit pentru grafuri ce conțin cicluri ​de cost negativ (când căutarea unui drum minim nu are sens). \\  +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ă:
-Cu ajutorul sau putem afla daca un graf conține cicluri. +
-Algoritmul folosește același mecanism de relaxare ca si Dijkstra, dar, spre deosebire de acesta, nu optimizează o soluție folosind un criteriu ​de optim local, ci parcurge fiecare muchie de un număr de ori egal cu numărul de noduri si încearcă sa o relaxeze de fiecare datapentru a îmbunătăți distanta până la nodul destinație al muchiei curente.+
  
-Motivul pentru care se face acest lucru este ca drumul minim dintre sursa si orice nod destinație poate sa treacă prin maximum |V| noduri (adică toate nodurile grafului), respectiv |V|-1 muchii; prin urmare, relaxarea tuturor muchiilor de |V|-1 ori este suficienta pentru a propaga până la toate nodurile informația despre distanta minima de la sursa.+{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-dijkstra-negative-costs-example02.png?512| Dijkstra cu costuri negative - exemplu 02}}
  
-Daca, la sfârșitul acestor |E|*(|V|-1) relaxărimai poate fi îmbunătățită distanță, atunci graful are un ciclu de cost negativ si problema nu are soluție.+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.
  
-Menținând notațiile anterioare, pseudocodul algoritmului este:+</​spoiler>​ \\
  
-<code cpp> 
-BellmanFord(sursa):​ 
-// initializari 
-foreach nod in V // V = multimea nodurilor 
-    daca muchie[sursa,​ nod] 
-        d[nod] = w[sursa, nod] 
-        P[nod] = sursa 
-    altfel 
-        d[nod] = +∞ 
-        P[nod] = null 
-d[sursa] = 0 
-p[sursa] = null 
  
-// relaxari succesive +==== Complexitate ====
-// cum in initializare se face o relaxare (daca exista drum direct de la sursa la nod =>  +
-// d[nod] ​w[sursa, nod]) mai sunt necesare |V-2| relaxari  +
-for i 1 to |V|-2  +
-    foreach (u, v) in E  // E multimea muchiilor +
-        daca d[v] > d[u] + w(u,v) +
-            d[v] d[u] + w(u,v) +
-            p[v] u; +
-             +
-// daca se mai pot relaxa muchii +
-foreach (u, v) in E +
-    daca d[v] > d[u] + w(u,v) +
-        fail (”exista cicluri negativ”) +
-</​code>​+
  
-Complexitatea algoritmului este în mod evident ​O(|E|*|V|). +  * **complexitate temporală**:​ $T = O(m * log n)\ sau\ O(|E| * log |V|)$ 
-  +  * **complexitate spațială** : $S O(n)$
-===== Drumuri minime intre oricare doua noduri =====+
  
-==== Floyd-Warshall ====+<spoiler Detalii (analiză + optimizări)>​
  
-Algoritmii ​din aceasta secțiune determina drumul ​de cost minim dintre oricare doua noduri dintr-un grafPentru a rezolva aceasta problema s-ar putea aplica unul din algoritmii ​de mai susconsiderând ​ca sursa fiecare nod, pe rând, dar o astfel ​de abordare ar fi ineficienta.+  * **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. 
 +  * **optimizare**:​ După cum se poate vedea, complexitatea este dată de numărul de relaxări ($m$ - de care nu putem scăpadeoarece 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 - [[https://​en.wikipedia.org/​wiki/​Heap_(data_structure)#​Comparison_of_theoretic_bounds_for_variants|Heap:​ Comparison of theoretic bounds for variants]]. 
 +    * **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 [[https://​kbaile03.github.io/​projects/​fibo_dijk/​fibo_dijk.html|Fibonacci Heaps and Dijkstra’s Algorithm - A Visualization]].
  
-Algoritmul Floyd-Warshall compara toate drumurile posibile din graf dintre fiecare 2 noduri, si poate fi utilizat si in grafuri cu muchii de cost negativ.+</​spoiler>​ \\
  
-Estimarea drumului optim poate fi reținut intr-o structura tridimensionala d[v1, v2, k], cu semnificația – costul minim al drumului de la v1 la v2, folosind ca noduri intermediare doar noduri pana la nodul k. Daca nodurile sunt numerotate de la 1, atunci d[v1, v2, 0] reprezintă costul muchiei directe de la v1 la v2, considerând +∞ daca aceasta nu exista. Exemplu, pentru v1 = 1, respectiv 2: \\ \\  
- ​{{:​pa:​laboratoare:​8_7.png?​500|}} \\ \\  
-Pornind cu valori ale lui k de la 1 la |V|, ne interesează să găsim cea mai scurta cale de la fiecare v1 la fiecare v2 folosind doar noduri intermedire din mulțimea {1, ..., k}. De fiecare data, comparam costul deja estimat al drumului de la v1 la v2, deci d[v1, v2,   k-1] obținut la pasul anterior, cu costul drumurilor de la v1 la k si de la k la v2, adică d[v1, k, k-1] + d[k, v2, k-1], obținutae la pasul anterior. 
-Atunci, d[v1, v2, |V|] va conține costul drumului minim de la v1 la v2. 
  
-Pseudocodul acestui algoritm este:+===== Bellman-Ford =====
  
-<code cpp> +Algoritmul Bellman-Ford a fost inițial propus de Alfonso Shimbel ​(1955), dar publicat ​(1956-1958) șnumit ulterior după [[https://​en.wikipedia.org/​wiki/​Richard_E._Bellman|Richard E**Bellman**]] și [[https://en.wikipedia.org/wiki/L._R._Ford_Jr.|Lester Randolph **Ford** Jr. ]]. Acest algoritm rezolvă **shortest-paths problem** în grafuri **G = (VE)** 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).
-FloydWarshall(G)+
-n = |V| +
-int d[nn, n] +
-foreach ​(i, j) in (1..n,1..n) +
-    d[i, j, 0] = w[i,j] // costul muchiei, sau infinit +
-for k = 1 to n +
-    foreach (i,j) in (1..n,1..n) +
-        d[i, j, k] = min(d[ij, k-1], d[i, k, k-1+ d[k, j, k-1]) +
-</​code>​+
  
-Complexitatea temporala este O(|V|^3), iar cea spațială este tot O(|V|^3). +Algoritmul **detectează** prezența ciclurilor de costuri negative în grafDacă nu există cicluri ​de cost negativ în grafacesta furnizează vectorul ​de distanțe $dși vectorul ​de părinți $p$ (ca și Dijkstra).
-O complexitate spațială cu un ordin mai mic se obține observând ca la un pas nu este nevoie decât ​de matricea de la pasul precedent d[ij, k-1] si cea de la pasul curent ​d[i, j, k]. O observație ​și mai bună este că, de la un pas k-1 la k, estimările lungimilor nu pot decât sa scadă, deci putem sa lucram pe o singura matrice. Deci, spațiul de memorie necesar este de dimensiune |V|^2.+
  
-Rescris, pseudocodul algoritmului arata astfel:+==== Bellman-Ford - Pseudocod ====
  
 <code cpp> <code cpp>
-FloydWarshall(G): +// apply Bellman-Ford'​s algorithm from source 
-|V| +// 
-int d[n, n+// source ​   = the source for the computing distances 
-foreach (i, j) in (1..n,1..n+// nodes     = list of all nodes from G 
-    d[i, j] = w[i,j] // costul muchiei, sau infinit +// edges     = list of all edges from G 
-for = 1 to n +// 
-    foreach (i,j) in (1..n,1..n+// returns: has_cycle, d, p 
-        d[i, j] = min(d[i, j], d[i, k] + d[kj])+//          has_cycle = negative cycle detection flag (true if found) 
 +//          d = distance vector (defined only if has_cycle == false) 
 +//          p = parent vector (defined only if has_cycle == false) 
 +// 
 +Bellman-Ford(source, ​G=(nodes, edges)) { 
 +  // STEP 0initialize results 
 +  // d[node] ​distance from source to node 
 +  // p[node= parent of node on the shortest path from source to node 
 +  foreach (node in nodes) { 
 +      d[node] = +oo;                          // distance not yet computed 
 +      p(node= null;                         // parent not yet found 
 +  } 
 + 
 +  // STEP 1: set distance and parent for source node 
 +  ​d[source] = 0;                              // distance from source to source 
 +  p[source= null;                           // source never has parent 
 + 
 +  // 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 (nodeneigh
 +        d[neigh] = d[node+ w[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 
 +  foreach ((nodeneigh) in edges) { 
 +    if (d[node] + w[node][neigh] < d[neigh]) {   // try to relax edge (nodeneigh) 
 +      // negative cycle detected! 
 +      return true, null, null; 
 +    } 
 +  } 
 + 
 +  // STEP 4: no cycle detected 
 +  return false, d, p; 
 +
 + 
 +// Usage example: 
 +has_cycle, d, p = Bellman-Ford(source,​ G=(nodes, edges)); 
 +if (has_cycle) { 
 +  print "Has Cycle!"​ 
 +  STOP. 
 +} else { 
 +  // 1. Use distances from d 
 +  // (e.g. d[node= distance from source to node) 
 +  // 
 +  // 2. Rebuild path from node to source using parents (p) 
 +  RebuildPath(source,​ destination,​ p); 
 +}
 </​code>​ </​code>​
 +==== Exemple ====
 +
 +=== Exemplu Bellman-Ford ===
 +
 +{{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-bellman-example01.png?​512| Exemplu Bellman-Ford}}
 +
 +Drumurile minime calculate de algoritmul lui Bellman-Ford sunt:
 +
 +|node|1|2|3|4|5| \\
 +|d[node]|0|-2|-2|-3|-2| \\
 +|p[node]|null|4|1|3|4| \\
 +
 +
 +<spoiler Explicație pas cu pas> În exemplul atașat, avem un graf **orientat** cu următoare configurație:​
 +
 +  * ''​%%n = 5%%'',​ ''​%%m = 6%%''​
 +  * 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: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>​ \\
 +
 +
 +=== [Studiu de caz] Exemplu Bellman-Ford:​ detecție ciclu de cost negativ ===
 +
 +Dacă graful are un ciclu de cost negativ, se va putea plimba pe acel ciclu la infinit, reactulizând distanțele nodurilor implicate.
 +
 +<spoiler Exemplu detecție ciclu de cost negativ cu Bellman-Ford>​ {{https://​ocw.cs.pub.ro/​courses/​_media/​pa/​new_pa/​lab09-graph-bellman-example02.png?​512| Exemplu Bellman-Ford - ciclu negativ}}
 +
 +În exemplul atașat, avem un graf **orientat** cu următoare configurație:​
 +
 +  * ''​%%n = 5%%'',​ ''​%%m = 6%%''​
 +  * 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: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 - 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 ====
 +
 +  * **complexitate temporală**:​ $T = O(n * m)\ sau\ O(|V| * |E|)$
 +  * **complexitate spațială** : $S = O(1)$
 +
 +<spoiler Detalii (analiză + optimizări)>​
 +
 +  * **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.
 +  * **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>​ \\
 +
 +
 +===== TLDR =====
 +
 +  * 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.
 +    * **Dijkstra** are complexitate mai bună, deci este de preferat, însă acesta se poate folosi doar pe grafuri cu costuri **nenegative**.
 +    * 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 =====
 +
 +
 +<​note>​
 +
 +Scheletul de laborator se găsește pe pagina [[https://​github.com/​acs-pa/​pa-lab/​tree/​main/​skel/​lab09|pa-lab::​skel/​lab09]].
 +
 +</​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ă:
 +
 +  * știți **convențiile** folosite
 +  * evitați **buguri**
 +  * evitați **depunctări** la lab/​teme/​test
 +
 +
 +</​note>​
 +
 +==== Dijkstra ====
 +
 +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 - 1$ noduri din graf.
 +
 +
 +<note warning>
 +
 +Restricții și precizări:
 +
 +  * $ n <= 50.000 $
 +  * $ m <= 2.5 * 10^5 $
 +  * $ 0 <= c <= 20.000$, unde c este costul/​lungimea unui arc
 +  * timp de execuție
 +    * C++: ''​%%1s%%''​
 +    * Java: ''​%%2s%%''​
 +
 +
 +</​note>​
 +<​note>​
 +
 +Rezultatul se va returna sub forma unui vector **d** cu **n + 1** elemente.
 +
 +Convenție:
 +
 +  * $d[node]$ = costul minim / lungimea minimă a unui drum de la **source** la nodul **node**
 +  * $d[source] = 0$
 +  * $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>​
  
-Pentru a determina drumul efectiv, nu doar costul acestuia, avem doua variante:+==== RebuildPath ====
  
-1. Se retine o structura ​de părinți, similara cu cea de la Dijkstra, dar, bineînțeles,​ bidimensionala. \\ +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:
-2. Se folosește divide et impera astfel:+
  
-- se caută un pivot k astfel încât cost[i][j] = cost[i][k] + cost[j][k] \\ +  * $p[node]$ = părintele lui node de pe drumul minim de la sursă la node. 
-se apelează funcția recursiv pentru ambele drumuri -> (i,k),(k,j) \\ +  * $p[source] = 0$ sursa nu are părinte. 
-dacă pivotul ​nu poate fi găsit, afișăm i \\ +  * $p[node] = 0$ nodul $node$ ​nu este accesibil din $source$.
-- după terminarea funcției recursie afișăm extremitatea dreapta a drumului \\+
  
-===== Cazuri speciale =====+Se cere să se reconstituie drumul de la nodul **source** la nodul **destination**.
  
-1. Daca avem un graf **neorientat**,​ **fara cicluri** (un arbore), exista un singur 
-drum intre oricare doua noduri, care poate fi aflat printr-o simpla parcurgere DFS. 
-Folosind diferite preprocesari [8],[9], putem calcula distanta intre oricare doua noduri 
-in timp constant, O(1). 
  
-2. Daca avem un graf **orientat**,​ **fara cicluri** (un DAG [10]), raspunsul poate fi  +<note warning>
-in continuare aflat printr-un DFS, dar trebuie tinut cont de faptul ca pot exista mai multe +
-drumuri intre doua noduri.(este necesar sa salvam, pe parcurs, +
-costul minim de ajunge intr-un anumit nod). O(|V|+|E|)+
  
-3. Daca avem un graf unde toate muchiile au **cost egal**, putem afla distanta minima de la +Restricții și precizări:
-un nod sursa la orice alt nod printr-o parcurgere BFS. (de asemeni, tinand cont de faptul ca  +
-pot exista mai multe drumuri pana la un anumit nod). O(|V|+|E|)+
  
-4. Pentru grafuri orientate, ​**rare** (relativ putine muchii), putem folosi algoritmul lui Johnson([11]) pentru ​ +  ​$ n <= 50.000 $ 
-calcularea distantei minime ​de la un nod, la oricare alt nod. O(|V|^2log|V| ​|V||E|)+  * timp de execuție 
 +    * C++: ''​%%1s%%''​ 
 +    * Java: ''​%%2s%%''​
  
-===== Concluzii ===== 
  
-    ***Dijkstra*** +</​note>​ 
-– calculează drumurile minime de la o sursa către celelalte noduri \\ +<​note>​
-– nu poate fi folosit daca exista muchii de cost negativ \\ +
-– complexitate minima O(|V|lg|V| + |E|) utilizând heapuri Fibonacci;​\\+
  
-    ​***Bellman – Ford*+Rezultatul se va returna sub forma unui vector ​**path**: $path = (source, ..., destination)$.
-– calculează drumurile minime de la o sursă către celelalte noduri \\ +
-– detectează existența ciclurilor de cost negativ \\ +
-– complexitate O(|V| * |E|\\+
  
-    ***Floyd – Warshall** 
-– calculează drumurile minime intre oricare doua noduri din graf \\ 
-– poate fi folosit in grafuri cu cicluri de cost negativ, dar nu le detectează \\ 
-– complexitate O(|V|^3) \\ 
  
-===== Referinţe: =====+</​note>​
  
-[1] [[http://​en.wikipedia.org/​wiki/​Dijkstra'​s_algorithm]]+==== Bellman-Ford ====
  
-[2] [[http://en.wikipedia.org/​wiki/​Bellman-Ford_algorithm]]+Se dă un graf **orientat** cu **n** noduri și **m** arceGraful are pe arce **costuri oarecare** (pozitive sau negative).
  
-[3] [[http://​www.algorithmist.com/​index.php/​Floyd-Warshall'​s_Algorithm]]+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 grafSe va folosi aceeași convenție de reprezentare a vectorului de distanțe $d$.
  
-[4] [[http://​en.wikipedia.org/​wiki/​Binary_heap]] 
  
-[5] [[http://​en.wikipedia.org/​wiki/​Fibonacci_heap]]+<note warning>
  
-[6] T. Cormen, C. Leiserson, R. Rivest, C. Stein – Introducere în Algoritmi+Restricții și precizări:
  
-[7] CGiumale – Introducere în analiza algoritmilor+  * $ n <= 50.000 $ 
 +  * $ m <= 2.5 * 10^5 $ 
 +  * $ -1000 <= c <= +1000$, unde c este costul/​lungimea unui arc 
 +  * timp de execuție 
 +    * C++: ''​%%1s%%''​ 
 +    * Java: ''​%%2s%%''​
  
-[8] http://​en.wikipedia.org/​wiki/​Range_Minimum_Query 
  
-[9] http://​en.wikipedia.org/​wiki/​Lowest_common_ancestor+</note>
  
-[10] http://​en.wikipedia.org/​wiki/​Directed_acyclic_graph+==== BONUS ====
  
-[11] http://en.wikipedia.org/​wiki/​Johnson%27s_algorithm+La acest laborator, asistentul va alege 1-2 probleme din secțiunea extra.
  
-===== Probleme =====+==== Extra ====
  
-1) John primeste o harta a Europei, unde sunt marcate toate zborurile posibile de la o localitate la alta, respectiv costurile +  * [[https://​infoarena.ro/​problema/​distante|infoarena/​distante]] 
-acestoraAr dori sa stie care este costul minim al unui drum de la o localitate la oricare alta localitate, indiferent de numarul de escale ​**[3p]**  +  * [[https://​infoarena.ro/​problema/​catun|infoarena/​catun]] 
-<​hidden>​Roy-Floyd<​/hidden>+  ​[[https://​infoarena.ro/​problema/​lanterna|infoarena/​lanterna]] 
 +  ​* [[https://​infoarena.ro/​problema/​ciclu|infoarena/​ciclu]
 +  ​[[https://​codeforces.com/​contest/​464/​problem/​E|codeforces/​the-classic-probem]] 
 +  ​[[https://​codeforces.com/​contest/​229/​problem/​B|codeforces/​planets]] 
 +  * [[http://​poj.org/​problem?​id=1734|acm/​sightseeingtrip]]
  
-1.1) Afisati localitatile aflate pe drumul optim de la un oras la altul **[1p]**+===== Referințe =====
  
-2) Deoarece numarul de localitati de pe harta este foarte mare, descoperiti costul unei rute optime de la o singura localitate +[0Chapters ​**Single-Source Shortest Paths** **All-Pairs Shortest Paths**, “Introduction to Algorithms”,​ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein.
-(cea in care se afla John) la oricare alta localitate. **[3p]** <​hidden>​Dijkstra</​hidden>​  +
-  +
-3) John descopera ca pe anumite rute companiile ofera reduceri semnificative,​ clientii fideli primind chiar recompense financiare  +
-daca aleg anumite rute(costuri negative in graf). Determinati in aceste circumstante costul optim de la localitatea curenta la oricare alta localitate.**[3p]** <​hidden>​Bellman-Ford</​hidden> ​+
  
-3.1) Ajutati-l pe John sa descopere daca exista posibilitatea de a specula aceste recompense pentru a circula la infinit ​ 
-intre doua localitati, fara sa plateasca nimic. Afisati o astfel de ruta, daca exista. **[1p]** <​hidden>​Cicluri negative in graf</​hidden> ​ 
pa/laboratoare/laborator-08.1408995533.txt.gz · Last modified: 2014/08/25 22:38 by traian.rebedea
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