Algoritmul Floyd Warshall

Algoritmul Floyd Warshall

The Algoritmul Floyd-Warshall , numit după creatorii săi Robert Floyd și Stephen Warshall , este un algoritm fundamental în informatică și teoria grafurilor. Este folosit pentru a găsi cele mai scurte căi între toate perechile de noduri dintr-un grafic ponderat. Acest algoritm este foarte eficient și poate gestiona grafice cu ambele pozitiv si n greutăți de margine negative , ceea ce îl face un instrument versatil pentru rezolvarea unei game largi de probleme de rețea și conectivitate.

Cuprins

Floyd-Warshall-Algoritm-banner

Algoritmul Floyd Warshall:

The Algoritmul Floyd Warshall este un algoritm de calea cea mai scurtă, spre deosebire de toate perechile Dijkstra și Bellman Ford care sunt algoritmi cu calea cea mai scurtă cu o singură sursă. Acest algoritm funcționează atât pentru regizat și ponderat nedirecţionat grafice. Dar, nu funcționează pentru graficele cu cicluri negative (unde suma marginilor dintr-un ciclu este negativă). Urmează Programare dinamică abordare pentru a verifica fiecare cale posibilă care trece prin fiecare nod posibil pentru a calcula distanța cea mai scurtă dintre fiecare pereche de noduri.

Ideea din spatele algoritmului Floyd Warshall:

Să presupunem că avem un grafic G[][] cu ÎN vârfuri din 1 la N . Acum trebuie să evaluăm a shortestPathMatrix[][] unde s hortestPathMatrix[i][j] reprezintă calea cea mai scurtă între vârfuri i și j .

Evident, cea mai scurtă cale între i la j va avea unele k numărul de noduri intermediare. Ideea din spatele algoritmului floyd warshall este de a trata fiecare vârf din 1 la N ca un nod intermediar unul câte unul.

Următoarea figură arată proprietatea optimă a substructurii de mai sus în algoritmul floyd warshall:

Algoritmul Floyd Warshall Algoritmul:

  • Inițializați matricea soluției la fel ca matricea graficului de intrare ca prim pas.
  • Apoi actualizați matricea soluției considerând toate vârfurile ca un vârf intermediar.
  • Ideea este de a alege toate nodurile unul câte unul și de a actualiza toate căile cele mai scurte care includ vârful ales ca vârf intermediar în calea cea mai scurtă.
  • Când alegem numărul de vârf k ca vârf intermediar, am luat deja în considerare vârfurile {0, 1, 2, .. k-1} ca vârfuri intermediare.
  • Pentru fiecare pereche (i, j) dintre vârfurile sursă și respectiv destinație, există două cazuri posibile.
    • k nu este un vârf intermediar pe calea cea mai scurtă de la i la j . Păstrăm valoarea de dist[i][j] așa cum este.
    • k este un vârf intermediar pe calea cea mai scurtă de la i la j . Actualizăm valoarea lui dist[i][j] la fel de dist[i][k] + dist[k][j], dacă dist[i][j]> dist[i][k] + dist[k][j]

Algoritmul Pseudo-Cod al lui Floyd Warshall: >

Pentru k = 0 până la n – 1
Pentru i = 0 până la n – 1
Pentru j = 0 până la n – 1
Distanța[i, j] = min(Distanța[i, j], Distanța[i, k] + Distanța[k, j])

unde i = Nodul sursă, j = Nodul Destinație, k = Nodul Intermediar

Ilustrație a algoritmului Floyd Warshall:

Să presupunem că avem un grafic așa cum se arată în imagine:

dryRun1drawio

Pasul 1 : Inițializați matricea Distanță[][] folosind graficul de intrare astfel încât Distanța[i][j]= greutatea muchiei de la i la j , de asemenea Distanța[i][j] = Infinit dacă nu există margine de la i la j.

pas1 desen

Pasul 2 : Trata nodul A ca nod intermediar și calculați Distanța[][] pentru fiecare pereche de noduri {i,j} folosind formula:

= Distanța[i][j] = minimă (Distanța[i][j], (Distanța de la i la A ) + (Distanța de la A la j))
= Distanța[i][j] = minimă (Distanța[i][j], Distanța[i][ A ] + Distanța[ A ][j])

step2drawio

Pasul 3 : Trata nodul B ca nod intermediar și calculați Distanța[][] pentru fiecare pereche de noduri {i,j} folosind formula:

= Distanța[i][j] = minimă (Distanța[i][j], (Distanța de la i la B ) + (Distanța de la B la j))
= Distanța[i][j] = minimă (Distanța[i][j], Distanța[i][ B ] + Distanța[ B ][j])

step3drawio

Pasul 4 : Trata nodul C ca nod intermediar și calculați Distanța[][] pentru fiecare pereche de noduri {i,j} folosind formula:

= Distanța[i][j] = minimă (Distanța[i][j], (Distanța de la i la C ) + (Distanța de la C la j))
= Distanța[i][j] = minimă (Distanța[i][j], Distanța[i][ C ] + Distanța[ C ][j])

pas4dravio

Pasul 5 : Trata nodul D ca nod intermediar și calculați Distanța[][] pentru fiecare pereche de noduri {i,j} folosind formula:

= Distanța[i][j] = minimă (Distanța[i][j], (Distanța de la i la D ) + (Distanța de la D la j))
= Distanța[i][j] = minimă (Distanța[i][j], Distanța[i][ D ] + Distanța[ D ][j])

pasul5drawio

Pasul 6 : Trata nodul ȘI ca nod intermediar și calculați Distanța[][] pentru fiecare pereche de noduri {i,j} folosind formula:

= Distanța[i][j] = minimă (Distanța[i][j], (Distanța de la i la ȘI ) + (Distanța de la ȘI la j))
= Distanța[i][j] = minimă (Distanța[i][j], Distanța[i][ ȘI ] + Distanța[ ȘI ][j])

pas6drawio

Pasul 7 : Deoarece toate nodurile au fost tratate ca un nod intermediar, acum putem returna matricea Distanță[][] actualizată ca matrice de răspuns.

pasul7drawio
Practică recomandată Încercați!

Mai jos este implementarea abordării de mai sus:

C++
// C++ Program for Floyd Warshall Algorithm #include  using namespace std; // Number of vertices in the graph #define V 4 /* Define Infinite as a large enough value.This value will be used for vertices not connected to each other */ #define INF 99999 // A function to print the solution matrix void printSolution(int dist[][V]); // Solves the all-pairs shortest path // problem using Floyd Warshall algorithm void floydWarshall(int dist[][V]) {  int i, j, k;  /* Add all vertices one by one to  the set of intermediate vertices.  --->Înainte de începerea unei iterații, avem cele mai scurte distanțe între toate perechile de vârfuri, astfel încât cele mai scurte distanțe să considere doar vârfurile din mulțimea {0, 1, 2, .. k-1} ca vârfuri intermediare.  ----> După terminarea unei iterații, vârful nr. k se adaugă la mulțimea vârfurilor intermediare și mulțimea devine {0, 1, 2, .. k} */ pentru (k = 0; k < V; k++) {  // Pick all vertices as source one by one  for (i = 0; i  < V; i++) {  // Pick all vertices as destination for the  // above picked source  for (j = 0; j  < V; j++) {  // If vertex k is on the shortest path from  // i to j, then update the value of  // dist[i][j]  if (dist[i][j]>(dist[i][k] + dist[k][j]) && (dist[k][j] != INF && dist[i][k] != INF)) dist[i][j] = dist[i][k] + dist[k][j];  } } } // Imprimă cea mai scurtă distanță matrice printSolution(dist); } /* O funcție utilitar pentru a tipări soluția */ void printSolution(int dist[][V]) { cout < < 'The following matrix shows the shortest '  'distances'  ' between every pair of vertices 
';  for (int i = 0; i  < V; i++) {  for (int j = 0; j  < V; j++) {  if (dist[i][j] == INF)  cout  < < 'INF'   < < ' ';  else  cout  < < dist[i][j]  < < ' ';  }  cout  < < endl;  } } // Driver's code int main() {  /* Let us create the following weighted graph  10  (0)------->(3) | /| 5 | |  | | 1 |/ |  (1)------->(2) 3 */ int graph[V][V] = { { 0, 5, INF, 10 }, { INF, 0, 3, INF }, { INF, INF, 0, 1 }, { INF, INF, INF, 0 } };  // Apelul funcției floydWarshall(graph);  întoarce 0; } // Acest cod este contribuit de Mythri J L>>> C (3) | /| 5 | |  | | 1 |/ |  (1)------->(2) 3 */ int graph[V][V] = { { 0, 5, INF, 10 }, { INF, 0, 3, INF }, { INF, INF, 0, 1}, { INF, INF, INF, 0}};  // Apelul funcției floydWarshall(graph);  întoarce 0; }>>> Java (3) | /| 5 | |  | | 1 |/ |  (1)------->(2) 3 */ int graph[][] = { { 0, 5, INF, 10 }, { INF, 0, 3, INF }, { INF, INF, 0, 1 }, { INF, INF, INF, 0 } };  AllPairShortestPath a = nou AllPairShortestPath();  // Apel de funcție a.floydWarshall(graph);  } } // A contribuit de Aakash Hasija>>> Python3 Înainte de începerea unei iterații, avem cele mai scurte distanțe între toate perechile de vârfuri, astfel încât cele mai scurte distanțe să considere doar vârfurile din mulțimea {0, 1, 2, .. k-1} ca vârfuri intermediare.  ----> După terminarea unei iterații, vârful nr. k se adaugă la mulțimea de vârfuri intermediare și mulțimea devine {0, 1, 2, .. k} ''' pentru k în intervalul (V): # alegeți toate vârfurile ca sursă unul câte unul pentru i în range(V): # Alegeți toate vârfurile ca destinație pentru # sursa aleasă mai sus pentru j în intervalul (V): # Dacă vârful k este pe calea cea mai scurtă de la # i la j, atunci actualizați valoarea dist[i][ j] dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j] ) printSolution(dist) # O funcție utilitar pentru a tipări soluția def printSolution (dist): print('Următoarea matrice arată cele mai scurte distanțe între fiecare pereche de vârfuri') pentru i în interval (V): pentru j în interval (V): if(dist[i][j] == INF): print('%7s' % ('INF'), final=' ') else: print('%7d	' % (dist[i][j]), end=' ') if j == V-1: print() # Codul driverului if __name__ == '__main__': ''' 10 (0)------ ->(3) | /| 5 | |  | | 1 |/ |  (1)------->(2) 3 ''' grafic = [[0, 5, INF, 10], [INF, 0, 3, INF], [INF, INF, 0 , 1], [INF, INF, INF, 0] ] # Apelul funcției floydWarshall(graph) # Acest cod este contribuit de Mythri J L 
C#
// C# program for Floyd Warshall All // Pairs Shortest Path algorithm. using System; public class AllPairShortestPath {  readonly static int INF = 99999, V = 4;  void floydWarshall(int[, ] graph)  {  int[, ] dist = new int[V, V];  int i, j, k;  // Initialize the solution matrix  // same as input graph matrix  // Or we can say the initial  // values of shortest distances  // are based on shortest paths  // considering no intermediate  // vertex  for (i = 0; i  < V; i++) {  for (j = 0; j  < V; j++) {  dist[i, j] = graph[i, j];  }  }  /* Add all vertices one by one to  the set of intermediate vertices.  --->Înainte de începerea unei iterații, avem cele mai scurte distanțe între toate perechile de vârfuri, astfel încât cele mai scurte distanțe să considere doar vârfurile din mulțimea {0, 1, 2, .. k-1} ca vârfuri intermediare.  ---> După terminarea unei iterații, vârful nr. k se adaugă la mulțimea vârfurilor intermediare și mulțimea devine {0, 1, 2, .. k} */ pentru (k = 0; k < V; k++) {  // Pick all vertices as source  // one by one  for (i = 0; i  < V; i++) {  // Pick all vertices as destination  // for the above picked source  for (j = 0; j  < V; j++) {  // If vertex k is on the shortest  // path from i to j, then update  // the value of dist[i][j]  if (dist[i, k] + dist[k, j]   < dist[i, j]) {  dist[i, j]  = dist[i, k] + dist[k, j];  }  }  }  }  // Print the shortest distance matrix  printSolution(dist);  }  void printSolution(int[, ] dist)  {  Console.WriteLine(  'Following matrix shows the shortest '  + 'distances between every pair of vertices');  for (int i = 0; i  < V; ++i) {  for (int j = 0; j  < V; ++j) {  if (dist[i, j] == INF) {  Console.Write('INF ');  }  else {  Console.Write(dist[i, j] + ' ');  }  }  Console.WriteLine();  }  }  // Driver's Code  public static void Main(string[] args)  {  /* Let us create the following  weighted graph  10  (0)------->(3) | /| 5 | |  | | 1 |/ |  (1)------->(2) 3 */ int[, ] grafic = { { 0, 5, INF, 10 }, { INF, 0, 3, INF }, { INF, INF, 0 , 1 }, { INF, INF, INF, 0 } };  AllPairShortestPath a = nou AllPairShortestPath();  // Apel de funcție a.floydWarshall(graph);  } } // Acest articol este contribuit de // Abdul Mateen Mohammed 
Javascript
// A JavaScript program for Floyd Warshall All  // Pairs Shortest Path algorithm.  var INF = 99999;  class AllPairShortestPath {  constructor() {  this.V = 4;  }  floydWarshall(graph) {  var dist = Array.from(Array(this.V), () =>nou Array(this.V).fill(0));  var i, j, k;  // Inițializați matricea soluției // la fel ca și matricea graficului de intrare // Sau putem spune că // valorile inițiale ale celor mai scurte distanțe // se bazează pe cele mai scurte căi // luând în considerare niciun punct intermediar // pentru (i = 0; i < this.V; i++) {  for (j = 0; j  < this.V; j++) {  dist[i][j] = graph[i][j];  }  }  /* Add all vertices one by one to  the set of intermediate vertices.  --->Înainte de începerea unei iterații, avem cele mai scurte distanțe între toate perechile de vârfuri, astfel încât cele mai scurte distanțe să considere doar vârfurile din mulțimea {0, 1, 2, .. k-1} ca vârfuri intermediare.  ---> După terminarea unei iterații, vârful nr. k se adaugă la mulțimea vârfurilor intermediare și mulțimea devine {0, 1, 2, .. k} */ pentru (k = 0; k < this.V; k++) {  // Pick all vertices as source  // one by one  for (i = 0; i  < this.V; i++) {  // Pick all vertices as destination  // for the above picked source  for (j = 0; j  < this.V; j++) {  // If vertex k is on the shortest  // path from i to j, then update  // the value of dist[i][j]  if (dist[i][k] + dist[k][j]  < dist[i][j]) {  dist[i][j] = dist[i][k] + dist[k][j];  }  }  }  }  // Print the shortest distance matrix  this.printSolution(dist);  }  printSolution(dist) {  document.write(  'Following matrix shows the shortest ' +  'distances between every pair of vertices '  );  for (var i = 0; i  < this.V; ++i) {  for (var j = 0; j  < this.V; ++j) {  if (dist[i][j] == INF) {  document.write(' INF ');  } else {  document.write('  ' + dist[i][j] + ' ');  }  }  document.write(' ');  }  }  }  // Driver Code  /* Let us create the following  weighted graph  10  (0)------->(3) | /| 5 | |  | | 1 |/ |  (1)------->(2) 3 */ var grafic = [ [0, 5, INF, 10], [INF, 0, 3, INF], [INF, INF, 0, 1] , [INF, INF, INF, 0], ];  var a = nou AllPairShortestPath();  // Imprimă soluția a.floydWarshall(graph);    // Acest cod este contribuit de rdtaank.>>> PHP (3) | /| 5 | |   | | 1 |/ |  (1)------->(2) 3 */ $graph = array(array(0, 5, $INF, 10), array($INF, 0, 3, $INF), array($ INF, $INF, 0, 1), matrice ($INF, $INF, $INF, 0)); // Apelul funcției floydWarshall($graph, $V, $INF); // Acest cod este contribuit de Ryuga ?> 

Ieșire
The following matrix shows the shortest distances between every pair of vertices 0 5 8 9 INF 0 3 4 INF INF 0 1 INF INF INF 0 

Analiza complexității algoritmului Floyd Warshall:

  • Complexitatea timpului: O(V 3 ), unde V este numărul de vârfuri din grafic și rulăm trei bucle imbricate fiecare cu dimensiunea V
  • Spațiu auxiliar: O(V 2 ), pentru a crea o matrice 2-D pentru a stoca cea mai scurtă distanță pentru fiecare pereche de noduri.

Notă : Programul de mai sus tipărește doar cele mai scurte distanțe. Putem modifica soluția pentru a imprima cele mai scurte căi și prin stocarea informațiilor predecesorului într-o matrice 2D separată.

De ce algoritmul Floyd-Warshall este mai bun pentru graficele dense și nu pentru graficele rare?

Graficul dens : Un grafic în care numărul de muchii este semnificativ mult mai mare decât numărul de vârfuri.
Grafic rar : Un grafic în care numărul de muchii este foarte mic.

Indiferent de câte muchii există în grafic Algoritmul Floyd Warshall rulează pentru O(V 3 ) ori prin urmare este cel mai potrivit pentru Grafice dense . În cazul graficelor rare, Algoritmul lui Johnson este mai potrivit.

  • Cum se detectează ciclul negativ într-un grafic folosind algoritmul Floyd Warshall?
  • Cum este algoritmul Floyd-warshall diferit de algoritmul lui Dijkstra?
  • Cum este algoritmul Floyd-warshall diferit de algoritmul Bellman-Ford?

Aplicații în lumea reală ale algoritmului Floyd-Warshall:

  • În rețelele de calculatoare, algoritmul poate fi folosit pentru a găsi calea cea mai scurtă între toate perechile de noduri dintr-o rețea. Aceasta este denumită ca rutarea rețelei .
  • Conectivitate de zbor În industria aviației pentru a găsi cea mai scurtă cale între aeroporturi.
  • GIS ( Sisteme Informaţionale Geografice ) aplicațiile implică adesea analiza datelor spațiale, cum ar fi rețelele de drumuri, pentru a găsi cele mai scurte căi între locații.
  • algoritmul lui Kleene care este o generalizare a lui Floyd Warshall, poate fi folosită pentru a găsi expresii regulate pentru un limbaj obișnuit.