網絡流算法Push-relabel的Python實現

網絡流的背景我就不多說了,就是在一個有向圖中找出最大的流量,有意思的是,該問題的對偶問題爲最小割,找到一種切分,使得圖的兩邊的流通量最小,而且通常對偶問題是原問題的一個下界,但最小割正好等於最大流,即切割的邊就是最大流中各個path飽和邊的一個組合。說得可能比較含糊,這裏想要了解清楚還是查閱相關資料吧。

最大流最原始最經典的解法就是FF算法,算法複雜度爲O(mC),C爲邊的容量的總和,m爲邊數。而今天講的Push-relabel算法是90年代提出的高效算法,複雜度爲O(n^3),其實網絡流最關鍵的步驟就是添加反向邊,得出剩餘圖。而其他的改進就是爲了在尋找增廣路徑時儘可能貪心,流量儘可能大。

好了,開始講Push-relabel的主要思想,首先構造一個函數excess,代表每個節點保存的流量,就是等於該節點的入流量-出流量,正常來說,s的保存流量爲負,t的保存流量爲正,其他節點的保存流量均爲0,而算法的最終目標就是這個,此外還定義一個height函數(h),表示每個節點的高度。然後,初始化過程是,h(s)=n,h(v)=0,對於所有不爲s的節點,f(s, u)=c(s, u),對於所有從s出發的邊都默認飽和,這是上界。接着,就是Push-relabel的過程了,首先遍歷圖中所有節點,如果存在非t的且excess大於0的節點v,則查看v出發的所有邊(v, w),如果h(v)>h(w),則可以將label,即excess的流量,傳遞給w,如果該邊爲正向邊,傳的大小爲bottleneck=min{excess(v), c(v,w) - f(v, w)},否則bottleneck=min{excess(v), f(v, w)},傳完之後,繼續尋找excess大於0的節點,注意,如果v有邊,但所有邊都是h(v)<h(w),則將v的高度提升1,繼續尋找。

源代碼如下:

注意圖的輸入格式需滿足DIMACS格式。

__author__ = 'xanxus'
nodeNum, edgeNum = 0, 0
arcs = []


class Arc(object):
    def __init__(self):
        self.src = -1
        self.dst = -1
        self.cap = -1


s, t = -1, -1
with open('sample.dimacs') as f:
    for line in f.readlines():
        line = line.strip()
        if line.startswith('p'):
            tokens = line.split(' ')
            nodeNum = int(tokens[2])
            edgeNum = tokens[3]
        if line.startswith('n'):
            tokens = line.split(' ')
            if tokens[2] == 's':
                s = int(tokens[1])
            if tokens[2] == 't':
                t = int(tokens[1])
        if line.startswith('a'):
            tokens = line.split(' ')
            arc = Arc()
            arc.src = int(tokens[1])
            arc.dst = int(tokens[2])
            arc.cap = int(tokens[3])
            arcs.append(arc)

nodes = [-1] * nodeNum
for i in range(s, t + 1):
    nodes[i - s] = i
adjacent_matrix = [[0 for i in range(nodeNum)] for j in range(nodeNum)]
forward_matrix = [[0 for i in range(nodeNum)] for j in range(nodeNum)]
for arc in arcs:
    adjacent_matrix[arc.src - s][arc.dst - s] = arc.cap
    forward_matrix[arc.src - s][arc.dst - s] = arc.cap
flow_matrix = [[0 for i in range(nodeNum)] for j in range(nodeNum)]

height = [0] * nodeNum
height[0] = nodeNum
for i in range(len(adjacent_matrix)):
    flow_matrix[0][i] = adjacent_matrix[0][i]
    adjacent_matrix[0][i] = 0
    adjacent_matrix[i][0] = flow_matrix[0][i]


def excess(v):
    in_flow, out_flow = 0, 0
    for i in range(len(flow_matrix)):
        in_flow += flow_matrix[i][v]
        out_flow += flow_matrix[v][i]
    return in_flow - out_flow


def exist_excess():
    for v in range(len(flow_matrix)):
        if excess(v) > 0 and v != t - s:
            return v
    return None


v = exist_excess()
while v:
    has_lower_height = False
    for j in range(len(adjacent_matrix)):
        if adjacent_matrix[v][j] != 0 and height[v] > height[j]:
            has_lower_height = True
            if forward_matrix[v][j] != 0:
                bottleneck = min([excess(v), adjacent_matrix[v][j]])
                flow_matrix[v][j] += bottleneck
                adjacent_matrix[v][j] -= bottleneck
                adjacent_matrix[j][v] += bottleneck
            else:
                bottleneck = min([excess(v), flow_matrix[j][v]])
                flow_matrix[j][v] -= bottleneck
                adjacent_matrix[v][j] -= bottleneck
                adjacent_matrix[j][v] += bottleneck
    if not has_lower_height:
        height[v] += 1
    v = exist_excess()
for arc in arcs:
    print 'f %d %d %d' % (arc.src, arc.dst, flow_matrix[arc.src - s][arc.dst - s])

希望對大家有所幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章