Ejemplo n.º 1
0
        public Dictionary <string, double> Execute(int _StartNodeId, int _TargetNodeId)
        {
            //Console.WriteLine("Starte Edmonds Karp Algorithmus");
            var    hStartNode   = FUsedGraph.GetNodeDictionary()[_StartNodeId];
            double hGesamtfluss = 0.0;

            // Initialisierung Flussgraph
            var hUsedGraphEdgeDictionary = FUsedGraph.GenerateEdgeHashDictionary();

            FFlussGraphDictionary = new Dictionary <string, double>();
            foreach (var hEdgeHash in hUsedGraphEdgeDictionary.Keys)
            {
                FFlussGraphDictionary.Add(hEdgeHash, 0.0);
            }

            // Erstelle den Residualgraph

            var hResidualGraph = ResidualGraphGenerator.Generate(FUsedGraph, FFlussGraphDictionary);

            // Eine Breitensuche auf dem Residualgraph
            var hBfsSearch = new BreadthFirstSearch();
            var hBfsGraph  = hBfsSearch.Execute <CapacityWeighted>(hResidualGraph.GetNodeDictionary()[_StartNodeId]);

            // Solange im Augmentationsnetzwerk noch ein Pfad zum Zielknoten gefunden wird.
            while (hBfsGraph.GetNodeDictionary().ContainsKey(_TargetNodeId))
            {
                // Ermittle im BFS Ergebnis des Augmentationsgraph den Pfad zum Zielknoten und merke den Augmentationswert
                var hBfsGraphParentInfo     = hBfsSearch.GetParentNodeEdge();
                var hCurrentNodeIdInBFSPath = _TargetNodeId;
                var hBfsPathEdgeHashesStack = new Stack <string>();
                var hAugmentationsValue     = double.PositiveInfinity;
                while (hCurrentNodeIdInBFSPath != _StartNodeId)
                {
                    if (hBfsGraphParentInfo[hCurrentNodeIdInBFSPath].Edge.GetWeightValue <CapacityWeighted>() < hAugmentationsValue)
                    {
                        hAugmentationsValue = hBfsGraphParentInfo[hCurrentNodeIdInBFSPath].Edge.GetWeightValue <CapacityWeighted>();
                    }
                    // Merken welche Kanten in der BFS Suche involviert waren. Dies wird als "VonKnotenId-NachKnotenId" String gespeichert. Dies entspricht dem EdgeHash
                    hBfsPathEdgeHashesStack.Push(hBfsGraphParentInfo[hCurrentNodeIdInBFSPath].Node.Id + "-" + hCurrentNodeIdInBFSPath);
                    hCurrentNodeIdInBFSPath = hBfsGraphParentInfo[hCurrentNodeIdInBFSPath].Node.Id; // Ermitteln des Parents
                }

                // Gesamtfluss
                hGesamtfluss += hAugmentationsValue;

                // Bisherigen Flusspfad anpassen
                // Beim zurücklaufen zum Startknoten wurden alle relevanten Kanten besucht und die Hashes der Kanten gespeichert
                foreach (var hBfsPathEdgeHash in hBfsPathEdgeHashesStack)
                {
                    if (ResidualGraphGenerator.HinkantenEdgeHashes.Contains(hBfsPathEdgeHash))
                    {
                        // Bei der Breitensuche im Augmentieren Graphen wurde eine Hinkante verwendet.
                        // Diese Kante kann ich im Flussgraphen Dictionary direkt ansprechen da die Konten im Hash in der richtigen Reihenfolge stehen
                        FFlussGraphDictionary[hBfsPathEdgeHash] += hAugmentationsValue;
                    }
                    else if (ResidualGraphGenerator.RueckKantenEdgeHashes.Contains(hBfsPathEdgeHash))
                    {
                        // 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 = InvertEdgeHash(hBfsPathEdgeHash);
                        FFlussGraphDictionary[hHashValueOfOriginalEdge] -= hAugmentationsValue;
                    }
                }

                // Neuer Residualgraph
                hResidualGraph = ResidualGraphGenerator.Generate(FUsedGraph, FFlussGraphDictionary);

                // Bfs darauf
                hBfsSearch = new BreadthFirstSearch();
                hBfsGraph  = hBfsSearch.Execute <CapacityWeighted>(hResidualGraph.GetNodeDictionary()[_StartNodeId]);
            }

            Dictionary <string, double> hResultFlow = new Dictionary <string, double>();

            //Console.WriteLine("Flussverlauf:");
            foreach (var hFlussInfo in FFlussGraphDictionary)
            {
                if (hFlussInfo.Value > 0.0)
                {
                    //Console.WriteLine(hFlussInfo.Key + "\t" + hFlussInfo.Value);
                    hResultFlow.Add(hFlussInfo.Key, hFlussInfo.Value);
                }
            }
            //Console.WriteLine("Gesamtfluss:\t" + hGesamtfluss);

            return(hResultFlow);
        }
        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);
        }