Table of Contents

Laborator 12: Flux maxim în rețele de transport

Bitdefender provides cybersecurity solutions with leading security efficacy, performance and ease of use to small and medium businesses, mid-market enterprises and consumers. Guided by a vision to be the world’s most trusted cybersecurity solutions provider, Bitdefender is committed to defending organizations and individuals around the globe against cyberattacks to transform and improve their digital experience.

Obiective laborator

În acest laborator vom introduce contextul pentru maximum flow problem și vom studia algoritmi care pot rezolva această problemă.

Maximum flow problem

Vă rugăm să parcugeți Maximum flow problem pentru a vă familiariza cu contextul, problema și notațiile folosite.

Concepte necesare:

Algoritmi

Pentru maximum flow problem există mai mulți algoritmi, dintre care amintim:

Puteți consulta capitolul Maximum Flow din Introduction to Algorithms [0] pentru mai multe detalii despre acești algoritmi.

În acest laborator vom studia și analiza metoda Ford-Fulkerson, apoi vom implementa algoritmul Edmonds-Karp (care este o optimizare peste Ford-Fulkerson).

Ford-Fulkerson

Algoritmul lui Lester Randolph **Ford** Jr.  și Delbert Ray **Fulkerson**, cunoscut ca și algoritmul / metoda Ford-Fulkerson este un algoritm greedy pentru calcularea fluxului maxim într-o rețea de transport.

Atât timp cât în graf încă există drumuri de ameliorare (augmenting paths), înseamnă că mai putem adauga flux in rețea prin acele drumuri. La fiecare pas alegem un drum de ameliorare, prin care adăugăm cantitatea maximă posibilă de flux.

Ford-Fulkerson - Pseudocod

Ford-Fulkerson - Pseudocod

// apply Ford-Fulkerson's algorithm for computing maximum flow in network G
//
// nodes     = list of all nodes from G
// edges     = the list of all edges in G
//             example: edge (node, neigh, capacity)
// S         = source in network G
// T         = sink in network G
//
// returns: maxFlow (maximum flow in G from S to T)
//
Ford-Fulkerson(G=(nodes, edges), S, T) {
  // STEP 0: initialize
  // * flow in network
  foreach ((u, v) in edges) {
      f[u][v] = 0;
  }
  // * maximum total flow
  totalFlow = 0;
 
  while (exists augmenting path p in G) {
      augment flow f along p;
      update totalFlow;
  }
 
  return totalFlow;
}
 
// Usage example:
maxFlow = Ford-Fulkerson(G=(nodes, edges), S, T);
// Use maxFlow.

După cum se observă, metoda Ford-Fulkerson descrie care este abordarea generală, însă nu ne spune concret cum să găsim drumurile de ameliorare și cum să creștem fluxul pe ele.

Putem să alegem drumurile la întâmplare. Vom obține un rezultat corect (nu vom demonstra).

Din punct de vedere al performanței, în cel mai rău caz, pornind o parcurgere oarecare vom găsi în $O(n + m)$ un drum de ameliorare pe care vom putea crește cu exact o unitate cantitatea de flux. Această abordare duce la o complexitate de $O((n + m) * |f_{max}|)$ - adică un timp de execuție proporțional cu cantitea de flux pompată. Abordarea este ineficientă.


În practică NU vom folosi direct metoda Ford-Fulkerson, ci vom folosi algoritmul Edmonds-Karp, pe care îl vom analiza în secțiunea următoare.

Edmonds-Karp

Algoritmul lui Jack **Edmonds** și Richard M. **Karp**, cunoscut ca și algoritmul Edmonds-Karp reprezintă o implementare concretă a metodei Ford-Fulkerson. Acest algoritm rezolvă problema drumurilor de ameliorare folosind BFS - adică drumul cel mai scurt (ca și număr de arce), pe care se poate merge ținând cont de restricția de capacitate ($f(u, v) \lt c(u, v)$).

Edmonds-Karp - pseudocod

// apply Edmonds-Karp's algorithm for computing maximum flow in network G
//
// nodes     = list of all nodes from G
// adj[node] = the adjacency list of node
//             example: adj[node] = {..., neigh, ...} => edge (node, neigh)
// S         = source in network G
// T         = sink in network G
// f         = flow in network G
// c         = capacity in network G
//
// returns: maxFlow (maximum flow in G from S to T)
//
Edmonds-Karp(G=(nodes, adj), S, T) {
  // STEP 0: initialize
  // * flow in network
  foreach (u in nodes) {
      foreach(v in nodes) {
          f[u][v] = 0;
      }
  }
  // * maximum total flow
  totalFlow = 0;
 
  // STEP 1: Start BFS for finding augmenting path(s).
  while (BFS finds at least one augmenting path in G) {
    // STEP 2.1: Compute residual capacity of augmenting path.
    rc = +oo; // residual capacity
    foreach ((u, v) in path) {
        rc = min(rc, c[ u ][ v ] - f[ u ][ v ]);
    }
 
    // STEP 2.2: Update flow on path with rc.
    foreach ((u, v) in path) {
        f[ u ][ v ] += rc;      // Increase on edge (u, v).
        f[ v ][ u ] -= rc;      // Decrease on edge (v, u).
    }
 
    // STEP 2.3: Update total flow.
    totalFlow += rc;
  }
 
  return totalFlow;
}
 
// Usage example:
maxFlow = Edmonds-Karp(G=(nodes, adj), S, T);
// Use maxFlow.

Algoritmul Edmons-Karp pornește o parcurgere BFS la fiecare pas. Pentru un drum de ameliorare găsit, calculează capacitatea reziduală a acestuia, apoi crește corespunzător fluxul pe acesta.

La fiecare pas, atunci când se crește fluxul pe un arc, se scade fluxul pe arcul invers (vom demonstra într-o subsecțiune viitoare necesitatea acestui pas pentru corectitudinea algoritmului).

Algoritmul se oprește când nu mai există drumuri de ameliorare. Fluxul pompat până atunci în rețea este maxim.

Exemple

Edmonds-Karp: exemplu rezultat

 Exemplu Edmonds-Karp

Fluxul maxim calculat cu Edmonds-Karp pentru rețeaua atașată este: 2.

Drumurile de ameliorare folosite sunt:

Exemplu Edmonds-Karp: exemplu simplu pas cu pas

În această secțiune vom exemplifica de ce doar simpla incrementare a fluxului pe arcele directe nu este suficientă pentru a găsi fluxul maxim în rețea. Următoarea secțiunea conține soluția.

Exemplu

Exemplu

Pornim de la rețeaua din figura următoare:

 Exemplu Edmonds-Karp

  • n = 6, m = 7
  • Sursa este S = 1, iar terminalul este T = 6.
  • Funcția de capacitate c are valorile din figură.

Dacă rulăm Edmonds-Karp, o posibilă succesiune de stări pentru rețea este cea din figura următoare:

 Exemplu Edmonds-Karp

  • Inițial fluxul în rețea este zero.
  • Se vor găsi 2 drumuri de ameliorare $1 - 2 - 4 - 6$ și $ 1 - 3 - 5 - 6$. Pe fiecare drum se pompează câte o unitate de flux.
  • În rețeaua reziduală obținută se observă că nu se mai poate ajunge de la S la T.
  • Prin urmare fluxul obținut este maxim și are valoare $1 + 1 = 2$.

NU avem control asupra drumurilor găsite de BFS, adică ordinea în care acestea vor fi alese, ci știm doar că se va alege cele mai scurte drumuri.

O altă succesiune posibilă și corectă de stări pentru rețea se găsește în figura următoare:

 Exemplu Edmonds-Karp

  • Inițial fluxul în rețea este zero.
  • Se găsește drumul de ameliorare $1 - 2 - 5 - 6$ (tot de lungime 3 arce) cu capacitatea reziduală 1. Se pompează această unitate de flux.
  • În rețeaua reziduală obținută se observă că nu se mai poate ajunge de la S la T.
  • Fluxul total obținut este 1, care NU este maxim.

Prin urmare ajungem într-un punct în care algoritmul a greșit și nu mai poate pompa flux în plus. Parcugeți următorul exemplu pentru a vedea concret cum se rezolvă această problemă.


Exemplu Edmonds-Karp: exemplu detaliat cu flux pe arce inverse

În această secțiune vom arăta ce face algoritmul pas cu pas și vom observa necesitatea și corectitudinea scăderii fluxului pe arcele inverse.

Pornim de la rețeaua din figura următoare:

 Exemplu Edmonds-Karp

Să vedem în continuare la ce ne ajută dacă alegem din nou, ghinionist, drumul de amelioarare $1 - 2 - 5 - 6$ și dacă de data aceasta putem corecta. Vom face acțiunile marcate în figura următoare:

 Exemplu Edmonds-Karp

Vom face acțiunile marcate în figura următoare:

 Exemplu Edmonds-Karp

Să observăm că starea finală a rețelei conține 2 unități de flux. Dacă ne uităm cu atenție, observăm că avem o unitate de flux pe drumul $1 - 2 - 4 - 6$ și alta pe $1 - 3 - 5 - 6$.

Deși Edmonds-Karp nu a ales inițial aceste 2 drumuri, s-a corectat pe parcurs, iar în final, dacă fluxul maxim trebuia să treacă pe aici, rețeaua reziduală a fost actualizată / corectată până s-a întâmplat acest lucru!

Semnificația înaintării pe un arc fictiv este că defapt cautăm să deviem fluxul de pe un drum ales anterior (de exemplu, $1 - 2 - 5 - 6$), pe o altă cale (de exemplu, porțiunea $1 - 2$ să se continue cu $2 - 4 - 6$), astfel încât să putem folosi partea eliberată (de exemplu, $5 - 6$), în construcția unui nou drum la pasul curent.

Complexitate

Detalii (analiză + optimizări)

Detalii (analiză + optimizări)

  • complexitate temporală: Demonstrația se găsește în Introduction to Algorithms. Ideea de bază este că de fiecare dată când creștem fluxul pe un drum de ameliorare, o muchie devine saturată. Dacă vreodată această muchie va mai fi folosită în alt drum de ameliorare (în cazul unei corecții), aceasta va fi folosită într-un drum mai lung. Respectivele distanțe sunt monotone și limitate de $O(|V|)$.
  • complexitate spațială : Stocăm o coadă pentru algoritmul BFS. De asemenea, în implementare vom stoca și un vector de părinți pentru a putea parcurge un drum de ameliorare de la T la S (sens invers).


TLDR

Exerciții

Scheletul de laborator se găsește pe pagina pa-lab::skel/lab12.

Înainte de a rezolva exercițiile, asigurați-vă că ați citit și înțeles toate precizările din secțiunea 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

Edmonds-Karp

Se dă un graf orientat cu n noduri și m arce. Graful are pe arce capacitati pozitive.

Folosiși Edmonds-Karp pentru a găsi fluxul maxim între nodul sursă 1 și nodul destinație n în graful dat.

Restricții și precizări:

  • $ n <= 10^3 $
  • $ m <= 5 * 10^3 $
  • $ 1 <= c <= 110.000$, unde c este capacitatea unui arc
  • Nu există arce de la n la 1.
  • timp de execuție
    • C++: 1s
    • Java: 2s

Rezultatul se va returna sub forma unui singur număr, reprezentând fluxul maxim ce poate fi trimis prin rețea.

BONUS

La acest laborator, asistentul va alege 1-2 probleme din secțiunea extra.

Extra

Referințe

[0] Chapter Maximum Flow, “Introduction to Algorithms”, Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford Stein.

[1] Maximal flow through a network, L.R. Ford, D.R. Fulkerson

[2] Theoretical improvements in algorithmic efficiency for network flow problems, Edmonds, Jack; Karp, Richard M.

[3] Dinitz Algorithm, Yefim Dinitz