mirror of
https://github.com/tu-darmstadt-informatik/AuD18.git
synced 2025-12-13 01:45:49 +00:00
Initial implementation of MaxFlow
This commit is contained in:
parent
8f2a9ea67c
commit
53b0546bed
11
MaxFlow/.classpath
Normal file
11
MaxFlow/.classpath
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
1
MaxFlow/.gitignore
vendored
Normal file
1
MaxFlow/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/bin/
|
||||
17
MaxFlow/.project
Normal file
17
MaxFlow/.project
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>MaxFlow</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
79
MaxFlow/src/lab/Edge.java
Normal file
79
MaxFlow/src/lab/Edge.java
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package lab;
|
||||
|
||||
/**
|
||||
* Class representing an edge in a directed graph
|
||||
*/
|
||||
public class Edge {
|
||||
|
||||
protected final Vertex from;
|
||||
protected final Vertex to;
|
||||
private final int capacity;
|
||||
private int flow;
|
||||
private boolean bold = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param from Source vertex of the edge
|
||||
* @param to Destination vertex of the edge
|
||||
* @param distance Length (km) of the edge
|
||||
* @param speedLimit Speed with which the 'car' can travel along this edge
|
||||
*/
|
||||
public Edge(Vertex from, Vertex to, int capacity) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.capacity = capacity;
|
||||
this.flow = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get driving time for this edge
|
||||
* @return Driving time (m) for this edge
|
||||
*/
|
||||
public int flow() {
|
||||
return this.flow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get length of this edge
|
||||
* @return Length of the edge
|
||||
*/
|
||||
public int capacity() {
|
||||
return this.capacity;
|
||||
}
|
||||
|
||||
public int freeCapacity() {
|
||||
return this.capacity - this.flow;
|
||||
}
|
||||
|
||||
public int backCapacity() {
|
||||
return this.flow;
|
||||
}
|
||||
|
||||
public void increaseFlow(int inc) {
|
||||
this.flow += inc;
|
||||
if(this.flow < 0 || this.flow > this.capacity)
|
||||
throw new RuntimeException("Edge capacity exceeded: "+this.flow);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.flow = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Embolden the edge if it's capacity was not fully utilized
|
||||
*/
|
||||
public void emboldenIfFreeCapacity() {
|
||||
this.bold = this.freeCapacity() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a dot code representation of this edge
|
||||
* @return Dot code line representing the edge
|
||||
*/
|
||||
public String toDotCode() {
|
||||
return from.getName() + " -> " + to.getName() + " [label=\""+ this.capacity + "-" + this.flow + "\"]" + (this.bold ? "[style=bold];" : ";");
|
||||
}
|
||||
}
|
||||
18
MaxFlow/src/lab/InfiniteFlowException.java
Normal file
18
MaxFlow/src/lab/InfiniteFlowException.java
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package lab;
|
||||
|
||||
/**
|
||||
* Exception signaling that a given graph has infinite maximum flow because at
|
||||
* least one source and destination are equal
|
||||
*/
|
||||
public class InfiniteFlowException extends Exception {
|
||||
|
||||
public InfiniteFlowException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -2738022894329680602L;
|
||||
|
||||
}
|
||||
@ -1,6 +1,12 @@
|
||||
package lab;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
/**
|
||||
* MaxFlow.java
|
||||
*/
|
||||
@ -21,14 +27,57 @@ public class MaxFlow {
|
||||
public static final int NO_PATH = -4;
|
||||
public static final int SOURCES_SAME_AS_DESTINATIONS = Integer.MAX_VALUE;
|
||||
|
||||
private HashMap<String, Vertex> vertices;
|
||||
private ArrayList<Edge> edges;
|
||||
|
||||
/**
|
||||
* The constructor, setting the name of the file to parse.
|
||||
*
|
||||
* @param filename the absolute or relative path and filename of the file
|
||||
*/
|
||||
public MaxFlow(final String filename) {
|
||||
//TODO Add you code here
|
||||
this.vertices = new HashMap<String, Vertex>();
|
||||
this.edges = new ArrayList<Edge>();
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
|
||||
String line;
|
||||
|
||||
// Read all lines
|
||||
while ((line = br.readLine()) != null) {
|
||||
if(line.matches(".* -> .*;")) {
|
||||
String[] info = line.replaceAll("\\s+", " ").replaceFirst(" -> ", ",").replaceFirst("\\[label=\"", ",").replaceFirst("\".*$", "").split(",");
|
||||
info[0] = info[0].replaceFirst(" ", "");
|
||||
info[1] = info[1].replaceFirst(" ", "");
|
||||
Vertex origin;
|
||||
Vertex destination;
|
||||
if(vertices.containsKey(info[0]))
|
||||
origin = vertices.get(info[0]);
|
||||
else {
|
||||
origin = new Vertex(info[0]);
|
||||
this.vertices.put(info[0], origin);
|
||||
}
|
||||
|
||||
if(vertices.containsKey(info[1]))
|
||||
destination = vertices.get(info[1]);
|
||||
else {
|
||||
destination = new Vertex(info[1]);
|
||||
this.vertices.put(info[1], destination);
|
||||
}
|
||||
|
||||
Edge edge = new Edge(origin, destination, Integer.parseInt(info[2]));
|
||||
// Add edge to source and destination vertex
|
||||
origin.addOutgoing(edge);
|
||||
destination.addIncoming(edge);
|
||||
this.edges.add(edge);
|
||||
//System.out.println("Parse edge "+info[0]+"->"+info[1]+" cap: "+info[2]);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("File not found");
|
||||
} catch (IOException e) {
|
||||
System.out.println("IO Exception");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,8 +94,40 @@ public class MaxFlow {
|
||||
* SOURCES_SAME_AS_DESTINATIONS if sources == destinations
|
||||
*/
|
||||
public final int findMaxFlow(final String[] sources, final String[] destinations) {
|
||||
//TODO Add you code here
|
||||
return 0; // dummy, replace
|
||||
ArrayList<Vertex> sourceVertices = new ArrayList<Vertex>();
|
||||
ArrayList<Vertex> destinationVertices = new ArrayList<Vertex>();
|
||||
|
||||
for(Edge edge : edges)
|
||||
edge.reset();
|
||||
|
||||
for(String sourceKey : sources)
|
||||
if(vertices.containsKey(sourceKey))
|
||||
sourceVertices.add(vertices.get(sourceKey));
|
||||
for(String destinationKey : destinations)
|
||||
if(vertices.containsKey(destinationKey))
|
||||
destinationVertices.add(vertices.get(destinationKey));
|
||||
|
||||
if(sourceVertices.isEmpty() && destinationVertices.isEmpty())
|
||||
return MaxFlow.NO_SOURCE_DESTINATION_FOUND;
|
||||
else if(sourceVertices.isEmpty())
|
||||
return MaxFlow.NO_SOURCE_FOUND;
|
||||
else if(destinationVertices.isEmpty())
|
||||
return MaxFlow.NO_DESTINATION_FOUND;
|
||||
|
||||
for(Vertex source : sourceVertices) {
|
||||
try {
|
||||
while(findAugmentingPath(source, destinationVertices)) {}
|
||||
}
|
||||
catch(InfiniteFlowException e) {
|
||||
return MaxFlow.SOURCES_SAME_AS_DESTINATIONS;
|
||||
}
|
||||
}
|
||||
|
||||
int res = 0;
|
||||
for(Vertex source : sourceVertices)
|
||||
res += source.getTotalFlow();
|
||||
|
||||
return res == 0 ? MaxFlow.NO_PATH : res;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,8 +138,122 @@ public class MaxFlow {
|
||||
* @return a ArrayList of Strings as specified in the task in dot code
|
||||
*/
|
||||
public final ArrayList<String> findResidualNetwork(final String[] sources, final String[] destinations) {
|
||||
//TODO Add you code here
|
||||
return null; // dummy, replace
|
||||
ArrayList<Vertex> sourceVertices = new ArrayList<Vertex>();
|
||||
ArrayList<Vertex> destinationVertices = new ArrayList<Vertex>();
|
||||
|
||||
for(Edge edge : edges)
|
||||
edge.reset();
|
||||
|
||||
for(String sourceKey : sources)
|
||||
if(vertices.containsKey(sourceKey))
|
||||
sourceVertices.add(vertices.get(sourceKey));
|
||||
for(String destinationKey : destinations)
|
||||
if(vertices.containsKey(destinationKey))
|
||||
destinationVertices.add(vertices.get(destinationKey));
|
||||
|
||||
if(!sourceVertices.isEmpty() && !destinationVertices.isEmpty()) {
|
||||
for(Vertex source : sourceVertices) {
|
||||
try {
|
||||
while(findAugmentingPath(source, destinationVertices)) {}
|
||||
}
|
||||
catch(InfiniteFlowException e) {
|
||||
break; // Doesn't reset capacities already used before exception came up
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(Edge edge : edges)
|
||||
edge.emboldenIfFreeCapacity();
|
||||
|
||||
ArrayList<String> res = new ArrayList<String>();
|
||||
|
||||
res.add("digraph{");
|
||||
|
||||
for(String sourceName : sources)
|
||||
res.add(sourceName+" [shape=doublecircle][style=bold];");
|
||||
for(String destName : destinations)
|
||||
res.add(destName+" [shape=circle][style=bold];");
|
||||
for(Edge edge : this.edges)
|
||||
res.add(edge.toDotCode());
|
||||
|
||||
res.add("}");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private boolean findAugmentingPath(Vertex source, ArrayList<Vertex> destinations) throws InfiniteFlowException{
|
||||
HashMap<String,Edge> pred = new HashMap<String,Edge>();
|
||||
ArrayList<Vertex> queue = new ArrayList<Vertex>();
|
||||
ArrayList<Edge> res = new ArrayList<Edge>();
|
||||
|
||||
System.out.println("Looking for augmenting path from "+source.getName());
|
||||
|
||||
queue.add(source);
|
||||
pred.put(source.getName(), null);
|
||||
|
||||
Vertex current;
|
||||
while(!queue.isEmpty()) {
|
||||
current = queue.remove(0);
|
||||
|
||||
//System.out.println("Visiting "+current.getName());
|
||||
|
||||
if(destinations.contains(current)) {
|
||||
if(current == source)
|
||||
throw new InfiniteFlowException("Source " + source.getName() + " is also destination");
|
||||
|
||||
System.out.println("Found augmenting path");
|
||||
|
||||
// Build path and return
|
||||
String nextName = current.getName();
|
||||
String currentName;
|
||||
Edge backedge;
|
||||
int maxCapacity = Integer.MAX_VALUE;
|
||||
while(pred.get(nextName) != null) {
|
||||
backedge = pred.get(nextName);
|
||||
currentName = nextName;
|
||||
//System.out.println("Walking backwards: "+backedge.from.getName()+" -> "+backedge.to.getName());
|
||||
res.add(0, backedge);
|
||||
nextName = (backedge.to.getName().equals(currentName)) ? backedge.from.getName() : backedge.to.getName();
|
||||
maxCapacity = (backedge.to.getName().equals(currentName)) ? Math.min(maxCapacity, backedge.freeCapacity()) : Math.min(maxCapacity, backedge.backCapacity());
|
||||
}
|
||||
Vertex nextVertex = source;
|
||||
System.out.println(maxCapacity);
|
||||
for(Edge edge : res) {
|
||||
if(edge.from == nextVertex) {
|
||||
edge.increaseFlow(maxCapacity);
|
||||
nextVertex = edge.to;
|
||||
}
|
||||
else if(edge.to == nextVertex) {
|
||||
edge.increaseFlow(- maxCapacity);
|
||||
nextVertex = edge.from;
|
||||
}
|
||||
else
|
||||
throw new RuntimeException("Edge is neither from nor to source node");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
for(Edge edge : current.getResidual()) {
|
||||
if(edge.from == current && pred.containsKey(edge.to.getName()) || edge.to == current && pred.containsKey(edge.from.getName()))
|
||||
continue;
|
||||
|
||||
if(edge.from == current && edge.freeCapacity() > 0) {
|
||||
queue.add(edge.to);
|
||||
pred.put(edge.to.getName(), edge);
|
||||
System.out.println("Adding "+current.getName()+" -> "+edge.to.getName() + " cap: "+edge.freeCapacity());
|
||||
}
|
||||
else if(edge.to == current && edge.backCapacity() > 0) {
|
||||
queue.add(edge.from);
|
||||
pred.put(edge.from.getName(), edge);
|
||||
System.out.println("Adding "+current.getName()+" -> "+edge.from.getName() + " cap: "+edge.backCapacity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("No augmenting path from "+source.getName());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
81
MaxFlow/src/lab/Vertex.java
Normal file
81
MaxFlow/src/lab/Vertex.java
Normal file
@ -0,0 +1,81 @@
|
||||
package lab;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class representing a vertex ('street crossing' in this context) in a directed graph
|
||||
*/
|
||||
public class Vertex {
|
||||
|
||||
private final String name;
|
||||
private ArrayList<Edge> incoming;
|
||||
private ArrayList<Edge> outgoing;
|
||||
private ArrayList<Edge> allEdges;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param name Name of the vertex. Assumed to be per-graph unique
|
||||
* @param waitTime Average time spent waiting at this vertex
|
||||
*/
|
||||
public Vertex(String name) {
|
||||
this.name = name;
|
||||
this.incoming = new ArrayList<Edge>();
|
||||
this.outgoing = new ArrayList<Edge>();
|
||||
this.allEdges = new ArrayList<Edge>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter method for vertex name
|
||||
* @return Vertex name
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an incoming edge to this vertex
|
||||
* @param edge Incoming edge
|
||||
*/
|
||||
public void addIncoming(Edge edge) {
|
||||
incoming.add(edge);
|
||||
allEdges.add(edge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an outgoing edge to this vertex
|
||||
* @param edge Outgoing edge
|
||||
*/
|
||||
public void addOutgoing(Edge edge) {
|
||||
outgoing.add(edge);
|
||||
allEdges.add(edge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a List of all outgoing edges of this vertex
|
||||
* @return
|
||||
*/
|
||||
public ArrayList<Edge> getOutgoing() {
|
||||
return outgoing;
|
||||
}
|
||||
|
||||
public ArrayList<Edge> getResidual() {
|
||||
return allEdges;
|
||||
}
|
||||
|
||||
public int getTotalFlow() {
|
||||
int res = 0;
|
||||
for(Edge edge : outgoing)
|
||||
res += edge.flow();
|
||||
for(Edge edge : incoming)
|
||||
res -= edge.flow();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a dot code representation of this vertex
|
||||
* @return Dot code representing the vertex
|
||||
*/
|
||||
public String toDotCode() {
|
||||
return this.name + "[label=\"" + this.name + "\"];";
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user