许多系统中包含了流量问题,例如,公路系统中有车辆流,控制系统中有信息流,供水系统中有水流,金融系统中有现金流等。
在生物安全领域和医疗系统中,也包括就诊、挂号、检查、治疗、转院等过程涉及的患者流,医疗设备、药品、医用材料等的采购、配送、使用等过程涉及的医疗资源流,以及医疗质量控制流和医保支付流等等。
对于这样一些包含了流量问题的系统,我们往往要求出其系统的最大流量。给一个带收发点的网络,每条弧的赋权称为容量,在不超过每条弧的容量的前提下,确定每条弧的流量,求出从发点到收点的最大流量,这类问题通常称为最大流问题。
满足上述条件的网络流称为可行流,总存在最大可行流。
当支路上 f i j = c i j f_{ij}=c_{ij} fij=cij ,称为饱和弧;v( f )为可行流的流量。
最大流问题也是一个线性规划问题。
设 f 是一个可行流, μ \mu μ 是 v s v_s vs 从 v t v_t vt 到的一条链,若 μ \mu μ 满足下列条件,则 μ \mu μ 是可行流的一条增广链:
给定一个图,该图表示每个边都有一个容量的流网络。此外,给定图中的两个顶点源 ‘s’ 和接收器 ‘t’,使用以下约束找到从 s 到 t 的最大可能流量:
该问题的最大流量为23:
福特-富克森定理:网络的最大流等于最小截量集容量。
从原始图中“减去”最大流路径,得到下图:
标记从 s 到达的所有节点——调用可到达节点集 A:
将这些节点和其他节点分开:
从图中删除红色的边,使得删除这些边后,没有从 s 到 t 的路径:
相应的最大流量路径为:
−
s
→
a
→
b
→
t
:
11
−
s
→
c
→
a
→
b
→
t
:
1
−
s
→
c
→
d
→
b
→
t
:
7
−
s
→
c
→
d
→
t
:
4
在1962年,L.R.Ford和D.R.Fulkerson发明了一种解决最大流量问题的有效方法。它是一种沿着由起点到终点的路径逐步增加流量的通用方法,因此它也是同类算法的基础。在经典文献中它被称为Ford-Fulkerson算法,但它也被称为增广路径算法。
Ford-Fulkerson最大流量算法: 网络中的初始流量为零,沿着任意从起点到终点(且不含有饱和的正向边或是空逆向边)的增广路径增大流量,直到网络中不存在这样的路径为止。
from collections import defaultdict
初始化残差图(residual graph)以及行数 ROW。
def __init__(self, graph):
self.graph = graph # 残差图
self. ROW = len(graph)
# self.COL = len(gr[0])
使用广度优先搜索来找到从源点 s 到汇点 t 的一条增广路径,同时更新父节点数组 parent。
def BFS(self, s, t, parent):
visited = [False]*(self.ROW)
queue = []
queue.append(s)
visited[s] = True
while queue:
u = queue.pop(0)
for ind, val in enumerate(self.graph[u]):
if visited[ind] == False and val > 0:
queue.append(ind)
visited[ind] = True
parent[ind] = u
if ind == t:
return True
return False
在 FordFulkerson 方法中,使用 while 循环来不断寻找增广路径,并更新残差图直到没有增广路径为止。
def FordFulkerson(self, source, sink):
parent = [-1]*(self.ROW)
max_flow = 0
while self.BFS(source, sink, parent) :
path_flow = float("Inf")
s = sink
while(s != source):
path_flow = min (path_flow, self.graph[parent[s]][s])
s = parent[s]
max_flow += path_flow
v = sink
while(v != source):
u = parent[v]
self.graph[u][v] -= path_flow
self.graph[v][u] += path_flow
v = parent[v]
return max_flow
from collections import defaultdict
class Graph:
def __init__(self, graph):
self.graph = graph
self. ROW = len(graph)
def BFS(self, s, t, parent):
visited = [False]*(self.ROW)
queue = []
queue.append(s)
visited[s] = True
while queue:
u = queue.pop(0)
for ind, val in enumerate(self.graph[u]):
if visited[ind] == False and val > 0:
queue.append(ind)
visited[ind] = True
parent[ind] = u
if ind == t:
return True
return False
def FordFulkerson(self, source, sink):
parent = [-1]*(self.ROW)
max_flow = 0
while self.BFS(source, sink, parent) :
path_flow = float("Inf")
s = sink
while(s != source):
path_flow = min (path_flow, self.graph[parent[s]][s])
s = parent[s]
max_flow += path_flow
v = sink
while(v != source):
u = parent[v]
self.graph[u][v] -= path_flow
self.graph[v][u] += path_flow
v = parent[v]
return max_flow
graph = [[0, 16, 13, 0, 0, 0],
[0, 0, 10, 12, 0, 0],
[0, 4, 0, 0, 14, 0],
[0, 0, 9, 0, 0, 20],
[0, 0, 0, 7, 0, 4],
[0, 0, 0, 0, 0, 0]]
g = Graph(graph)
source = 0; sink = 5
print ("The maximum possible flow is %d " % g.FordFulkerson(source, sink))
运行结果为:
The maximum possible flow is 23
Edmonds和Karp发明的另一种Ford-Fulkerson算法的实现是优先处理能够将流量增大最多的增广路径。对于这种(以及其他一些)方法,可以通过稍加修改Dijkstra的最短路径算法、由优先队列得到剩余容量最大的正向边或是流量最大的逆向边来实现。或者也可以寻找最长增广路径,或是随机选择增广路径。
各种最大流量算法的分析仍然是一个有趣而活跃的研究领域。从理论角度来说,我们已经得到了各种最大流量算法在最坏情况下的上界,但这些上界大多远远高于实际应用中所观察到的真实成本,而且也比较小的下界(线性级别)高出许多。
“最大流量算法的实际应用仍然既是一门艺术也是一门科学。它的艺术之处在于为特定的应用场景选择最有效的策略;它的科学之处在于对问题本质的理解。”