Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.
Negative Cycle Detection
Hallo,
um einen min-cost-flow mit dem Kreislöschungsalgorithmus zu bestimmen, versuche ich den Algorithmus von Bellman-Ford zu implementieren, der negative Kreis detektiert.
Der Digraph, den die Methode übergeben bekommt, enthält bereits Vorwärts- und Rückwärtskanten. Wenn der Fluss auf der Vorwärtskante z.B. 2 ist, dann ist der Fluss auf der Rückwärtskante 0 und deren Kapazität 2. Verringert sich der Fluss auf der Vorwärtskante, wird auch die Kapazität auf der Rückwärtskante verringert. Wenn also keine Kapazität mehr auf einer Kante übrig ist, dann ignoriere ich diese Kante beim Bellman-Ford Verfahren.
Leider scheint meine Implementierung nicht alle negativen Kreise zu finden. Sieht vielleicht jemand das Problem? Würde mich auch über eine Referenzimplementierung freuen. Im Anhang ist der komplette Code zum Ausprobieren.
public static List<DirectedEdge> bellmanFord(Digraph digraph, final int source) {
Set<Integer> nodes = digraph.nodes();
// Initialize distance vector
Map<Integer, Integer> distances = new HashMap<>(nodes.size());
Map<Integer, Integer> predecessors = new HashMap<>(nodes.size());
for (Integer node : nodes) {
if (source == node) {
distances.put(node, 0);
} else {
distances.put(node, Integer.MAX_VALUE);
}
predecessors.put(node, -1);
}
// Relaxate |nodes|-1 times
for (int i=0; i<nodes.size()-1; i++) {
for (DirectedEdge e : digraph.edges()) {
// Only consider edges where some capacity is remaining
if (e.capacity() - e.flow() <= 0) {
continue;
}
if (distances.get(e.to()) > distances.get(e.from()) + e.costs()) {
distances.put(e.to(), distances.get(e.from()) + e.costs()); // update distance label
predecessors.put(e.to(), e.from()); // update predecessor
}
}
}
// Check whether there is an edge (u, v) such that d(u) + W(u, v) < d(v)
for (DirectedEdge e : digraph.edges()) {
// Only consider edges where some capacity is remaining
if (e.capacity() - e.flow() <= 0) {
continue;
}
if (distances.get(e.to()) > distances.get(e.from()) + e.costs()) {
// Negative cycle detected. Go backward from v along the predecessor chain, until a cycle is found
List<Integer> nodesInChain = new LinkedList<>();
List<DirectedEdge> edgesAlongCycle = new LinkedList<>();
int current = e.to();
int predecessor = e.from();
DirectedEdge edge = e;
while (!nodesInChain.contains(current)) {
nodesInChain.add(current);
edgesAlongCycle.add(edge);
current = predecessor;
predecessor = predecessors.get(current);
int lowestCost = Integer.MAX_VALUE;
Set<DirectedEdge> possibleEdges = digraph.edges(predecessor, current);
for (DirectedEdge possibleEdge : possibleEdges) {
// Take the one that has capacity remaining of course and the lowest costs
if (possibleEdge.capacity() - possibleEdge.flow() <= 0) {
continue;
}
if (possibleEdge.costs() < lowestCost) {
edge = possibleEdge;
lowestCost = possibleEdge.costs();
}
}
if (Integer.MAX_VALUE == lowestCost) {
throw new Exception("No edge found");
}
}
// Cycle completely found
// Let's see where the cycle has started
int startIndex = nodesInChain.indexOf(current);
// And throw out the earlier edges which are not part of the cycle
for (int i=0; i<startIndex; i++) {
edgesAlongCycle.remove(0);
}
// Return the negative cycle
return edgesAlongCycle;
}
}
// No negative-weight cycles found
return null;
}
Attachment:
mincostflow.zip: https://fsi.cs.fau.de/unb-attachments/post_138076/mincostflow.zip
Hat sich erledigt, hab eine andere Implementierung aus Princeton gefunden. Die bauen mit den Kanten zu den jeweiligen Vorgängerknoten einen kleineren Graphen auf und suchen dann darin mit Tiefensuche nach Zyklen.
Hier der Quellcode:
BellmanFordSP
EdgeWeightedDirectedCycle