logo

Algoritmo Ford-Fulkerson para el problema de flujo máximo

El algoritmo Ford-Fulkerson es un algoritmo ampliamente utilizado para resolver el problema de flujo máximo en una red de flujo. El problema del flujo máximo implica determinar la cantidad máxima de flujo que se puede enviar desde un vértice fuente a un vértice sumidero en un gráfico ponderado dirigido, sujeto a restricciones de capacidad en los bordes.

cómo abrir aplicaciones ocultas en Android

El algoritmo funciona encontrando iterativamente una ruta de aumento, que es una ruta desde la fuente hasta el sumidero en el gráfico residual, es decir, el gráfico obtenido restando el flujo de corriente de la capacidad de cada borde. Luego, el algoritmo aumenta el flujo a lo largo de este camino en la cantidad máxima posible, que es la capacidad mínima de los bordes a lo largo del camino.



Problema:

Dado un gráfico que representa una red de flujo donde cada borde tiene una capacidad. Además, dados dos vértices fuente 'arena hundir 't' en el gráfico, encuentre el flujo máximo posible de s a t con las siguientes restricciones:

  • El flujo en un borde no excede la capacidad dada del borde.
  • El flujo entrante es igual al flujo saliente para todos los vértices excepto sy t.

Por ejemplo, considere el siguiente gráfico del libro CLRS.



ford_fulkerson1

El flujo máximo posible en el gráfico anterior es 23.

ford_fulkerson2



Práctica recomendada Encuentre el flujo máximo ¡Pruébelo!

Requisito previo : Introducción al problema de flujo máximo

Algoritmo Ford-Fulkerson

La siguiente es una idea simple del algoritmo Ford-Fulkerson:

  1. Comience con el flujo inicial como 0.
  2. Si bien existe una ruta aumentada desde la fuente hasta el sumidero:
    • Encuentre una ruta de aumento utilizando cualquier algoritmo de búsqueda de rutas, como la búsqueda en amplitud o la búsqueda en profundidad.
    • Determine la cantidad de flujo que se puede enviar a lo largo del camino de aumento, que es la capacidad residual mínima a lo largo de los bordes del camino.
    • Aumente el flujo a lo largo del camino de aumento en la cantidad determinada.
  3. Devuelve el caudal máximo.

Complejidad del tiempo: La complejidad temporal del algoritmo anterior es O (max_flow * E). Realizamos un bucle mientras hay un camino de aumento. En el peor de los casos, podemos agregar 1 unidad de flujo en cada iteración. Por lo tanto, la complejidad del tiempo pasa a ser O(max_flow * E).

¿Cómo implementar el algoritmo simple anterior?
Primero definamos el concepto de gráfico residual que es necesario para comprender la implementación.

Gráfico residual de una red de flujo es un gráfico que indica un posible flujo adicional. Si hay una ruta desde la fuente hasta el sumidero en el gráfico residual, entonces es posible agregar flujo. Cada borde de un gráfico residual tiene un valor llamado capacidad residual que es igual a la capacidad original del borde menos el flujo de corriente. La capacidad residual es básicamente la capacidad actual del borde.

Hablemos ahora de los detalles de implementación. La capacidad residual es 0 si no hay un borde entre dos vértices del gráfico residual. Podemos inicializar el gráfico residual como gráfico original ya que no hay flujo inicial y la capacidad residual inicialmente es igual a la capacidad original. Para encontrar una ruta de aumento, podemos hacer un BFS o DFS del gráfico residual. Hemos utilizado BFS en la siguiente implementación. Usando BFS, podemos averiguar si hay una ruta desde el origen hasta el receptor. BFS también crea una matriz principal []. Usando la matriz parent[], recorremos el camino encontrado y encontramos un posible flujo a través de este camino al encontrar la capacidad residual mínima a lo largo del camino. Luego agregamos el flujo de ruta encontrado al flujo general.

Lo importante es que necesitamos actualizar las capacidades residuales en el gráfico residual. Restamos el flujo de ruta de todos los bordes a lo largo de la ruta y agregamos flujo de ruta a lo largo de los bordes inversos. Necesitamos agregar flujo de ruta a lo largo de los bordes inversos porque es posible que luego necesitemos enviar el flujo en dirección inversa (consulte el siguiente enlace, por ejemplo).

A continuación se muestra la implementación del algoritmo Ford-Fulkerson. Para simplificar las cosas, el gráfico se representa como una matriz 2D.

C++




// C++ program for implementation of Ford Fulkerson> // algorithm> #include> #include> #include> #include> using> namespace> std;> // Number of vertices in given graph> #define V 6> /* Returns true if there is a path from source 's' to sink> >'t' in residual graph. Also fills parent[] to store the> >path */> bool> bfs(>int> rGraph[V][V],>int> s,>int> t,>int> parent[])> {> >// Create a visited array and mark all vertices as not> >// visited> >bool> visited[V];> >memset>(visited, 0,>sizeof>(visited));> >// Create a queue, enqueue source vertex and mark source> >// vertex as visited> >queue<>int>>q;> >q.push(s);> >visited[s] =>true>;> >parent[s] = -1;> >// Standard BFS Loop> >while> (!q.empty()) {> >int> u = q.front();> >q.pop();> >for> (>int> v = 0; v if (visited[v] == false && rGraph[u][v]>0) { // Si encontramos una conexión al nodo receptor, // entonces ya no tiene sentido BFS. Nosotros // solo tenemos que establecer su padre y podemos devolver // verdadero si (v == t) { padre[ v] = u; devolver verdadero; } q.push(v); padre[v] = u; visitado[v] = verdadero; } } } // No alcanzamos el receptor en BFS a partir del origen, por lo que // devuelve falso devuelve falso; } // Devuelve el flujo máximo de s a t en el gráfico dado int fordFulkerson(int graph[V][V], int s, int t) { int u, v; // Crea un gráfico residual y llena el gráfico residual // con las capacidades dadas en el gráfico original como // capacidades residuales en el gráfico residual int rGraph[V] [V]; // Gráfico residual donde rGraph[i][j] // indica la capacidad residual del borde // de i a j (si hay un borde. Si // rGraph[i][j] es 0, entonces no lo hay) for (u = 0; u for (v = 0; v rGraph[u][v] = graph[u][v]; int parent[V]; // Esta matriz se rellena con BFS y // almacena la ruta int max_flow = 0; // No hay flujo inicialmente // Aumenta el flujo mientras haya una ruta desde el origen hasta // el sumidero while (bfs(rGraph, s, t, parent)) { // Encuentra la capacidad residual mínima de los bordes a lo largo de // la ruta llena por BFS O podemos decir encontrar el // flujo máximo a través de la ruta encontrada int path_flow = INT_MAX; parent[v]; path_flow = min(path_flow, rGraph[u][v] } // actualiza las capacidades residuales de los bordes y // invierte los bordes a lo largo de la ruta para (v = t; v != s; v = parent[v]) { u = parent[v]; rGraph[u][v] -= path_flow; rGraph[v][u] += path_flow } // Agregar flujo de ruta al flujo general max_flow += path_flow } // Devuelve el flujo general return max_flow; } // Programa controlador para probar las funciones anteriores int main() { // Creemos un gráfico como se muestra en el ejemplo anterior int graph[V][V] = { { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 }, { 0, 4, 0, 0, 14, 0 }, { 0, 0, 9, 0, 0, 20 }, { 0, 0, 0, 7 , 0, 4 }, { 0, 0, 0, 0, 0, 0 } }; corte<< 'The maximum possible flow is ' << fordFulkerson(graph, 0, 5); return 0; }>

>

algoritmo de clasificación de fusión
>

Java




// Java program for implementation of Ford Fulkerson> // algorithm> import> java.io.*;> import> java.lang.*;> import> java.util.*;> import> java.util.LinkedList;> class> MaxFlow {> >static> final> int> V =>6>;>// Number of vertices in graph> >/* Returns true if there is a path from source 's' to> >sink 't' in residual graph. Also fills parent[] to> >store the path */> >boolean> bfs(>int> rGraph[][],>int> s,>int> t,>int> parent[])> >{> >// Create a visited array and mark all vertices as> >// not visited> >boolean> visited[] =>new> boolean>[V];> >for> (>int> i =>0>; i visited[i] = false; // Create a queue, enqueue source vertex and mark // source vertex as visited LinkedList queue = new LinkedList(); queue.add(s); visited[s] = true; parent[s] = -1; // Standard BFS Loop while (queue.size() != 0) { int u = queue.poll(); for (int v = 0; v if (visited[v] == false && rGraph[u][v]>0) { // Si encontramos una conexión al nodo // receptor, entonces BFS ya no tiene sentido // más. Solo tenemos que establecer su padre // y podemos devolver verdadero si (v == t) { padre[ v] = u; devolver verdadero; } cola.add(v); padre[v] = u; visitado[v] = verdadero; } } } // No alcanzamos el receptor en BFS a partir de la fuente, // así que devuelve false return false; } // Devuelve el flujo máximo de s a t en el // gráfico dado int fordFulkerson(int graph[][], int s, int t) { int u, v; // Crear un gráfico residual y llenar el gráfico residual // con las capacidades dadas en el gráfico original // como capacidades residuales en el gráfico residual // Gráfico residual donde rGraph[i][j] indica // capacidad residual del borde de i a j (si // hay una arista. Si rGraph[i][j] es 0, entonces // no la hay) int rGraph[][] = new int[V][V]; for (u = 0; u for (v = 0; v rGraph[u][v] = graph[u][v]; // Esta matriz se llena con BFS y para almacenar la ruta int parent[] = new int[ V]; int max_flow = 0; // No hay flujo inicialmente // Aumenta el flujo mientras haya una ruta desde la fuente // hasta el sumidero while (bfs(rGraph, s, t, parent)) { // Encuentra la capacidad residual mínima de los bordes // a lo largo del camino llenado por BFS. O podemos decir // encontrar el flujo máximo a través del camino encontrado. int path_flow = Integer.MAX_VALUE for (v = t; v != s; v = parent[v] ]) { u = parent[v]; path_flow = Math.min(path_flow, rGraph[u][v] } // actualiza las capacidades residuales de los bordes y // invierte los bordes a lo largo de la ruta para (v = t; v != s; v = parent[v]) { u = parent[v]; rGraph[u][v] -= path_flow; rGraph[v][u] += path_flow } // Agregar flujo de ruta al general; flow max_flow += path_flow } // Devuelve el flujo general return max_flow; } // Programa controlador para probar las funciones anteriores public static void main(String[] args) throws java.lang.Exception { // Creemos el gráfico que se muestra en el ejemplo anterior int graph[][] = new int[][] { { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 }, { 0, 4 , 0, 0, 14, 0 }, { 0, 0, 9, 0, 0, 20 }, { 0, 0, 0, 7, 0, 4 }, { 0, 0, 0, 0, 0, 0 } }; MaxFlow m = nuevo MaxFlow(); System.out.println('El flujo máximo posible es ' + m.fordFulkerson(graph, 0, 5)); } }>

>

>

Pitón




# Python program for implementation> # of Ford Fulkerson algorithm> from> collections>import> defaultdict> # This class represents a directed graph> # using adjacency matrix representation> class> Graph:> >def> __init__(>self>, graph):> >self>.graph>=> graph># residual graph> >self>. ROW>=> len>(graph)> ># self.COL = len(gr[0])> >'''Returns true if there is a path from source 's' to sink 't' in> >residual graph. Also fills parent[] to store the path '''> >def> BFS(>self>, s, t, parent):> ># Mark all the vertices as not visited> >visited>=> [>False>]>*>(>self>.ROW)> ># Create a queue for BFS> >queue>=> []> ># Mark the source node as visited and enqueue it> >queue.append(s)> >visited[s]>=> True> ># Standard BFS Loop> >while> queue:> ># Dequeue a vertex from queue and print it> >u>=> queue.pop(>0>)> ># Get all adjacent vertices of the dequeued vertex u> ># If a adjacent has not been visited, then mark it> ># visited and enqueue it> >for> ind, val>in> enumerate>(>self>.graph[u]):> >if> visited[ind]>=>=> False> and> val>>0>:> ># If we find a connection to the sink node,> ># then there is no point in BFS anymore> ># We just have to set its parent and can return true> >queue.append(ind)> >visited[ind]>=> True> >parent[ind]>=> u> >if> ind>=>=> t:> >return> True> ># We didn't reach sink in BFS starting> ># from source, so return false> >return> False> > > ># Returns the maximum flow from s to t in the given graph> >def> FordFulkerson(>self>, source, sink):> ># This array is filled by BFS and to store path> >parent>=> [>->1>]>*>(>self>.ROW)> >max_flow>=> 0> # There is no flow initially> ># Augment the flow while there is path from source to sink> >while> self>.BFS(source, sink, parent) :> ># Find minimum residual capacity of the edges along the> ># path filled by BFS. Or we can say find the maximum flow> ># through the path found.> >path_flow>=> float>(>'Inf'>)> >s>=> sink> >while>(s !>=> source):> >path_flow>=> min> (path_flow,>self>.graph[parent[s]][s])> >s>=> parent[s]> ># Add path flow to overall flow> >max_flow>+>=> path_flow> ># update residual capacities of the edges and reverse edges> ># along the path> >v>=> sink> >while>(v !>=> source):> >u>=> parent[v]> >self>.graph[u][v]>->=> path_flow> >self>.graph[v][u]>+>=> path_flow> >v>=> parent[v]> >return> max_flow> > # Create a graph given in the above diagram> graph>=> [[>0>,>16>,>13>,>0>,>0>,>0>],> >[>0>,>0>,>10>,>12>,>0>,>0>],> >[>0>,>4>,>0>,>0>,>14>,>0>],> >[>0>,>0>,>9>,>0>,>0>,>20>],> >[>0>,>0>,>0>,>7>,>0>,>4>],> >[>0>,>0>,>0>,>0>,>0>,>0>]]> g>=> Graph(graph)> source>=> 0>; sink>=> 5> > print> (>'The maximum possible flow is %d '> %> g.FordFulkerson(source, sink))> # This code is contributed by Neelam Yadav>

algoritmo knn
>

>

C#




// C# program for implementation> // of Ford Fulkerson algorithm> using> System;> using> System.Collections.Generic;> public> class> MaxFlow {> >static> readonly> int> V = 6;>// Number of vertices in> >// graph> >/* Returns true if there is a path> >from source 's' to sink 't' in residual> >graph. Also fills parent[] to store the> >path */> >bool> bfs(>int>[, ] rGraph,>int> s,>int> t,>int>[] parent)> >{> >// Create a visited array and mark> >// all vertices as not visited> >bool>[] visited =>new> bool>[V];> >for> (>int> i = 0; i visited[i] = false; // Create a queue, enqueue source vertex and mark // source vertex as visited List cola = nueva lista (); cola.Añadir(s); visitado[s] = verdadero; padre[s] = -1; // Bucle BFS estándar while (queue.Count != 0) { int u = queue[0]; cola.RemoveAt(0); for (int v = 0; v if (visited[v] == false && rGraph[u, v]> 0) { // Si encontramos una conexión al nodo // receptor, entonces no tiene sentido en BFS / / más Solo tenemos que configurar su padre // y podemos devolver verdadero si (v == t) { padre[v] = u; devolver verdadero; Agregar(v) = u; v] = true; } } } // No alcanzamos el sumidero en BFS a partir de la fuente, // así que devuelve false return false } // Devuelve el flujo máximo // de s a t en el gráfico dado int fordFulkerson; (int[, ] gráfico, int s, int t) { int u, v; // Crear un gráfico residual y llenar // el gráfico residual con // las capacidades dadas en el gráfico original como // capacidades residuales en el gráfico residual / / Gráfico residual donde rGraph[i,j] // indica la capacidad residual del // borde de i a j (si hay // un borde. Si rGraph[i,j] es 0, entonces // no lo hay) int [, ] rGraph = new int[V, V]; for (u = 0; u for (v = 0; v rGraph[u, v] = graph[u, v]; // Esta matriz se llena con BFS y para almacenar la ruta int[] parent = new int[V]; int flujo_máximo = 0; // No hay flujo inicialmente // Aumenta el flujo mientras haya un camino desde la fuente // hasta el sumidero while (bfs(rGraph, s, t, parent)) { // Encuentra la capacidad residual mínima de los bordes // a lo largo del camino llenado por BFS. O podemos decir // encontrar el flujo máximo a través del camino encontrado. int ruta_flujo = int.MaxValue; for (v = t; v != s; v = padre[v]) { u = padre[v]; ruta_flujo = Math.Min(ruta_flujo, rGraph[u, v]); } // actualiza las capacidades residuales de los bordes y // invierte los bordes a lo largo del camino for (v = t; v != s; v = parent[v]) { u = parent[v]; rGraph[u, v] -= ruta_flujo; rGraph[v, u] += ruta_flujo; } // Agregar flujo de ruta al flujo general max_flow += path_flow; } // Devuelve el flujo general return max_flow; } // Código del controlador public static void Main() { // Creemos un gráfico como se muestra en el ejemplo anterior int[, ] graph = new int[, ] { { 0, 16, 13, 0, 0, 0 }, { 0, 0, 10, 12, 0, 0 }, { 0, 4, 0, 0, 14, 0 }, { 0, 0, 9, 0, 0, 20 }, { 0, 0, 0, 7 , 0, 4 }, { 0, 0, 0, 0, 0, 0 } }; MaxFlow m = nuevo MaxFlow(); Console.WriteLine('El flujo máximo posible es ' + m.fordFulkerson(graph, 0, 5)); } } /* Este código fue aportado por PrinciRaj1992 */>

>

>

JavaScript




> // Javascript program for implementation of Ford> // Fulkerson algorithm> // Number of vertices in graph> let V = 6;> // Returns true if there is a path from source> // 's' to sink 't' in residual graph. Also> // fills parent[] to store the path> function> bfs(rGraph, s, t, parent)> {> > >// Create a visited array and mark all> >// vertices as not visited> >let visited =>new> Array(V);> >for>(let i = 0; i visited[i] = false; // Create a queue, enqueue source vertex // and mark source vertex as visited let queue = []; queue.push(s); visited[s] = true; parent[s] = -1; // Standard BFS Loop while (queue.length != 0) { let u = queue.shift(); for(let v = 0; v { if (visited[v] == false && rGraph[u][v]>0) { // Si encontramos una conexión al nodo // receptor, entonces BFS ya no tiene sentido // más. Solo tenemos que establecer su padre // y podemos devolver verdadero si (v == t) { padre[ v] = u; devolver verdadero; } cola.push(v); padre[v] = u; visitado[v] = verdadero; } } } // No alcanzamos el receptor en BFS comenzando // desde la fuente, así que devuelve false return false; } // Devuelve el flujo máximo de s a t en // la función gráfica dada fordFulkerson(graph, s, t) { let u, v; // Crea un gráfico residual y llena el // gráfico residual con las capacidades dadas // en el gráfico original como residual // capacidades en el gráfico residual // Gráfico residual donde rGraph[i][j] // indica la capacidad residual del borde / / de i a j (si hay una arista. // Si rGraph[i][j] es 0, entonces // no la hay) let rGraph = new Array(V); for(u = 0; u { rGraph[u] = new Array(V); for(v = 0; v rGraph[u][v] = graph[u][v]; } // Esta matriz se llena con BFS y para almacenar la ruta let parent = new Array(V); // Inicialmente no hay flujo let max_flow = 0 // Aumenta el flujo mientras haya // hay una ruta desde el origen hasta el receptor while (bfs(rGraph, s, t , parent)) { // Encuentra la capacidad residual mínima de los bordes // a lo largo de la ruta llena por BFS. O podemos decir // encuentra el flujo máximo a través de la ruta encontrada. let path_flow = Number.MAX_VALUE; ; v != s; v = parent[v]) { u = parent[v]; path_flow = Math.min(path_flow, rGraph[u][v]); bordes inversos a lo largo de la ruta for(v = t; v != s; v = parent[v]) { u = parent[v]; = path_flow; } // Agregar flujo de ruta al flujo general max_flow += path_flow; } // Devuelve el flujo general return max_flow; // Creemos un gráfico como se muestra en el ejemplo anterior let graph = [ [ 0 , 16, 13, 0, 0, 0 ], [ 0, 0, 10, 12, 0, 0 ], [ 0, 4, 0, 0, 14, 0 ], [ 0, 0, 9, 0, 0 , 20 ], [ 0, 0, 0, 7, 0, 4 ], [ 0, 0, 0, 0, 0, 0 ] ]; document.write('El flujo máximo posible es ' + fordFulkerson(graph, 0, 5)); // Este código es aportado por avanitrachhadiya2155>

>

>

Producción

The maximum possible flow is 23>

Complejidad del tiempo: O(|V| * E^2) , donde E es el número de aristas y V es el número de vértices.

matriz de cadenas c

Complejidad espacial: O (V), mientras creamos la cola.

La implementación anterior del algoritmo Ford Fulkerson se llama Algoritmo de Edmonds-Karp . La idea de Edmonds-Karp es utilizar BFS en la implementación de Ford Fulkerson, ya que BFS siempre elige un camino con un número mínimo de aristas. Cuando se utiliza BFS, la complejidad del tiempo en el peor de los casos se puede reducir a O(VE2). La implementación anterior utiliza una representación matricial de adyacencia, aunque BFS toma O(V2) tiempo, la complejidad temporal de la implementación anterior es O (EV3) (Referirse libro CLRS como prueba de la complejidad del tiempo)

Este es un problema importante ya que surge en muchas situaciones prácticas. Los ejemplos incluyen maximizar el transporte con límites de tráfico dados, maximizar el flujo de paquetes en redes informáticas.
Algoritmo de Dinc para Max-Flow.

Ejercicio:
Modifique la implementación anterior para que se ejecute en O(VE2) tiempo.