algorithm max network flow

The network flow problem is to seek the max flow of a network, there are several property of a network.

  • Vertex

+ adjacent Edge is those from the vertex out

  • Edge

+ link two vertex

+ have a direction

+ have a capacity ( c )

  • flow

+ stores how much has been used (f)

+ residual

+ stores the residual room, (= c-f)

Ford-Fulkerson algorithm

see detailed information in blog

https://www.cnblogs.com/luweiseu/archive/2012/07/14/2591573.html

and PPT of professor bdb

http://bioinfo.ict.ac.cn/~dbu/AlgorithmCourses/Lectures/Lec10.pdf

realization

class Edge(object):
    def __init__(self, u, v, c):
        self.u = u
        self.v = v
        self.capacity = c

    def __repr__(self):
        return str(self.u) + "->" + str(self.v) + ":" + str(self.capacity)


class NetworkFlow(object):
    def __init__(self):
        self.adjacent = {}
        self.flow = {}

    def addVertex(self, v):
        self.adjacent[v] = []

    def addEdge(self, u, v, w):
        if u == v:
            raise ValueError("u == v")
        e = Edge(u, v, w)
        re = Edge(v, u, 0)
        e.redge = re
        re.redge = e
        self.adjacent[u].append(e)
        self.adjacent[v].append(re)
        self.flow[e] = 0
        self.flow[re] = 0

    def getEdge(self, v):
        return self.adjacent[v]

    def findPath(self, u, v, found):
        if u == v:
            return found
        for e in self.adjacent[u]:
            residual = e.capacity - self.flow[e]
            if residual > 0 and (e, residual) not in found:
                ret = self.findPath(e.v, v, found + [(e, residual)])
                if ret != None:
                    return ret

    def maxFlow(self, u, v):
        path = self.findPath(u,v,[])
        while path != None:
            print(path)
            flow = min(res for e,res in path)
            for e,res in path:
                self.flow[e] += flow
                self.flow[e.redge] -= flow
            path = self.findPath(u,v,[])
        return sum(self.flow[e] for e in self.adjacent[u])

example

To solve this network, the result is 4.

network = NetworkFlow()
for i in range(6):
    network.addVertex(i)

network.addEdge(0, 1, 2)
network.addEdge(0, 2, 3)
network.addEdge(1, 3, 3)
network.addEdge(1, 4, 1)
network.addEdge(2, 3, 1)
network.addEdge(2, 4, 1)
network.addEdge(3, 5, 2)
network.addEdge(4, 5, 3)

The result is correct on this problem, everything seems good since now.

a bad example

The result is correct but takes many steps to solve this problem, and this is mainly because there is a bottleneck on this problem, it is explained clear on the PPT mentioned before.

There are several ways to solve this bad feature

scaling with delta

This method will take only several steps to solve the problem upward.

def findPath(self, u, v, found,  delta):
    if u == v:
        return found
    for e in self.adjacent[u]:
        residual = e.capacity - self.flow[e]
        # compare and make e.capacity > delta
        if e.capacity > delta and residual > 0 and (e, residual) not in found:
            ret = self.findPath(e.v, v, found + [(e, residual)], delta)
            if ret != None:
                return ret

def maxFlow(self, u, v):
    delta = sum(e.capacity for e in self.adjacent[u]) # set delta
    while delta >= 1:
        path = self.findPath(u,v,[], delta)
        print(delta)
        print(path)
        while path != None:
            flow = min(res for e,res in path)
            for e,res in path:
                self.flow[e] += flow
                self.flow[e.redge] -= flow
            path = self.findPath(u,v,[],delta)
        delta /= 2 # scale delta
    return sum(self.flow[e] for e in self.adjacent[u])

Edmonds-Karp algorithm

use BFS search to find the shortest path

def findShortestPath(self, u, v): # BFS
    visited = []
    level = [(u,[])]
    while level != []:
        nextlevel = []
        for x,path in level:
            visited.append(x)
            for e in self.adjacent[x]:
                residual = e.capacity - self.flow[e]
                if residual > 0 and e.v not in visited:
                    nextlevel.append((e.v,path+[(e,residual)]))
                if e.v == v:
                    return path+[(e,residual)]
        level = nextlevel

Dinic algorithm

use BFS tree to construct a layered network

reference:

http://blog.csdn.net/jwg2732/article/details/78516817

getLayeredNetwork

def getLayeredNetwork(self, u, v):
    layered = NetworkFlow()
    layered.addVertex(u)

    visited = []
    level = [u]
    count = 1
    while v not in level and level != []:
        count += 1
        visited += level
        nextlevel = []
        for x in level:
            for e in self.adjacent[x]:
                residual = e.capacity - self.flow[e]
                if residual > 0 and e.v not in visited:
                    nextlevel.append(e.v)
                    layered.addVertex(e.v)
                    layered.addE(e, residual)
        level = list(set(nextlevel))

    layered.count = count
    if v in level:
        return layered

maxFlow

def maxFlow(self, u, v):
    layered = self.getLayeredNetwork(u, v)
    while layered != None:
        level = [u]
        totalpaths = {}
        totalpaths[u] = ([([], sum(e.capacity for e in self.adjacent[u]))])
        for i in range(layered.count):
            nextlevel = []
            nextpaths = {}
            for x in totalpaths:
                for path, total in totalpaths[x]:
                    if x in level:
                        for e in layered.adjacent[x]:
                            if total == 0:
                                break
                            if e.v not in nextpaths:
                                nextpaths[e.v] = []
                            nextlevel.append(e.v)
                            if layered.residual[e] >= total:
                                nextpaths[e.v].append((path + [e], total))
                                break
                            else:
                                nextpaths[e.v].append(
                                    (path + [e], layered.residual[e]))
                                total -= layered.residual[e]
            level = list(set(nextlevel))
            if len(nextpaths) != 0:
                totalpaths = nextpaths

        for path, total in totalpaths[v]:
            for e in path:
                self.flow[e] += total
                self.flow[e.redge] -= total
        layered = self.getLayeredNetwork(u, v)
    return sum(self.flow[e] for e in self.adjacent[u])

a executing process example

----------------------------------i= 0
--------------------------x= 0
totalpath {0: [([], 96)]}
level [0]
-------------total: 96 path: []
---------e= 0->1:64 total: 96
---------e= 0->2:32 total: 32
----------------------------------i= 1
--------------------------x= 1
totalpath {1: [([0->1:64], 64)], 2: [([0->2:32], 32)]}
level [1, 2]
-------------total: 64 path: [0->1:64]
---------e= 1->3:64 total: 64
--------------------------x= 2
totalpath {1: [([0->1:64], 64)], 2: [([0->2:32], 32)]}
level [1, 2]
-------------total: 32 path: [0->2:32]
---------e= 2->3:32 total: 32

Push-Relabel algorithm

use a field forward of Edge to judge if the edge is forward or backward.

reference

http://blog.csdn.net/mr_kktian/article/details/53574134

a instance

0.initially

1.s->r1 push, r1->c1,c2,c3 =>t

2.r2->c1 so c1 has a excess=1 need to push back

3.c1->r1->s, push back the excess1 to s

4.s->r1->c1, because only the path to r1 have left capacity, and then c1 push the excess=1 to r2

5.finally

realize

def maxFlow(self, s, t):
    self.height[s] = len(self.adjacent)
    for e in self.adjacent[s]:
        self.flow[e] = e.capacity
        self.flow[e.redge] = e.capacity
        self.excess[e.v] = e.capacity
    self.excess[s] = 0

    while True:
        exc = [x for x in self.excess if self.excess[x] > 0 and x != t]
        judge = False
        flags = False
        if len(exc) == 0:
            break
        elif len(exc) == 1 and exc[0] == s:
            flags = True

        for x in exc:
            flag = False
            for e in self.adjacent[x]:
                if self.height[x] > self.height[e.v]:
                    if e.forward and e.capacity - self.flow[e] > 0:
                        bottleneck = min(self.excess[x], e.capacity - self.flow[e])
                        self.flow[e] += bottleneck
                        self.flow[e.redge] += bottleneck
                        self.excess[x] -= bottleneck
                        self.excess[e.v] += bottleneck
                        flag = True
                    elif not e.forward and self.flow[e] > 0:
                        bottleneck = min(self.excess[x], self.flow[e])
                        self.flow[e] -= bottleneck
                        self.flow[e.redge] -= bottleneck
                        self.excess[x] -= bottleneck
                        self.excess[e.v] += bottleneck
                        flag = True

            if flag == False:
                self.height[x] += 1
                judge = True
        if judge and flags:
            break
    return self.excess[t]