网络流最大流分配(附python源码)

概念介绍

流(Flow)

一种抽象的实体,在源点流出,通过边输送,在汇点被吸收,将目标从一个地点输送到另一个地点。

网络流(Network flow)

在一个每条边都有容量(Capacity)的有向图分配流,使一条边的流量不会超过它的容量。

流网络 ( G , s , t , c ) (G,s,t,c) (G,s,t,c)

(𝑮,𝒔,𝒕,𝒄)
𝑮→图(𝐺𝑟𝑜𝑢𝑝) 𝒔→源点(𝑆𝑜𝑢𝑟𝑐𝑒) t→汇点(𝑡𝑎𝑟𝑔𝑒𝑡) 𝒄→容量函数(𝑐𝑎𝑝𝑎𝑐𝑖𝑡𝑦)

流量函数 f ( u , v ) f(u,v) f(u,v)
容量函数 c ( u , v ) c(u,v) c(u,v)
剩余容量函数 r ( u , v ) = c ( u , v ) − f ( u , v ) r(u,v)=c(u,v)-f(u,v) r(u,v)=c(u,v)f(u,v)

对于流量函数 f ( u , v ) f(u,v) f(u,v),满足以下条件:

  1. 斜对称: f ( u , v ) = − f ( v , u ) f(u,v)=-f(v,u) f(u,v)=f(v,u)
  2. 容量约束: f ( u , v ) = f ( u , v ) f(u,v)=f(u,v) f(u,v)=f(u,v)
  3. 流量守恒: ∀ u ∈ V − s , t , ∑ v ∈ V f ( u , v ) = 0 ∀u \in V-{s,t}, \sum_{v \in V}{f(u,v)=0} uVs,t,vVf(u,v)=0
  4. 零自流量: f ( u , u ) = 0 f(u,u)=0 f(u,u)=0

顶点𝒗的级𝒍𝒆𝒗𝒆𝒍(𝒗):

由源点𝑠到顶点𝑣的通路中的最少的边数

分级图𝑳为(𝑽,𝑬′),其中𝑬′={(𝒖,𝒗)|𝒍𝒆𝒗𝒆𝒍(𝒗)=𝒍𝒆𝒗𝒆𝒍(𝒖)+𝟏}

最大流最小割定理:

对流网络 ( G , s , t , c ) (G,s,t,c) (G,s,t,c) f f f G G G中的流,则下面的命题等价:

  1. 存在一个容量为 c ( u , v ) = ∣ f ∣ c(u,v)=|f| c(u,v)=f的割集 S , T {S,T} S,T;
  2. f f f G G G中的最大流;
  3. 不存在 f f f的增广路径。

最大流求解

Ford_Fullkerson方法

根据最大流最小割定理,在寻找图𝐺的最大流时,可以令𝐺的初始流量𝑓=0,然后重复地在𝑓的剩余图中寻找一条增广路径,用该路径的瓶颈流量来扩张流量𝑓,直到剩余图中不存在增广路径为止。

最大容量增广(MCA,Maximum Capacity Augmentation)

根据Ford-Fullkerson方法求解最大流问题,不同点在于指定了增广路径的选取方法:搜索一条具有最大瓶颈容量的增广路径来加快算法运行时间(贪婪算法)。

步骤:

  1. 对所有 ( u , v ) ∈ E (u,v) \in E (u,v)E, r ( u , v ) = c ( u , v ) r(u,v)=c(u,v) r(u,v)=c(u,v);
  2. 对所有 ( u , v ) ∈ E (u,v) \in E (u,v)E, f ( u , v ) = 0 f(u,v)=0 f(u,v)=0;
  3. 若剩余图𝑅中存在增广路径,找出使得瓶颈流量𝛿最大的增广路径𝑝,转4;不存在转6;
  4. 对所有增广路径 p p p上的边 ( u , v ) ∈ p (u,v) \in p (u,v)p,令 r ( u , v ) = r ( u , v ) − Δ r(u,v)=r(u,v)-\Delta r(u,v)=r(u,v)Δ
  5. 对所有增广路径 p p p上的边 ( u , v ) ∈ p (u,v) \in p (u,v)p,令 f ( u , v ) = f ( u , v ) + Δ f(u,v)=f(u,v)+\Delta f(u,v)=f(u,v)+Δ,转3;
  6. 返回最大流 f f f

最短路径增广(EK算法,Edmond-Karp)

根据Ford-Fullkerson方法求解最大流问题,不同点在于指定了增广路径的选取方法:搜索一条具有最短路径(边最少)的增广路径来加快算法运行时间。

步骤:

  1. 对所有 ( u , v ) ∈ E (u,v) \in E (u,v)E, r ( u , v ) = c ( u , v ) r(u,v)=c(u,v) r(u,v)=c(u,v);
  2. 对所有 ( u , v ) ∈ E (u,v) \in E (u,v)E, f ( u , v ) = 0 f(u,v)=0 f(u,v)=0;
  3. 按照分级图原理,用BFS在剩余图中搜索由𝑠到𝑡的最短路径𝑝,转4;不存在转6;
  4. 对所有增广路径 p p p上的边 ( u , v ) ∈ p (u,v) \in p (u,v)p,令 r ( u , v ) = r ( u , v ) − Δ r(u,v)=r(u,v)-\Delta r(u,v)=r(u,v)Δ
  5. 对所有增广路径 p p p上的边 ( u , v ) ∈ p (u,v) \in p (u,v)p,令 f ( u , v ) = f ( u , v ) + Δ f(u,v)=f(u,v)+\Delta f(u,v)=f(u,v)+Δ,转3;
  6. 返回最大流 f f f

MCA和EK算法的实现

举例

c ( u , v ) c(u,v) c(u,v)

源码

# -*- coding: utf-8 -*-
# Copyright (c) 2019 - Youpeng Hu <yoooooohu@foxmail.com>
import copy

class Weighted_Gragh:
    def __init__(self, vertices):
        self.adjacent_table = {}
        for vertex in vertices:
            self.adjacent_table.update({vertex: {}})
        print("Weighted Gragh has been created Successfully!!!!!")            
        print('self.adjacent_table', end=" -> \n")
        print(self.adjacent_table)

    
    def addNeighbor(self, source, terminal, weigh):
        neighbor_list = self.adjacent_table[source]
        neighbor_list.update({terminal: weigh})
        self.adjacent_table.update({source: neighbor_list})
        print('Add a new Neighbor\nself.adjacent_table', end=" -> \n")
        print(self.adjacent_table)

    def maxCapacityAugmentation(self, source, terminal):
        print('###################################')
        print('Max Capacity Augmentation algorithm')
        self.f_table = {}
        self.r_table = copy.deepcopy(self.adjacent_table)

        path = self.findMaxBottleneckDFS(source, terminal, 0, {})        
        while path:
            print('path', end=" >>>>>>>>>>>>>>\n")
            print(path)
            bottleneck = self.foundBottleneck(path, terminal)

            self.argumentFlow(bottleneck, path, terminal)

            path = self.findMaxBottleneckDFS(source, terminal, 0, {})

        return self.f_table

    def findMaxBottleneckDFS(self, source, terminal, level, level_table, max_bottleneck = 1000):
        print('(source, terminal, max_bottleneck)', end=" ->\n")
        print((source, terminal, max_bottleneck))

        level += 1
        for end, weight in self.r_table[source].items():
            if weight > 0:
                if level not in level_table:
                    level_table[level] = {}
                if end not in level_table[level]:
                    level_table[level][end] = {}    
                level_table[level][end] = {source: weight}
                if end == terminal:
                    return level_table
                if weight >= max_bottleneck:
                    return self.findMaxBottleneckDFS(end, terminal, level, level_table, max_bottleneck = max_bottleneck)
                elif weight < max_bottleneck:
                    return self.findMaxBottleneckDFS(end, terminal, level, level_table, max_bottleneck = weight)
            elif weight < 0:
                raise ("the value of weigh have some problem")

    def edmondKarp(self, source, terminal):
        print('###################################')
        print('edmondKarp algorithm')
        self.f_table = {}
        self.r_table = copy.deepcopy(self.adjacent_table)
        path = self.findTerminalBFS([source], terminal, 0, {}, [source])        
        while path:
            print('argument table', end=" >>>>>>>>>>>>>>\n")
            print(path)
            bottleneck = self.foundBottleneck(path, terminal)

            self.argumentFlow(bottleneck, path, terminal)

            path = self.findTerminalBFS([source], terminal, 0, {}, [source])
  
        return self.f_table

    def findTerminalBFS(self, start_list, terminal, level, level_table, visited):
        print('(start_list, terminal, level, level_table, visited)', end=" ->\n")
        print((start_list, terminal, level, level_table, visited))
        level += 1
        end_list = []
        for start in start_list:
            for end, weight in self.r_table[start].items():
                if (weight > 0) & (end not in visited):
                    if level not in level_table:
                        level_table[level] = {}
                    if end not in level_table[level]:
                        level_table[level][end] = {}
                    level_table[level][end] = {start: weight}
                    if terminal == end:
                        return level_table
                    visited.append(end)
                    end_list.append(end)

                elif weight < 0:
                    raise ("the value of weight have some problem")
        if end_list:
            return self.findTerminalBFS(end_list, terminal, level, level_table, visited)

    def foundBottleneck(self, path, terminal):
        weight_list = []
        tmp_level = max(path.keys())
        print('tmp_level', end =" ->")
        print(tmp_level)

        start = terminal
        while tmp_level > 0:
            for start, weight in path[tmp_level][start].items():
                pass
            tmp_level -= 1
            weight_list.append(weight)                
        print('Bottleneck of traffic -> {}'.format(min(weight_list)))
        return min(weight_list)

    def argumentFlow(self, bottleneck, path, terminal):
        tmp_level = max(path.keys())

        end = terminal
        print('path:\n', terminal, end="")
        while tmp_level > 0:
            for start, weight in path[tmp_level][end].items():
                pass
            print("->", start, weight, end="")
            if start not in self.f_table:
                self.f_table[start] = {}           
            if end not in self.f_table[start]:
                self.f_table[start][end] = 0          
            self.f_table[start][end] += bottleneck
            self.r_table[start][end] -= bottleneck
            tmp_level -= 1
            end = start
        print()     
        print('self.r_table', end=" ->\n")
        print(self.r_table)
        print('self.f_table', end=" ->\n")
        print(self.f_table) 

if __name__ == '__main__':
    gragh = Weighted_Gragh(['s','a','b','c','d','e','f','g','h','i','j','t'])
    gragh.addNeighbor('s', 'a', 6)
    gragh.addNeighbor('s', 'c', 8)
    gragh.addNeighbor('a', 'b', 3)
    gragh.addNeighbor('a', 'd', 3)
    gragh.addNeighbor('b', 't', 10)
    gragh.addNeighbor('c', 'd', 4)
    gragh.addNeighbor('c', 'f', 4)
    gragh.addNeighbor('d', 'e', 3)
    gragh.addNeighbor('d', 'g', 6)
    gragh.addNeighbor('e', 'b', 7)
    gragh.addNeighbor('e', 'j', 4)
    gragh.addNeighbor('f', 'h', 4)
    gragh.addNeighbor('g', 'e', 7)
    gragh.addNeighbor('h', 'g', 1)
    gragh.addNeighbor('h', 'i', 3)
    gragh.addNeighbor('i', 'j', 3)
    gragh.addNeighbor('j', 't', 5)

    gragh.edmondKarp('s', 't')
    print('gragh.f_table for Edmond_Karp', end="->>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
    print(gragh.f_table)
    print('gragh.r_table for Edmond_Karp', end="->>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
    print(gragh.r_table)   

    gragh.maxCapacityAugmentation('s', 't')
    print('gragh.f_table for maxCapacityAugmentation', end="->>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
    print(gragh.f_table)
    print('gragh.r_table for maxCapacityAugmentation', end="->>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
    print(gragh.r_table)

输出参考

无论对于MCA或EK算法,输出的r_table

gragh.r_table
{'s': {'a': 0, 'c': 0}, 
 'a': {'b': 0, 'd': 0}, 
 'b': {'t': 0}, 
 'c': {'d': 0, 'f': 0}, 
 'd': {'e': 0, 'g': 2}, 
 'e': {'b': 0, 'j': 3}, 
 'f': {'h': 0}, 
 'g': {'e': 2}, 
 'h': {'g': 0, 'i': 0}, 
 'i': {'j': 0}, 
 'j': {'t': 1}, 
 't': {}
}  

最大流 f m a x ( u , v ) f_{max}(u,v) fmax(u,v)
最大流

edited date: 2019-11-15

  • 9
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值