public void Execute() { var hNodeDictionary = FUsedGraph.GetNodeDictionary(); var hNodeCount = hNodeDictionary.Count; // Erstellen der SuperQuelle var hSuperSource = new BalancedNode(hNodeCount, 0.0); FUsedGraph.TryToAddNode(hSuperSource); // Erstellen der SuperSenke var hSuperTarget = new BalancedNode(hNodeCount + 1, 0.0); FUsedGraph.TryToAddNode(hSuperTarget); // Balance der Knoten prüfen und an SuperQuelle und SuperSenke anhängen foreach (var hNode in hNodeDictionary.Values) { var hNodeBalance = ((BalancedNode)hNode).Balance; if (hNodeBalance > 0.0) { // Es ist eine Quelle. Die SuperQuelle soll nun eine Kante auf diesen Knoten haben. Die Kapazität der Kante entspricht der Balance des Knotens var hNewEdge = new DirectedEdge(hSuperSource, hNode); hNewEdge.AddWeight(new CapacityWeighted(hNodeBalance)); FUsedGraph.AddEdge(hNewEdge); // Todo: Prüfen ob es richtig funktioniert } else if (hNodeBalance < 0.0) { // Es ist eine Senke. Dieser Knoten soll nun eine Kante zur SuperSenke haben. Die Kapazität der Kante entspricht der Balance des Knotens var hNewEdge = new DirectedEdge(hNode, hSuperTarget); hNewEdge.AddWeight(new CapacityWeighted(-1.0 * hNodeBalance)); FUsedGraph.AddEdge(hNewEdge); // Todo: Prüfen ob es richtig funktioniert } } FUsedGraph.UpdateNeighbourInfoInNodes(); // Nun soll der Edmonds-Karp Algorithmus ausgeführt werden. Dabei werden die SuperQuelle und SuperSenke verwendet var hEdmondsKarpAlgorithm = new EdmondsKarpAlgorithm(FUsedGraph); var hFlow = hEdmondsKarpAlgorithm.Execute(hSuperSource.Id, hSuperTarget.Id); // Nutzt die SuperQuelle die Kantenkapazitäten voll aus? foreach (var hNeighborEdge in hSuperSource.NeighbourEdges) { var hNeighborEdgeHash = hNeighborEdge.Edge.EdgeHashInfo()[0]; var hFlowValue = hFlow[hNeighborEdgeHash]; var hEdgeCapacity = hNeighborEdge.Edge.GetWeightValue <CapacityWeighted>(); if (!hFlowValue.Equals(hEdgeCapacity)) { // Kein gültiger b-Fluss (Graph kann nicht alles übertragen) FUsedGraph.RemoveNode(hSuperSource); FUsedGraph.RemoveNode(hSuperTarget); Console.WriteLine("Kein Fluss möglich"); return; } } // Löschen der SuperQuelle, SuperSenke und den verbindenden Kanten FUsedGraph.RemoveNode(hSuperSource); FUsedGraph.RemoveNode(hSuperTarget); // Den Fluss übernehmen. Dabei aber die Infos zur SuperQuelle und SuperSenke Ignorieren var hTotalFlow = 0.0; var hGraphEdgeDictionary = FUsedGraph.GenerateEdgeHashDictionary(); var hFlussGraphDictionary = new Dictionary <string, double>(); foreach (var hEdgeHash in hGraphEdgeDictionary.Keys) { hFlussGraphDictionary.Add(hEdgeHash, 0.0); } foreach (var hFlowInfo in hFlow) { if (hFlussGraphDictionary.ContainsKey(hFlowInfo.Key)) { hFlussGraphDictionary[hFlowInfo.Key] = hFlowInfo.Value; } } // Solange es noch negative Zyklen gibt var hFoundNegativeCycle = true; while (hFoundNegativeCycle) { hFoundNegativeCycle = false; // Residualgraphen erstellen var hResidualGraph = ResidualGraphGenerator.GenerateCostCapacity(FUsedGraph, hFlussGraphDictionary); var hEdgeHashDictionary = hResidualGraph.GenerateEdgeHashDictionary(); // Ausgehend von jedem Knoten wird versucht ein negativer Zykel zu finden foreach (var hNode in hResidualGraph.GetNodeDictionary()) { // Negativen Zyklus bezüglich den Kosten suchen var hBellmanFordAlgorithm = new BellmanFordAlgorithm(hResidualGraph); hBellmanFordAlgorithm.Execute(hNode.Value.Id); if (hBellmanFordAlgorithm.HasNegativeCycles) { // Negativen Zyklus gefunden hFoundNegativeCycle = true; // ToDo Auf diesem Zyklus die maximale Kapazität ermitteln var hEdgeHashesOfCycle = hBellmanFordAlgorithm.CycleEdges; var hMinCapacityInCycle = Double.PositiveInfinity; foreach (var hEdgeInCycleHash in hEdgeHashesOfCycle) { var hEdgeCapcaityValue = hEdgeHashDictionary[hEdgeInCycleHash].GetWeightValue <CapacityWeighted>(); if (hEdgeCapcaityValue < hMinCapacityInCycle) { hMinCapacityInCycle = hEdgeCapcaityValue; } } hTotalFlow += hMinCapacityInCycle; // ToDo Fluss auf dem Originalen Graphen entsprechend anpassen (Orginial Kante, Nicht-Original Kante) foreach (var hEdgeInCycleHash in hEdgeHashesOfCycle) { if (ResidualGraphGenerator.HinkantenEdgeHashes.Contains(hEdgeInCycleHash)) { // Hinkante // Diese Kante kann ich im Flussgraphen Dictionary direkt ansprechen da die Konten im Hash in der richtigen Reihenfolge stehen hFlussGraphDictionary[hEdgeInCycleHash] += hMinCapacityInCycle; } else if (ResidualGraphGenerator.RueckKantenEdgeHashes.Contains(hEdgeInCycleHash)) { // Bei der Breitensuche im Augmentieren Graphen wurde eine Rückkante verwendet. // Diese Rückkante existiert so im originalen Graphen nicht weshalb auch der Hash-Value nicht passt. // Dieser ist lediglich verdreht. Also wird der HashWert vorher noch gespiegelt um die ursprüngliche Kante zu identifizieren var hHashValueOfOriginalEdge = HelpFunctions.InvertEdgeHash(hEdgeInCycleHash); hFlussGraphDictionary[hHashValueOfOriginalEdge] -= hMinCapacityInCycle; } } break; } } } // Kosten des Flusses ermitteln var hFlowCost = 0.0; foreach (var hEdgeInFlow in hFlussGraphDictionary) { var hEdge = hGraphEdgeDictionary[hEdgeInFlow.Key]; hFlowCost += hEdge.GetWeightValue <CostWeighted>() * hEdgeInFlow.Value; } Console.WriteLine("Kostenminimaler Fluss:\t" + hFlowCost); }
public void Execute() { var hGraphEdgeHashDictionary = FUsedGraph.GenerateEdgeHashDictionary(); var hNodeDictionary = FUsedGraph.GetNodeDictionary(); var hFlussGraphDictionary = new Dictionary <string, double>(); foreach (var hEdge in hGraphEdgeHashDictionary) { var hFlowValue = 0.0; var hEdgeCost = hEdge.Value.GetWeightValue <CostWeighted>(); if (hEdgeCost < 0.0) { hFlowValue = hEdge.Value.GetWeightValue <CapacityWeighted>(); } // Negative Kanten direkt voll auslasten hFlussGraphDictionary.Add(hEdge.Key, hFlowValue); } // Pseudo-Balance Initalisieren var hPseudoBalanceDictionary = new Dictionary <int, double>(); foreach (var hNode in hNodeDictionary) { hPseudoBalanceDictionary.Add(hNode.Key, 0.0); } foreach (var hGraphEdgeHashDictionaryEntry in hGraphEdgeHashDictionary) { // Wenn es sich um eine Kante mit negativen Kosten handelt var hEdgeCost = hGraphEdgeHashDictionaryEntry.Value.GetWeightValue <CostWeighted>(); if (hEdgeCost < 0.0) { var hDirectedEdge = hGraphEdgeHashDictionaryEntry.Value as DirectedEdge; var hTargetNodeId = hDirectedEdge.GetPossibleEnpoints()[0].Id; var hSourceNodeId = hDirectedEdge.GetEdgeSource().Id; // Balance des Ursprungs-Knoten (bzgl. Kante) anpassen hPseudoBalanceDictionary[hSourceNodeId] += hFlussGraphDictionary[hGraphEdgeHashDictionaryEntry.Key]; // Balance des Ziel-Knotens (der Kante) anpassen hPseudoBalanceDictionary[hTargetNodeId] -= hFlussGraphDictionary[hGraphEdgeHashDictionaryEntry.Key]; } } // Ermitteln der Pseudo-Quellen und Pseudo-Senken var hPseudoQuellen = new HashSet <int>(); var hPseudoSenken = new HashSet <int>(); foreach (var hNode in hNodeDictionary.Values) { if (!(hNode is BalancedNode)) { throw new InvalidDataException("Nur Balanced Nodes"); } var hBalancedNode = hNode as BalancedNode; var hNodeBalance = hBalancedNode.Balance; var hPseudoBalance = hPseudoBalanceDictionary[hNode.Id]; if (hNodeBalance > hPseudoBalance) { hPseudoQuellen.Add(hNode.Id); } else if (hNodeBalance < hPseudoBalance) { hPseudoSenken.Add(hNode.Id); } } // Solange es noch Pseudoquellen gibt while (hPseudoQuellen.Count > 0) { // Residualgraphen erstellen var hResidualGraph = ResidualGraphGenerator.GenerateCostCapacity(FUsedGraph, hFlussGraphDictionary); var hResidualGraphEdgeHashDictionary = hResidualGraph.GenerateEdgeHashDictionary(); // Eine Pseudoquelle auswählen (z.B. die erste). var hSelectedPseudoQuellenId = hPseudoQuellen.First(); // Versuchen eine erreichbare Senke zu finden (Kürzeste Weg) var hFoundPath = false; foreach (var hCurrentPseudoSenkenId in hPseudoSenken) { // ToDo unnötig. Einmal reicht var hBellmanFordAlgorithm = new BellmanFordAlgorithm(hResidualGraph); hBellmanFordAlgorithm.Execute(hSelectedPseudoQuellenId, hCurrentPseudoSenkenId); if (hBellmanFordAlgorithm.FTargetFound) { hFoundPath = true; // Minimum ermitteln (Pseudo-Quelle, Minimale Kantenkapazität, Pseudo-Senke) // - Pseudo-Quelle var hSelectedPseudoQuelle = hNodeDictionary[hSelectedPseudoQuellenId] as BalancedNode; var hMinPseudoQuelle = hSelectedPseudoQuelle.Balance - hPseudoBalanceDictionary[hSelectedPseudoQuellenId]; // - Pseudo-Senke var hCurrentPseudoSenke = hNodeDictionary[hCurrentPseudoSenkenId] as BalancedNode; var hMinPseudoSenke = hPseudoBalanceDictionary[hCurrentPseudoSenkenId] - hCurrentPseudoSenke.Balance; // - Pfad var hMinCapacityInPath = double.PositiveInfinity; foreach (var hPathEdgeHash in hBellmanFordAlgorithm.FShortestPathToTarget) { var hEdgeCapacity = hResidualGraphEdgeHashDictionary[hPathEdgeHash].GetWeightValue <CapacityWeighted>(); if (hEdgeCapacity < hMinCapacityInPath) { hMinCapacityInPath = hEdgeCapacity; } } // Ermitteln was das Minimum von den drei ist var hAugmentationValue = Math.Min(hMinCapacityInPath, Math.Min(hMinPseudoQuelle, hMinPseudoSenke)); // Fluss anpassen foreach (var hPathEdgeHash in hBellmanFordAlgorithm.FShortestPathToTarget) { if (ResidualGraphGenerator.HinkantenEdgeHashes.Contains(hPathEdgeHash)) { // Hinkante // Diese Kante kann ich im Flussgraphen Dictionary direkt ansprechen da die Konten im Hash in der richtigen Reihenfolge stehen hFlussGraphDictionary[hPathEdgeHash] += hAugmentationValue; } else if (ResidualGraphGenerator.RueckKantenEdgeHashes.Contains(hPathEdgeHash)) { // Bei der Breitensuche im Augmentieren Graphen wurde eine Rückkante verwendet. // Diese Rückkante existiert so im originalen Graphen nicht weshalb auch der Hash-Value nicht passt. // Dieser ist lediglich verdreht. Also wird der HashWert vorher noch gespiegelt um die ursprüngliche Kante zu identifizieren var hHashValueOfOriginalEdge = HelpFunctions.InvertEdgeHash(hPathEdgeHash); hFlussGraphDictionary[hHashValueOfOriginalEdge] -= hAugmentationValue; } } // PseudoBalance anpassen hPseudoBalanceDictionary[hSelectedPseudoQuellenId] += hAugmentationValue; hPseudoBalanceDictionary[hCurrentPseudoSenkenId] -= hAugmentationValue; // ggf. Pseudo-Quelle oder Pseudo-Senke entfernen // ToDo Nur die betrachten, welche auch in der Pfad-Detektion verwendet wurde hPseudoQuellen.RemoveWhere( _PseudoQuellenId => { var hPseudoQuelle = hNodeDictionary[_PseudoQuellenId] as BalancedNode; if ((hPseudoBalanceDictionary[_PseudoQuellenId] - hPseudoQuelle.Balance) == 0) { return(true); } return(false); } ); hPseudoSenken.RemoveWhere( _PseudoSenkenId => { var hPseudoSenke = hNodeDictionary[_PseudoSenkenId] as BalancedNode; if ((hPseudoBalanceDictionary[_PseudoSenkenId] - hPseudoSenke.Balance) == 0) { return(true); } return(false); }); // Es wurde eine Senke gefunden. Also kann eine neue Pseudo-Quelle ausgewählt werden break; } } if (!hFoundPath) { // Wird keine erreichbare Senke gefunden so kann der Algortihmus aufhören. Es gibt keinen B-Fluss Console.WriteLine("Von der Pseudo-Quelle " + hSelectedPseudoQuellenId + " aus konnte keine Pseudo-Senke erreicht werden."); return; } } // Jetzt sollte es keine Pseudo-Quellen mehr geben. // Ausgabe des Kostenminimalen Flusses var hFlowCost = 0.0; foreach (var hEdgeInFlow in hFlussGraphDictionary) { var hEdge = hGraphEdgeHashDictionary[hEdgeInFlow.Key]; hFlowCost += hEdge.GetWeightValue <CostWeighted>() * hEdgeInFlow.Value; } Console.WriteLine("Kostenminimaler Fluss:\t" + hFlowCost); }