Edmonds–Karpov algoritam

S Vikipedije, slobodne enciklopedije

Edmonds–Karpov algoritam je implementacija Ford-Fulkersonovog algoritma za izračunavanje maksimalnog protoka u transportnom problemu u vremenu . Algoritam je prvi put objavio Jefim Dinic 1970. godine[1] i samostalno su objavili Džek Edmonds i Ričard Karp 1972. godine.[2] Algoritam obuhvata dodatne tehnike koje smanjuju vreme izvršavanja na .

Algoritam[uredi | uredi izvor]

Algoritam je identičan Ford-Fulkerson algoritmu osim što je definisan redosled prilikom pretraživanja. Pronađeni put mora biti najkraći put koji ima raspoložive kapacitete. To se može postići pretragom u širinu kada stavimo da grane imaju jediničnu dužinu. Vreme izvršavanja je pronađeno pokazivanjem da se svaka uvećavajuća putanja može naći u vremenu jer svaki put najmanje jedna od grana postaje zasićena (grana koja ima maksimalni mogući protok) i da je dužina najviše . Drugo svojstvo ovog algoritma je da se dužina najkraće uvećavajuće putanje uvećava monotono. Dokaz se može pronaći u knjizi Introduction to Algorithms.[3]

Implementacija[uredi | uredi izvor]

Pseudokod[uredi | uredi izvor]

algoritam EdmondsKarp
    ulaz:
        C[1..n, 1..n] (matrica kapaciteta)
        E[1..n, 1..?] (lista suseda)
        s             (izvor)
        t             (ponor ili cilj)
    izlaz:
        f             (vrednost maksimalnog protoka)
        F             (matrica koja daje dozvoljen protok sa maksimalnom vrednoscu)
    f := 0 (inicijalni protok je nula)
    F := niz(1..n, 1..n) (rezidualni kapacitet od u do v je C[u,v] - F[u,v])
    forever
        m, P := BreadthFirstSearch(C, E, s, t, F)
        if m = 0
            pauza
        f := f + m
        (Backtrack pretraga, zapis protoka)
        v := t
        while v ≠ s
            u := P[v]
            F[u,v] := F[u,v] + m
            F[v,u] := F[v,u] - m
            v := u
    return (f, F)
algoritam PretragaUSirinu
    ulaz:
        C, E, s, t, F
    izlaz:
        M[t]          (kapacitet pronadjenog puta)
        P             (roditeljska tabela)
    P := niz(1..n)
    for u in 1..n
        P[u] := -1
    P[s] := -2 (brinemo se da izvor nije ponovo pronadjen)
    M := niz(1..n) (kapacitet pronadjenog puta do cvora)
    M[s] := 8
    Q := queue()
    Q.offer(s)
    while Q.size() > 0
        u := Q.poll()
        for v in E[u]
            (Ukoliko postoji dostupan kapacitet i v nije vidjeno ranije u pretrazi)
            if C[u,v] - F[u,v] > 0 and P[v] = -1
                P[v] := u
                M[v] := min(M[u], C[u,v] - F[u,v])
                if v ≠ t
                    Q.offer(v)
                else
                    return M[t], P
    return 0, P

Implementacija u C++[uredi | uredi izvor]

#include<cstdio>
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<iostream>
 
#define MAX_NODES 100 // maksimalni broj cvorova u grafu
#define INF 2147483646 // reprezentuje beskonacnost
#define UNINITIALIZED -1 // vrednost cvora koji nema roditelja
 
using namespace std;
 
// reprezentuje kapacitet grane
int capacities[MAX_NODES][MAX_NODES];
// pokazuje koliko je protoka proteklo kroz granu
int flowPassed[MAX_NODES][MAX_NODES];
// reprezentuje graf. Graf mora da sadrzi i negativne grane!
vector<int> graph[MAX_NODES];
// prikazuje roditelje cvorova puta koji je izgradio BFS 
int parentsList[MAX_NODES]; 
// prikazuje maksimalni protok do cvora u putu koji je izgradio BFS
int currentPathCapacity[MAX_NODES]; 
 
int bfs(int startNode, int endNode)
{
memset(parentsList, UNINITIALIZED, sizeof(parentsList));
memset(currentPathCapacity, 0, sizeof(currentPathCapacity));
 
queue<int> q;
q.push(startNode);
  
parentsList[startNode]=-2;
currentPathCapacity[startNode]=INF;
 
while(!q.empty())
{
   int currentNode = q.front(); q.pop();
 
   for(int i=0; i<graph[currentNode].size(); i++)
   {
    int to = graph[currentNode][i];
    if(parentsList[to] == UNINITIALIZED)
    {
     if(capacities[currentNode][to] - flowPassed[currentNode][to] > 0)
     {
      // menjamo roditelje buduceg cvora da budu trenutni cvor
      parentsList[to] = currentNode;
 
      // proveravamo minimalni rezudualni kapacitet grane do sada
      currentPathCapacity[to] = min(currentPathCapacity[currentNode],
      capacities[currentNode][to] - flowPassed[currentNode][to]);
     
      // ukoliko smo dostigli poslednji cvor bfs ze zaustavlja
      if(to == endNode) return currentPathCapacity[endNode];
 
      // dodajemo nas buduci cvor u red
      q.push(to);
     }
    }
   }
}
 
return 0;
}
 
int edmondsKarp(int startNode, int endNode)
{
int maxFlow=0;
  
while(true)
{
   // pronalazimo uvecavajucu putanju i maksimalni protok koji mu odgovara
   int flow=bfs(startNode, endNode);
   
   // ukoliko ne mozemo da pronadjemo vise puteva protok ce biti 0
   if(flow==0)
   {
    break;
   }
 
   maxFlow +=flow;
   int currentNode=endNode;
   
   // azuriramo matricu protoka
   while(currentNode != startNode)
   {
    int previousNode = parentsList[currentNode];
    flowPassed[previousNode][currentNode] += flow;
    flowPassed[currentNode][previousNode] -= flow;
 
    currentNode=previousNode;
   }
}
 
return maxFlow;
}
 
int main()
{
int nodesCount, edgesCount;
cin>>nodesCount>>edgesCount;
 
int source, sink;
cin>>source>>sink;
 
for(int edge=0; edge<edgesCount; edge++)
{
   int from, to, capacity;
   cin>>from>>to>>capacity;
 
   capacities[from][to]=capacity;
   graph[from].push_back(to);
 
   //dodavanje negativne grane
   graph[to].push_back(from);
}
 
int maxFlow = edmondsKarp(source, sink);
 
cout<<maxFlow<<endl;
 
return 0;
}

Implementacija u Javi[uredi | uredi izvor]

import java.util.*;

/**
 * Pronalazi maksimalni protok u mreži protoka
 * @param E lista suseda
 * @param C matrica kapaciteta (mora biti n x n)
 * @param s izvor
 * @param t poniranje
 * @return maksimalni protok
 */
public class EdmondsKarp {
    public static int edmondsKarp(int[][] E, int[][] C, int s, int t) {
        int n = C.length;
        // Rezudualna kapacitivnost od u do v je C[u][v] - F[u][v]
        int[][] F = new int[n][n];
        while (true) {
            int[] P = new int[n]; // roditeljska tabela
            Arrays.fill(P, -1);
            P[s] = s;
            int[] M = new int[n]; // kapacitet putanje do cvora
            M[s] = Integer.MAX_VALUE;
            // BFS red
            Queue<Integer> Q = new LinkedList<Integer>();
            Q.offer(s);
            LOOP:
            while (!Q.isEmpty()) {
                int u = Q.poll();
                for (int v : E[u]) {
                    // ukoliko postoji dostupan kapacitet,
                    // i v nije vidjeno ranije u pretrazi
                    if (C[u][v] - F[u][v] > 0 && P[v] == -1) {
                        P[v] = u;
                        M[v] = Math.min(M[u], C[u][v] - F[u][v]);
                        if (v != t)
                            Q.offer(v);
                        else {
                            // Backtrack pretraga, i zapis protoka
                            while (P[v] != v) {
                                u = P[v];
                                F[u][v] += M[t];
                                F[v][u] -= M[t];
                                v = u;
                            }
                            break LOOP;
                        }
                    }
                }
            }
            if (P[t] == -1) { //ukoliko nismo pronasli put do t
                int sum = 0;
                for (int x : F[s])
                    sum += x;
                return sum;
            }
        }
    }
}

Primer[uredi | uredi izvor]

Dat je graf sa sedam čvorova, sa izvorom A, ciljem G i kapacitetima kao što su prikazani:

U parovima zapisanim na granama, je trenutan protok, a je kapacitet.

Preostali kapacitet od do je . Ako je protočni graf od do negativan, on doprinosi preostalom kapacitetu.

Kapacitet Put
Rezultujuća mreža

Primetimo da dužina uvećavajuće putanje pronađena ovim algoritmom nikada ne opada. Pronađeni put je najkraći mogući. Pronađeni protok je jednak kapacitetu kroz graf čiji su izvor i cilj isečeni. Postoji samo jedno minimalno sečenje u ovom grafu, particionisanjem čvorova u skupove i , sa kapacitetom:

Reference[uredi | uredi izvor]

  1. ^ Dinic, E. A. (1970). „Algorithm for solution of a problem of maximum flow in a network with power estimation”. Soviet Math. Doklady. Doklady. 11: 1277—1280. 
  2. ^ Edmonds, Jack; Karp, Richard M. (1972). „Theoretical improvements in algorithmic efficiency for network flow problems”. Journal of the ACM. Association for Computing Machinery. 19 (2): 248—264. doi:10.1145/321694.321699. 
  3. ^ Cormen, Thomas H. (2009). „26.2”. Introduction to Algorithms (third izd.). MIT Press. str. 727–730. ISBN 978-0-262-03384-8. 

Literatura[uredi | uredi izvor]