The meet in middle method for maximum flow calculation

Given the problem to compute the maximum flow between a source and a
destination in a graph, one could use the Ford Fulkerson method with
Edmonds and Karp breadth-first-scanning in O(VE^2). Imagining that we
are computing the flow between 2 cities in the world, say Bucharest
and Bruxelles, the shortest ways are added first to the flow, then a
storm of long ways arises even overseas through America or Asia, that
contribute to the maximum flow.
It's rather inefficient to compute the flow this way at a global
scale, instead, think of extending ways in the same time from the
source to the destination and from the destination to the source and
when the ways meet in middle we've found an augmenting path (an
ameliorating way), then the process continues in the current step with
all the ways that meet somewhere "in the middle". After the first full
step, when there are no more ways to link paths into gains, another
step is required on the modified graph after augmenting till no gains
are available in a step. At this point we've found the maximum flow.
So instead of going one way at a time, we do one move expanding the
source (towards the destination) and another move expanding the
destination (towards the source), at step k we have in the source/
destination nodes queue all the cities on a path of length k from
source/destination. When the 2 ways meet in middle, we've found an
amelioration and we use it. One step of the algorithm stops when there
are no more gains joining paths. When this happens, another full step
is required and when there's not even the slightest gain in one step,
the algorithm finishes and the maximum flow is found.
Think as all the cars leaving from Bruxelles to Bucharest and vice
versa chaotic in the same time via any direction and when they meet
somewhere "in the middle" (but also could be overseas), they return to
where they came from with some maximal gain equal to the minimum edge
of the path from source to dest.
The number of full steps required to find the maximum flow in a graph,
depends on the graph, an average of 2-3 full steps required it's
observed, but there are also cases when that number increases. It runs
much faster than standard Ford Fulkerson.
Determining by formula what's the average number of big steps or the
overall complexity of the algorithm is a bit strange, but it is
obviously better than standard one way at a time Ford Fulkerson -
Edmonds Karp algorithm.

Here's some C# source code for this:


using System;
using System.Collections.Generic;
using System.Text;

namespace AdvancedTree
{
        class clsMaxFlow
        {
                const int MAX_N = 1000;
                const int INF = 1000 * 1000 * 1000;
                int[,] C = new int[MAX_N, MAX_N];
                int[,] F = new int[MAX_N, MAX_N];
                int N, S, T;

                public clsMaxFlow()
                {
                        N = MAX_N;
                        S = 0;
                        T = MAX_N - 1;
                        Random R = new Random(10);
                        for (int I = N; --I >= 0; )
                                for (int J = N; --J >= 0; )
                                        C[I, J] = R.Next(10000);
                }

                
//-----------------------------------------------------------------------------------------------------------------------------

                public int MAX_FLOW_Ford_Fulkerson_Edmonds_Karp()
                {
                        Queue<int> Q = new Queue<int>(N);
                        int[] flow = new int[N];        //max flow till this 
point...
                        int[] back = new int[N];        //where the flow comes 
from (previous
point on path)...
                        for (int I = N; --I >= 0; )
                                for (int J = N; --J >= 0; )
                                        F[I, J] = 0;
                        int res = 0;
                        do {
                                for (int I = N; --I >= 0; flow[I] = -1, back[I] 
= -1) ;
                                flow[S] = INF;  //Flow from source to source is 
infinite
                                Q.Clear();
                                Q.Enqueue(S);   //Start from source only.
                                while (Q.Count > 0) {
                                        int I = Q.Dequeue();
                                        for (int J = N; --J >= 0; ) {
                                                int fl = Math.Min(flow[I], C[I, 
J] - F[I, J]);
                                                //we can reach point J with a 
flow of fl
                                                if (fl > 0) {
                                                        if (flow[J] < 0)        
//if J is unseen
                                                                Q.Enqueue(J);   
//we add it to queue.
                                                        if (fl > flow[J]) {
                                                                //if we got a 
bigger flow to J than previously,
                                                                //we update the 
max flow to point J.
                                                                flow[J] = fl;
                                                                back[J] = I;    
//I was processed before J so no cycle can
occur...
                                                        }
                                                }
                                        }
                                }
                                int Cf = flow[T];
                                if (Cf <= 0)    //if no flow at all to 
destination then quit, we're
done.
                                        break;
                                res += Cf;
                                for (int J = T, I; (I = back[J]) >= 0; J = I) {
                                        F[I, J] += Cf;
                                        F[J, I] -= Cf;
                                }       //update the residual network, aquiring 
current flow.
                        } while (true);
                        for (int I = N; --I >= 0; )
                                for (int J = N; --J >= 0; )
                                        if (F[I, J] > C[I, J])
                                                throw new Exception("Error: The 
flow is bigger than graph
capacity...");
                        return res;
                }

                
//-----------------------------------------------------------------------------------------------------------------------------

                private int flow_pass_thru(int[] back)
                {       //run-time: O(V) -> double pass...
                        int flow = INF;
                        for (int I = T; I != S; ) {
                                int K = back[I];
                                flow = Math.Min(flow, C[K, I] - F[K, I]);
                                I = K;
                        }       //step 1: get the max flow from source to dest 
in the given path
                        for (int I = T; I != S; ) {
                                int K = back[I];
                                F[K, I] += flow;
                                F[I, K] -= flow;
                                I = K;
                        }       //Aquire the flow.
                        return flow;
                }

                public int MAX_FLOW_Meet_in_Destination()
                {
                        Queue<int> Q = new Queue<int>(N);
                        int[] seen = new int[N];        // (0 = unseen), (1 = 
seen), (-1 =
destination)
                        int[] back = new int[N];        //previous point on 
path...
                        for (int I = N; --I >= 0; )
                                for (int J = N; --J >= 0; )
                                        F[I, J] = 0;
                        int res = 0;
                        do {
                                for (int I = N; --I >= 0; seen[I] = 0, back[I] 
= -1) ;
                                Q.Clear();
                                Q.Enqueue(S);   //Start from source only.
                                seen[S] = 1;    //consider source is seen
                                seen[T] = -1;
                                int flow = 0;
                                while (Q.Count > 0) {
                                        int I = Q.Dequeue();
                                        for (int J = N; --J >= 0; )
                                                if (seen[J] <= 0 && C[I, J] > 
F[I, J])
                                                        if (seen[J] == 0) {     
//marked from end
                                                                Q.Enqueue(J);
                                                                seen[J] = 1;
                                                                back[J] = I;
                                                        } else {
                                                                //We reached 
the destination (seen[J] < 0)
                                                                back[J] = I;
                                                                flow += 
flow_pass_thru(back);
                                                        }
                                }
                                if (flow <= 0)
                                        break;
                                res += flow;
                        } while (true);
                        for (int I = N; --I >= 0; )
                                for (int J = N; --J >= 0; )
                                        if (F[I, J] > C[I, J])
                                                throw new Exception("Error: The 
flow is bigger than graph
capacity...");
                        return res;
                }

                
//-----------------------------------------------------------------------------------------------------------------------------

                private int flow_pass_thru(int _I, int _J, int[] back)
                {       //run-time: O(V) -> double pass...
                        int
                                I = _I,
                                J = _J,
                        flow = C[I, J] - F[I, J];
                        while (I != S) {
                                int K = back[I];
                                flow = Math.Min(flow, C[K, I] - F[K, I]);
                                I = K;
                        }       //flow = maximum from I to source
                        while (J != T) {
                                int K = back[J];
                                flow = Math.Min(flow, C[J, K] - F[J, K]);
                                J = K;
                        }       //flow = maximum also from J to destination
                        I = _I;
                        J = _J;
                        F[I, J] += flow;
                        F[J, I] -= flow;
                        while (I != S) {
                                int K = back[I];
                                F[K, I] += flow;
                                F[I, K] -= flow;
                                I = K;
                        }       //Aquire flow one way (to source).
                        while (J != T) {
                                int K = back[J];
                                F[J, K] += flow;
                                F[K, J] -= flow;
                                J = K;
                        }       //Aquire flow the other way (to destination).
                        return flow;
                }

                public int MAX_FLOW_Meet_in_Middle()
                {
                        Queue<int> Q = new Queue<int>(N);
                        int[] seen = new int[N];        //(0 = unseen) (1 = 
seen from source) (-1
= seen from destination)
                        int[] back = new int[N];        //back point...
                        for (int I = N; --I >= 0; )
                                for (int J = N; --J >= 0; )
                                        F[I, J] = 0;
                        int
                                res = 0,
                                flow;
                        do {
                                for (int I = N; --I >= 0; seen[I] = 0, back[I] 
= -1) ;
                                Q.Clear();
                                Q.Enqueue(S);
                                Q.Enqueue(T);   //Start from source and 
destination in alternating
moves.
                                seen[S] = 1;
                                seen[T] = -1;
                                flow = 0;
                                while (Q.Count > 0) {
                                        int I = Q.Dequeue();
                                        if (seen[I] > 0) {      //if we're from 
source
                                                for (int J = N; --J >= 0; )
                                                        if (seen[J] <= 0 && 
C[I, J] > F[I, J])
                                                                if (seen[J] == 
0) {
                                                                        //if 
this node is unseen,
                                                                        //we 
add it to queue, we saw it from source
                                                                        
Q.Enqueue(J);
                                                                        seen[J] 
= 1;
                                                                        back[J] 
= I;
                                                                } else {
                                                                        
//otherwise we've found a path connecting source and
                                                                        //dest.
                                                                        flow += 
flow_pass_thru(I, J, back);
                                                                        //we 
aquire it.
                                                                }

                                        } else {        //else we're from dest
                                                int J = I;
                                                for (I = N; --I >= 0; )
                                                        if (seen[I] >= 0 && 
C[I, J] > F[I, J])
                                                                if (seen[I] == 
0) {
                                                                        //if 
this node is unseen,
                                                                        //we 
add it to queue, we saw it from dest
                                                                        
Q.Enqueue(I);
                                                                        seen[I] 
= -1;
                                                                        back[I] 
= J;
                                                                } else {
                                                                        
//otherwise we've found a path connecting dest and
                                                                        
//source.
                                                                        flow += 
flow_pass_thru(I, J, back);
                                                                        //we 
aquire it also.
                                                                }
                                        }
                                }
                                res += flow;
                        } while (flow > 0);
                        for (int I = N; --I >= 0; )
                                for (int J = N; --J >= 0; )
                                        if (F[I, J] > C[I, J])
                                                throw new Exception("Error: The 
flow is bigger than graph
capacity...");
                        return res;
                }

                
//-----------------------------------------------------------------------------------------------------------------------------

        }
}

//We can use a sorted list of edged or a hash(I, J) giving the flow
from I to J in O(1) to speed the calculations, but that's data //
structure dependent.


A small timing experiment:
                        clsMaxFlow F = new clsMaxFlow();
                        Stopwatch sw = new Stopwatch();
                        int flow;

                        string S = "";

                        sw.Reset(); sw.Start();
                        flow = F.MAX_FLOW_Ford_Fulkerson_Edmonds_Karp();
                        sw.Stop();
                        S += "MAX_FLOW_Ford_Fulkerson_Edmonds_Karp:  " + 
flow.ToString("N")
+ "   " + sw.ElapsedMilliseconds + "ms\n";

                        sw.Reset(); sw.Start();
                        flow = F.MAX_FLOW_Meet_in_Destination();
                        sw.Stop();
                        S += "MAX_FLOW_Meet_in_Destination:  " + 
flow.ToString("N") + "   "
+ sw.ElapsedMilliseconds + "ms\n";

                        sw.Reset(); sw.Start();
                        flow = F.MAX_FLOW_Meet_in_Middle();
                        sw.Stop();
                        S += "MAX_FLOW_Meet_in_Middle:  " + flow.ToString("N") 
+ "   " +
sw.ElapsedMilliseconds + "ms\n";

                        MessageBox.Show(S);



TEST RESULTS:
MAX_FLOW_Ford_Fulkerson_Edmonds_Karp:   5,038,169.00   43042ms
MAX_FLOW_Meet_in_Destination:           5,038,169.00    1527ms
MAX_FLOW_Meet_in_Middle:                5,038,169.00     104ms



Hope you find this post useful.
Badita Robert - Romania

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Algorithm Geeks" group.
To post to this group, send email to algogeeks@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/algogeeks
-~----------~----~----~----~------~----~------~--~---

Reply via email to