读者盆友,下午好。这里介绍下最短增广路径的Ford-Fulkerson最大流量算法
看这个需要对“网络流量算法”中的基本概念有所了解,这里就不啰嗦了。
直奔主题哈。这是这段时间算法系列的最后一篇拉,也算是五一劳动节对自己的奖励吧。迟点去去吃好吃的:肠粉。
本博客代码示例均来自:算法 Algorithmes Forth Edition
[美] Robert Sedgewick Kevin Wayne 著 谢路云译
package com.cmh.algorithm.background;
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.StdOut;
/**
* 最短增广路径的Ford-Fulkerson最大流量算法
* Author:起舞的日子
* Date: 2020/5/1 17:40
*/
public class FordFulkerson {
/**
* 网络中的初始流量为零,沿着任意从起点到终点(且不含有饱和的正向边或是空逆向边)的增广路径增大流量
* ,知道网络中不存在这样的路径为止
*/
/**
* 在剩余网络中是否存在从s到v的路径?
*/
private boolean[] marked;
/**
* 从s到v的最短路径上的最后一条边
*/
private FlowEdge[] edgeTo;
/**
* 当前最大流量
*/
private double value;
public FordFulkerson(FlowNetWork G, int s, int t) {
//找出从s到t的流量网络G的最大流量配置
while (hasAugmentingPath(G, s, t)) {
//利用所有存在的增广路径
//计算当前的瓶颈容量
double bottle = Double.POSITIVE_INFINITY;
for (int v = t; v != s; v = edgeTo[v].other(v)) {
bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v));
}
//增大流量
for (int v = t; v != s; v = edgeTo[v].other(v)) {
edgeTo[v].addResidualFlowTo(v, bottle);
}
value += bottle;
}
}
public double value() {
return value;
}
public boolean inCut(int v) {
return marked[v];
}
/**
* 在剩余网络中屠龙过广度优先搜索寻找增广路径
*/
private boolean hasAugmentingPath(FlowNetWork G, int s, int t) {
//标记路径已知的顶点
marked = new boolean[G.V()];
//路径上最后一条边
edgeTo = new FlowEdge[G.V()];
Queue<Integer> q = new Queue<>();
//标记起点
marked[s] = true;
q.enqueue(s);
while (!q.isEmpty()) {
int v = q.dequeue();
for (FlowEdge e : G.adj(v)) {
int w = e.other(v);
if (e.residualCapacityTo(w) > 0 && !marked[w]) {
//在剩余网络中,对于任意一条连接到一个未被标记的顶点的边
//保存路径上最后一条边
edgeTo[w] = e;
//标记w,因为路径现在是已知的了
marked[w] = true;
q.enqueue(w);
}
}
}
return marked[t];
}
public static void main(String[] args) {
FlowNetWork G = new FlowNetWork(new In(args[0]));
int s = 0, t = G.V() - 1;
FordFulkerson maxFlow = new FordFulkerson(G, s, t);
StdOut.println("Max flow from " + s + " to" + t);
for (int v = 0; v < G.V(); v++) {
for (FlowEdge e : G.adj(v)) {
if ((v == e.from()) && e.flow() > 0) {
StdOut.println(" " + e);
}
}
}
StdOut.println("Max flow value = " + maxFlow.value());
}
}
下面是该算法需要用到的数据类型:FlowEdge 、FlowNetWork
package com.cmh.algorithm.background;
/**
* 流量网络中的边(剩余网络)
* Author:起舞的日子
* Date: 2020/5/1 17:45
*/
public class FlowEdge {
/**
* 边的起点
*/
private final int v;
/**
* 边的终点
*/
private final int w;
/**
* 容量
*/
private final double capacity;
/**
* 流量
*/
private double flow;
public FlowEdge(int v, int w, double capacity) {
this.v = v;
this.w = w;
this.capacity = capacity;
this.flow = 0.0;
}
public int from() {
return v;
}
public int to() {
return w;
}
public double capacity() {
return capacity;
}
public double flow() {
return flow;
}
public int other(int vertex) {
if (vertex == v) {
return w;
} else if (vertex == w) {
return v;
} else {
throw new RuntimeException("Inconsistent edge");
}
}
public double residualCapacityTo(int vertex) {
if (vertex == v) {
return flow;
} else if (vertex == w) {
return capacity - flow;
} else {
throw new RuntimeException(" Inconsistent edge");
}
}
public void addResidualFlowTo(int vertex, double delta) {
if (vertex == v) {
flow = flow -= delta;
} else if (vertex == w) {
flow += delta;
} else {
throw new RuntimeException("Inconsistent edge");
}
}
public String toString() {
return String.format("%d->%d %2f %2.f", v, w, capacity, flow);
}
}
package com.cmh.algorithm.background;
import edu.princeton.cs.algs4.Bag;
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
/**
* Author:起舞的日子
* Date: 2020/5/1 17:57
*/
public class FlowNetWork {
private static final String NEWLINE = System.getProperty("line.separator");
private final int V;
private int E;
private Bag<FlowEdge>[] adj;
public FlowNetWork(int V) {
if (V < 0) {
throw new IllegalArgumentException("Number of vertices in a Graph must be nonnegative");
}
this.V = V;
this.E = 0;
adj = new Bag[V];
for (int v = 0; v < V; v++) {
adj[v] = new Bag<>();
}
}
public FlowNetWork(int V, int E) {
this(V);
if (E < 0) {
throw new IllegalArgumentException("Number of edges must be nonnegative");
}
for (int i = 0; i < E; i++) {
int v = StdRandom.uniform(V);
int w = StdRandom.uniform(V);
double capacity = StdRandom.uniform(100);
addEdge(new FlowEdge(v, w, capacity));
}
}
public FlowNetWork(In in) {
this(in.readInt());
int E = in.readInt();
if (E < 0) {
throw new IllegalArgumentException("number of edges must be nonnegative");
}
for (int i = 0; i < E; i++) {
int v = in.readInt();
int w = in.readInt();
validateVertex(v);
validateVertex(w);
double capacity = in.readDouble();
addEdge(new FlowEdge(v, w, capacity));
}
}
public void addEdge(FlowEdge e) {
int v = e.from();
int w = e.to();
validateVertex(v);
validateVertex(w);
adj[v].add(e);
adj[w].add(e);
E++;
}
private void validateVertex(int v) {
if (v < 0 || v >= V) {
throw new IllegalArgumentException("vertex" + v + "is not between 0 and " + (V - 1));
}
}
public int V() {
return V;
}
public int E() {
return E;
}
public Iterable<FlowEdge> adj(int v) {
validateVertex(v);
return adj(v);
}
public Iterable<FlowEdge> edges() {
Bag<FlowEdge> list = new Bag<>();
for (int v = 0; v < V; v++) {
for (FlowEdge e : adj(v)) {
if (e.to() != v) {
list.add(e);
}
}
}
return list;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append(V + " " + E + NEWLINE);
for (int v = 0; v < V; v++) {
s.append(v + ": ");
for (FlowEdge e : adj(v)) {
if (e.to() != v) {
s.append(e + " ");
}
}
s.append(NEWLINE);
}
return s.toString();
}
public static void main(String[] args) {
In in = new In(args[0]);
FlowNetWork G = new FlowNetWork(in);
StdOut.println(G);
}
}
https://github.com/cmhhcm/my2020.git
好了,读者盆友,晚安。接下来我们将走入:设计模式系列。
然后是微服务系列:SpringBoot SpingCluod等。我们会争取在接下来的2周内完成。