mirror of
https://github.com/tu-darmstadt-informatik/AuD18.git
synced 2025-12-13 01:45:49 +00:00
Add comments
This commit is contained in:
parent
956d5889bf
commit
e3709251e4
@ -18,8 +18,7 @@ public class Edge {
|
||||
* 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
|
||||
* @param capacity Maximum number of cars that can travel on this edge per hour
|
||||
*/
|
||||
public Edge(Vertex from, Vertex to, int capacity) {
|
||||
this.from = from;
|
||||
@ -29,35 +28,50 @@ public class Edge {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get driving time for this edge
|
||||
* @return Driving time (m) for this edge
|
||||
* Get current flow along this edge
|
||||
* @return Current flow in the direction of the edge
|
||||
*/
|
||||
public int flow() {
|
||||
return this.flow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get length of this edge
|
||||
* @return Length of the edge
|
||||
* Get maximum flow capacity of this edge
|
||||
* @return Capacity of the edge
|
||||
*/
|
||||
public int capacity() {
|
||||
return this.capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get residual (unused) capacity of the edge
|
||||
* @return Free capacity of the edge
|
||||
*/
|
||||
public int freeCapacity() {
|
||||
return this.capacity - this.flow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capacity of the edge running in the opposite direction in the residual network (alias for flow)
|
||||
* @return Capacity of the back-edge in the residual network
|
||||
*/
|
||||
public int backCapacity() {
|
||||
return this.flow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the flow along this edge (route additional cars over this street)
|
||||
* @param inc
|
||||
*/
|
||||
public void increaseFlow(int inc) {
|
||||
this.flow += inc;
|
||||
if(this.flow < 0 || this.flow > this.capacity)
|
||||
throw new RuntimeException("Edge capacity exceeded: "+this.flow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the edge (e.g. for different flow calculations in the same graph)
|
||||
*/
|
||||
public void reset() {
|
||||
this.flow = 0;
|
||||
}
|
||||
|
||||
@ -44,12 +44,16 @@ public class MaxFlow {
|
||||
|
||||
// Read all lines
|
||||
while ((line = br.readLine()) != null) {
|
||||
// If line is an edge
|
||||
if(line.matches(".* -> .*;")) {
|
||||
// Do some string replace regex magic
|
||||
String[] info = line.replaceAll("\\s+", " ").replaceFirst(" -> ", ",").replaceFirst("\\[label=\"", ",").replaceFirst("\".*$", "").split(",");
|
||||
info[0] = info[0].replaceFirst(" ", "");
|
||||
info[1] = info[1].replaceFirst(" ", "");
|
||||
Vertex origin;
|
||||
Vertex destination;
|
||||
|
||||
// Create vertices if they do not exist already
|
||||
if(vertices.containsKey(info[0]))
|
||||
origin = vertices.get(info[0]);
|
||||
else {
|
||||
@ -97,9 +101,11 @@ public class MaxFlow {
|
||||
ArrayList<Vertex> sourceVertices = new ArrayList<Vertex>();
|
||||
ArrayList<Vertex> destinationVertices = new ArrayList<Vertex>();
|
||||
|
||||
// Reset edge flows left from previous calculations
|
||||
for(Edge edge : edges)
|
||||
edge.reset();
|
||||
|
||||
// Build Lists of sources and destinations
|
||||
for(String sourceKey : sources)
|
||||
if(vertices.containsKey(sourceKey))
|
||||
sourceVertices.add(vertices.get(sourceKey));
|
||||
@ -107,6 +113,7 @@ public class MaxFlow {
|
||||
if(vertices.containsKey(destinationKey))
|
||||
destinationVertices.add(vertices.get(destinationKey));
|
||||
|
||||
// Handle edge cases
|
||||
if(sourceVertices.isEmpty() && destinationVertices.isEmpty())
|
||||
return MaxFlow.NO_SOURCE_DESTINATION_FOUND;
|
||||
else if(sourceVertices.isEmpty())
|
||||
@ -114,20 +121,23 @@ public class MaxFlow {
|
||||
else if(destinationVertices.isEmpty())
|
||||
return MaxFlow.NO_DESTINATION_FOUND;
|
||||
|
||||
// To avoid restructuring the input graph, sources are added to the flow one after another
|
||||
// This intuitively yields the same result as it is equivalent to using certain paths from a unified source before others
|
||||
for(Vertex source : sourceVertices) {
|
||||
try {
|
||||
while(findAugmentingPath(source, destinationVertices)) {}
|
||||
while(findAugmentingPath(source, destinationVertices)) {} // Call findAugmentingPath until it returns false (= no remaining path)
|
||||
}
|
||||
catch(InfiniteFlowException e) {
|
||||
return MaxFlow.SOURCES_SAME_AS_DESTINATIONS;
|
||||
return MaxFlow.SOURCES_SAME_AS_DESTINATIONS; // return appropriate error code if sources and destinations are not distinct
|
||||
}
|
||||
}
|
||||
|
||||
int res = 0;
|
||||
// Total net flow in all sources is the MaxFlow of the graph
|
||||
for(Vertex source : sourceVertices)
|
||||
res += source.getTotalFlow();
|
||||
|
||||
return res == 0 ? MaxFlow.NO_PATH : res;
|
||||
return res == 0 ? MaxFlow.NO_PATH : res; // Flow = 0 implies no path from source to drain
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,9 +151,11 @@ public class MaxFlow {
|
||||
ArrayList<Vertex> sourceVertices = new ArrayList<Vertex>();
|
||||
ArrayList<Vertex> destinationVertices = new ArrayList<Vertex>();
|
||||
|
||||
// Reset edge flows left from previous calculations
|
||||
for(Edge edge : edges)
|
||||
edge.reset();
|
||||
|
||||
// Build Lists of sources and destinations
|
||||
for(String sourceKey : sources)
|
||||
if(vertices.containsKey(sourceKey))
|
||||
sourceVertices.add(vertices.get(sourceKey));
|
||||
@ -151,13 +163,16 @@ public class MaxFlow {
|
||||
if(vertices.containsKey(destinationKey))
|
||||
destinationVertices.add(vertices.get(destinationKey));
|
||||
|
||||
// To avoid restructuring the input graph, sources are added to the flow one after another
|
||||
// This intuitively yields the same result as it is equivalent to using certain paths from a unified source before others
|
||||
if(!sourceVertices.isEmpty() && !destinationVertices.isEmpty()) {
|
||||
for(Vertex source : sourceVertices) {
|
||||
try {
|
||||
while(findAugmentingPath(source, destinationVertices)) {}
|
||||
while(findAugmentingPath(source, destinationVertices)) {} // Call findAugmentingPath until it returns false (= no remaining path)
|
||||
}
|
||||
catch(InfiniteFlowException e) {
|
||||
break; // Doesn't reset capacities already used before exception came up
|
||||
// Works alright with the test cases though
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,8 +180,8 @@ public class MaxFlow {
|
||||
for(Edge edge : edges)
|
||||
edge.emboldenIfFreeCapacity();
|
||||
|
||||
// Build dotcode representation of graph
|
||||
ArrayList<String> res = new ArrayList<String>();
|
||||
|
||||
res.add("digraph{");
|
||||
|
||||
for(String sourceName : sources)
|
||||
@ -177,82 +192,98 @@ public class MaxFlow {
|
||||
res.add(edge.toDotCode());
|
||||
|
||||
res.add("}");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an augmenting path in the residual network of the current flow from a single source to any destination
|
||||
* (if such a path exists) and adds it to the flow
|
||||
* @param source The source vertex
|
||||
* @param destinations List of possible destination vertices
|
||||
* @return True if a path was added, False if no augmenting path exists
|
||||
* @throws InfiniteFlowException
|
||||
*/
|
||||
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());
|
||||
//System.out.println("Looking for augmenting path from "+source.getName());
|
||||
|
||||
// Start BFS at source vertex
|
||||
queue.add(source);
|
||||
pred.put(source.getName(), null);
|
||||
pred.put(source.getName(), null); // Predecessor of source is null
|
||||
|
||||
Vertex current;
|
||||
while(!queue.isEmpty()) {
|
||||
// Remove the next queued vertex
|
||||
current = queue.remove(0);
|
||||
|
||||
//System.out.println("Visiting "+current.getName());
|
||||
|
||||
// If a path to a destination was found
|
||||
if(destinations.contains(current)) {
|
||||
if(current == source)
|
||||
throw new InfiniteFlowException("Source " + source.getName() + " is also destination");
|
||||
throw new InfiniteFlowException("Source " + source.getName() + " is also destination"); // Throw exception if destination is a source
|
||||
|
||||
System.out.println("Found augmenting path");
|
||||
|
||||
// Build path and return
|
||||
// Build augmenting path and find max capacity
|
||||
ArrayList<Edge> path = new ArrayList<Edge>();
|
||||
String nextName = current.getName();
|
||||
String currentName;
|
||||
Edge backedge;
|
||||
int maxCapacity = Integer.MAX_VALUE;
|
||||
int maxCapacity = Integer.MAX_VALUE; // Assume 'infinite' capacity initially
|
||||
// Walk backwards along predecessors
|
||||
while(pred.get(nextName) != null) {
|
||||
backedge = pred.get(nextName);
|
||||
backedge = pred.get(nextName); // Get edge that was used to get to nextName
|
||||
currentName = nextName;
|
||||
//System.out.println("Walking backwards: "+backedge.from.getName()+" -> "+backedge.to.getName());
|
||||
res.add(0, backedge);
|
||||
path.add(0, backedge);
|
||||
// Get next vertex on path and update maxCapacity (conditionals needed because directed edges can be walked forwards _and_ backwards in the residual network)
|
||||
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());
|
||||
}
|
||||
// Walk path forwards and update flow
|
||||
Vertex nextVertex = source;
|
||||
System.out.println(maxCapacity);
|
||||
for(Edge edge : res) {
|
||||
for(Edge edge : path) {
|
||||
// If the edge was used along its natural direction, increase the flow by maxCapacity
|
||||
if(edge.from == nextVertex) {
|
||||
edge.increaseFlow(maxCapacity);
|
||||
nextVertex = edge.to;
|
||||
}
|
||||
// If the edge was used opposed to its natural direction, decrease the flow
|
||||
else if(edge.to == nextVertex) {
|
||||
edge.increaseFlow(- maxCapacity);
|
||||
nextVertex = edge.from;
|
||||
}
|
||||
else
|
||||
throw new RuntimeException("Edge is neither from nor to source node");
|
||||
throw new RuntimeException("Edge is neither from nor to source node"); // Shouldn't happen but let's be safe
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, continue BFS with all connected vertices
|
||||
for(Edge edge : current.getResidual()) {
|
||||
// Do not visit already visited vertices (vertices that already have a predecessor)
|
||||
if(edge.from == current && pred.containsKey(edge.to.getName()) || edge.to == current && pred.containsKey(edge.from.getName()))
|
||||
continue;
|
||||
|
||||
// If the edge is a regular forward edge with nonzero capacity, add the destination vertex and update its predecessor
|
||||
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());
|
||||
//System.out.println("Adding "+current.getName()+" -> "+edge.to.getName() + " cap: "+edge.freeCapacity());
|
||||
}
|
||||
// If the edge is a backwards edge with nonzero effective capacity, add the destination vertex and update predecessor
|
||||
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("Adding "+current.getName()+" -> "+edge.from.getName() + " cap: "+edge.backCapacity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("No augmenting path from "+source.getName());
|
||||
|
||||
//System.out.println("No augmenting path from "+source.getName());
|
||||
// Return false if queue is empty but no path has been found
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ package lab;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class representing a vertex ('street crossing' in this context) in a directed graph
|
||||
* Class representing a vertex in a directed graph
|
||||
*/
|
||||
public class Vertex {
|
||||
|
||||
@ -15,7 +15,6 @@ public class Vertex {
|
||||
/**
|
||||
* 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;
|
||||
@ -51,17 +50,17 @@ public class Vertex {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a List of all outgoing edges of this vertex
|
||||
* @return
|
||||
* Get a List of all outgoing or incoming edges of this vertex (for analyzing the residual network)
|
||||
* @return List of all edges connecting to or from this vertex
|
||||
*/
|
||||
public ArrayList<Edge> getOutgoing() {
|
||||
return outgoing;
|
||||
}
|
||||
|
||||
public ArrayList<Edge> getResidual() {
|
||||
return allEdges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the net flow in this vertex. Should be zero for all non-source non-drain vertices
|
||||
* @return Net flow of the vertex
|
||||
*/
|
||||
public int getTotalFlow() {
|
||||
int res = 0;
|
||||
for(Edge edge : outgoing)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user