コード例 #1
0
        /// <summary>
        /// Wyznacza ścieżki składające się z najmniejszej liczby krawędzi
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="s">Wierzchołek źródłowy</param>
        /// <param name="d">Znalezione najkrótsze ścieżki (parametr wyjściowy)</param>
        /// <returns>true</returns>
        /// <remarks>
        /// Elementy tablicy d zawierają odległości od źródła do wierzchołka określonego przez indeks elementu.<para/>
        /// Jeśli ścieżka od źródła do danego wierzchołka nie istnieje, to odległość ma wartość NaN. <para/>
        /// Metoda zawsze zwraca true (można ją stosować do każdego grafu).
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool BFPaths(this ASD.Graphs.Graph g, int s, out PathsInfo[] d)
        {
            var dTemp = new PathsInfo[g.VerticesCount];

            for (var i = 0; i < g.VerticesCount; i++)
            {
                dTemp[i].Dist = double.NaN;
            }
            dTemp[s].Dist = 0.0;

            bool VisitEdge(Edge edge)
            {
                if (!dTemp[edge.To].Dist.IsNaN())
                {
                    return(true);
                }
                dTemp[edge.To].Dist = dTemp[edge.From].Dist + 1.0;
                dTemp[edge.To].Last = edge;
                return(true);
            }

            g.GeneralSearchFrom <EdgesQueue>(s, null, null, VisitEdge);
            d = dTemp;
            return(true);
        }
コード例 #2
0
        /// <summary>
        /// Wyznacza najkrótsze ścieżki algorytmem Floyda-Warshalla
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="d">Znalezione najkrótsze ścieżki (parametr wyjściowy)</param>
        /// <returns>Informacja czy graf nie zawiera cyklu o ujemnej długości</returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Elementy tablicy d zawierają odległości pomiedzy każdą parą wierzchołków grafu.<para/>
        /// Jeśli dla danej pary wierzchołków odpowiednia ścieżka nie istnieje, to odległość ma wartość NaN.<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Jeśli badany graf zawiera cykl o ujemnej długości, to metoda zwraca false.
        /// Parametr d jest wówczas równy null.
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool FloydWarshallShortestPaths(this ASD.Graphs.Graph g, out PathsInfo[,] d)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }

            d = new PathsInfo[g.VerticesCount, g.VerticesCount];
            for (var i = 0; i < g.VerticesCount; i++)
            {
                for (var j = 0; j < g.VerticesCount; j++)
                {
                    d[i, j].Dist = double.NaN;
                }

                foreach (var edge in g.OutEdges(i))
                {
                    d[i, edge.To].Dist = edge.Weight;
                    d[i, edge.To].Last = edge;
                }

                if (d[i, i].Dist.IsNaN() || d[i, i].Dist >= 0.0)
                {
                    d[i, i].Dist = 0.0;
                    d[i, i].Last = null;
                }

                if (!(d[i, i].Dist < 0.0))
                {
                    continue;
                }
                d = null;
                return(false);
            }

            for (var k = 0; k < g.VerticesCount; k++)
            {
                for (var i = 0; i < g.VerticesCount; i++)
                {
                    for (var j = 0; j < g.VerticesCount; j++)
                    {
                        if (!d[i, j].Dist.IsNaN() && !(d[i, j].Dist > d[i, k].Dist + d[k, j].Dist))
                        {
                            continue;
                        }
                        d[i, j].Dist = d[i, k].Dist + d[k, j].Dist;
                        d[i, j].Last = d[k, j].Last;
                        if (i != j || d[i, j].Dist >= 0.0)
                        {
                            continue;
                        }
                        d = null;
                        return(false);
                    }
                }
            }
            return(true);
        }
コード例 #3
0
        /// <summary>
        /// Wyznacza najkrótsze ścieżki algorytmem Johnsona
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="d">Znalezione najkrótsze ścieżki (parametr wyjściowy)</param>
        /// <returns>Informacja czy graf nie zawiera cyklu o ujemnej długości</returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Elementy tablicy d zawierają odległości pomiedzy każdą parą wierzchołków grafu.<para/>
        /// Jeśli dla danej pary wierzchołków odpowiednia ścieżka nie istnieje, to odległość ma wartość NaN.<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Jeśli badany graf zawiera cykl o ujemnej długości, to metoda zwraca false.
        /// Parametr d jest wówczas równy null.<para/>
        /// Metoda wykonuje obliczenia równolegle w wielu wątkach.
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool JohnsonShortestPathsParallel(this ASD.Graphs.Graph g, out PathsInfo[,] d)
        {
            d = null;
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }

            var graph = g.IsolatedVerticesGraph(true, g.VerticesCount + 1);

            for (var i = 0; i < g.VerticesCount; i++)
            {
                graph.AddEdge(g.VerticesCount, i, 0.0);
                foreach (var edge in g.OutEdges(i))
                {
                    graph.AddEdge(edge);
                }
            }

            if (!FordBellmanShortestPathsParallel(graph, g.VerticesCount, out var dFordBellman))
            {
                return(false);
            }

            graph = g.IsolatedVerticesGraph();

            for (var i = 0; i < g.VerticesCount; i++)
            {
                foreach (var edge in g.OutEdges(i))
                {
                    graph.AddEdge(edge.From, edge.To,
                                  edge.Weight + dFordBellman[edge.From].Dist - dFordBellman[edge.To].Dist);
                }
            }

            var dTemp = new PathsInfo[g.VerticesCount, g.VerticesCount];

            void Action(int i)
            {
                g.DijkstraShortestPaths(i, out var dDijkstra);
                for (var j = 0; j < g.VerticesCount; j++)
                {
                    dTemp[i, j].Dist = dDijkstra[j].Dist + dFordBellman[j].Dist - dFordBellman[i].Dist;
                    dTemp[i, j].Last = dDijkstra[j].Last;
                }
            }

            Parallel.For(0, g.VerticesCount, Action);

            d = dTemp;
            return(true);
        }
コード例 #4
0
        /// <summary>
        /// Znajduje cykl o ujemnej długości (wadze)
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="d">Informacje o najkrótszych ścieżkach</param>
        /// <returns>
        /// Krotka (weight, cycle) składająca się z długości (sumy wag krawędzi)
        /// znalezionego cyklu i tablicy krawędzi tworzących ten cykl)
        /// </returns>
        /// <remarks>
        /// Elementy tablicy d powinny zawierać odległości wyznaczone algorytmem Forda-Bellmana,
        /// który zatrzymał się z wynikiem false.<para/>
        /// Jeśli analiza tablicy d nie wykryła cyklu o ujemnej długości metoda zwraca (0,null).<para/>
        /// Nie oznacza to, że w grafie nie ma żadnego cyklu o ujemnej dlugości, a jedynie że nie ma takiego cyklu,
        /// który zakłóciłby działanie uruchomionego wcześciej algorytmu Forda-Bellmana (dla danego źródła).<para/>
        /// Elementy (krawędzie) umieszczone są w zwracanej tablicy w kolejności swojego następstwa w znalezionym cyklu.
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static (double weight, Edge[] cycle) FindNegativeCostCycle(this ASD.Graphs.Graph g, PathsInfo[] d)
        {
            Edge?edge = null;
            var  vert = 0;

            while (edge == null && vert < g.VerticesCount)
            {
                if (!d[vert].Dist.IsNaN())
                {
                    foreach (var e in g.OutEdges(vert))
                    {
                        if (!d[e.To].Dist.IsNaN() && !(d[e.To].Dist > d[vert].Dist + e.Weight))
                        {
                            continue;
                        }
                        edge = e;
                        break;
                    }
                }
                vert++;
            }
            if (edge == null)
            {
                return(0.0, null);
            }

            var hashSet = new HashSet <int> {
                edge.Value.To
            };
            var from = d[edge.Value.To].Last.Value.From;

            while (!hashSet.Contains(from))
            {
                hashSet.Add(from);
                from = d[from].Last.Value.From;
            }
            var start      = from;
            var weight     = 0.0;
            var edgesStack = new EdgesStack();

            do
            {
                edge = d[from].Last;
                edgesStack.Put(edge.Value);
                weight += edge.Value.Weight;
                from    = edge.Value.From;
            }while (from != start);
            return(weight, edgesStack.ToArray());
        }
コード例 #5
0
        /// <summary>
        /// Wyznacza najkrótsze ścieżki algorytmem Dijkstry
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="s">Wierzchołek źródłowy</param>
        /// <param name="d">Znalezione najkrótsze ścieżki (parametr wyjściowy)</param>
        /// <returns>Informacja czy graf spełnia założenia algorytmu Dijkstry</returns>
        /// <remarks>
        /// Elementy tablicy d zawierają odległości od źródła do wierzchołka określonego przez indeks elementu.<para/>
        /// Jeśli ścieżka od źródła do danego wierzchołka nie istnieje, to odległość ma wartość NaN.<para/>
        /// Jeśli badany graf nie spełnia założeń algorytmu Dijkstry, to metoda zwraca false.
        /// Parametr d jest wówczas równy null.<para/>
        /// Założenia badane są jedynie częściowo, w zakresie mogącym wpłynąć na działanie algorytmu
        /// (na przykład dla grafu niespójnego krawędzie o ujemnych wagach w innej składowej spójnej
        /// niż źródło nie zostaną wykryte - metoda zwróci true).<para/>
        /// Zaimplementowana jest wersja alorytmu Dijkstry korzystająca z kolejki priorytetowej.
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool DijkstraShortestPaths(this ASD.Graphs.Graph g, int s, out PathsInfo[] d)
        {
            bool Cmp(KeyValuePair <int, double> kvp1, KeyValuePair <int, double> kvp2)
            {
                return(kvp1.Value != kvp2.Value ? kvp1.Value < kvp2.Value : kvp1.Key < kvp2.Key);
            }

            var priorityQueue = new PriorityQueue <int, double>(Cmp, CMonDoSomething.Nothing);

            d = new PathsInfo[g.VerticesCount];
            for (var i = 0; i < g.VerticesCount; i++)
            {
                d[i].Dist = double.NaN;
            }
            d[s].Dist = 0.0;

            for (var j = 0; j < g.VerticesCount; j++)
            {
                priorityQueue.Put(j, (j != s) ? double.PositiveInfinity : 0.0);
            }

            while (!priorityQueue.Empty)
            {
                var vert = priorityQueue.Get();
                if (d[vert].Dist.IsNaN())
                {
                    return(true);
                }
                foreach (var edge in g.OutEdges(vert))
                {
                    if (edge.Weight < 0.0)
                    {
                        d = null;
                        return(false);
                    }

                    if (!priorityQueue.Contains(edge.To) ||
                        !priorityQueue.ImprovePriority(edge.To, d[vert].Dist + edge.Weight))
                    {
                        continue;
                    }
                    d[edge.To].Dist = d[vert].Dist + edge.Weight;
                    d[edge.To].Last = edge;
                }
            }
            return(true);
        }
コード例 #6
0
        /// <summary>
        /// Wyznacza najkrótsze ścieżki algorytmem Forda-Bellmana
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="s">Wierzchołek źródłowy</param>
        /// <param name="d">Znalezione najkrótsze ścieżki (parametr wyjściowy)</param>
        /// <returns>Informacja czy graf spełnia założenia algorytmu Forda-Bellmana</returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Elementy tablicy d zawierają odległości od źródła do wierzchołka określonego przez indeks elementu.<para/>
        /// Jeśli ścieżka od źródła do danego wierzchołka nie istnieje, to odległość ma wartość NaN.<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Jeśli badany graf nie spełnia założeń algorytmu Forda-Bellmana, to metoda zwraca false.
        /// Parametr d zawiera wówczas informacje umożliwiające wyznaczenie cyklu o ujemnej długości.<para/>
        /// Założenia badane są jedynie częściowo, w zakresie mogącym wpłynąć na działanie algorytmu
        /// (na przykład dla grafu niespójnego cykle o ujemnej długości w innej składowej spójnej
        /// niż źródło nie zostaną wykryte - metoda zwróci true).
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool FordBellmanShortestPaths(this ASD.Graphs.Graph g, int s, out PathsInfo[] d)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }

            var array = new bool[g.VerticesCount];

            d = new PathsInfo[g.VerticesCount];
            for (var i = 0; i < g.VerticesCount; i++)
            {
                d[i].Dist = double.NaN;
            }
            d[s].Dist = 0.0;

            array[s] = true;
            var rotations = 0;
            var change    = g.VerticesCount > 1;

            while (change && rotations++ != g.VerticesCount)
            {
                change = false;
                for (var j = 0; j < g.VerticesCount; j++)
                {
                    if (!array[j])
                    {
                        continue;
                    }
                    array[j] = false;
                    foreach (var edge in g.OutEdges(j))
                    {
                        if (!d[edge.To].Dist.IsNaN() && d[edge.To].Dist <= d[edge.From].Dist + edge.Weight)
                        {
                            continue;
                        }

                        change          = true;
                        array[edge.To]  = true;
                        d[edge.To].Dist = d[edge.From].Dist + edge.Weight;
                        d[edge.To].Last = edge;
                    }
                }
            }
            return(!change);
        }
コード例 #7
0
        /// <summary>
        /// Wyznacza najkrótsze ścieżki w grafie skierowanym bez cykli (DAG)
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="s">Wierzchołek źródłowy</param>
        /// <param name="d">Znalezione najkrótsze ścieżki (parametr wyjściowy)</param>
        /// <returns>Informacja czy graf spełnia założenia algorytmu</returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Elementy tablicy d zawierają odległości od źródła do wierzchołka określonego przez indeks elementu.<para/>
        /// Jeśli ścieżka od źródła do danego wierzchołka nie istnieje, to odległość ma wartość NaN.<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Jeśli badany graf jest skierowany, ale zawiera cykl, to metoda zwraca false.
        /// Parametr d jest wówczas równy null.<para/>
        /// Wykrywane są jedynie cykle osiągalne ze źródła (tylko takie mogą wpłynąć na działanie algorytmu),
        /// cykle nieosiągalne nie zostaną wykryte - metoda zwróci true.
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool DAGShortestPaths(this ASD.Graphs.Graph g, int s, out PathsInfo[] d)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }

            if (!g.TopologicalSort(out var original2topological, out var topological2original))
            {
                d = null;
                return(false);
            }
            d = new PathsInfo[g.VerticesCount];
            for (var i = 0; i < g.VerticesCount; i++)
            {
                d[i].Dist = double.NaN;
            }
            d[s].Dist = 0.0;

            for (var j = original2topological[s]; j < g.VerticesCount; j++)
            {
                if (d[topological2original[j]].Dist.IsNaN())
                {
                    continue;
                }
                foreach (var edge in g.OutEdges(topological2original[j]))
                {
                    if (!d[edge.To].Dist.IsNaN() && !(d[edge.To].Dist > d[edge.From].Dist + edge.Weight))
                    {
                        continue;
                    }
                    d[edge.To].Dist = d[edge.From].Dist + edge.Weight;
                    d[edge.To].Last = edge;
                }
            }
            return(true);
        }
コード例 #8
0
        /// <summary>
        /// Wyznacza najkrótsze ścieżki algorytmem Floyda-Warshalla
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="d">Znalezione najkrótsze ścieżki (parametr wyjściowy)</param>
        /// <returns>Informacja czy graf nie zawiera cyklu o ujemnej długości</returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Elementy tablicy d zawierają odległości pomiedzy każdą parą wierzchołków grafu.<para/>
        /// Jeśli dla danej pary wierzchołków odpowiednia ścieżka nie istnieje, to odległość ma wartość NaN.<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Jeśli badany graf zawiera cykl o ujemnej długości, to metoda zwraca false.
        /// Parametr d jest wówczas równy null.<para/>
        /// Metoda wykonuje obliczenia równolegle w wielu wątkach.
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool FloydWarshallShortestPathsParallel(this ASD.Graphs.Graph g, out PathsInfo[,] d)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }

            var dTemp = new PathsInfo[g.VerticesCount, g.VerticesCount];

            void Init(int i, ParallelLoopState pls)
            {
                for (var j = 0; j < g.VerticesCount; j++)
                {
                    dTemp[i, j].Dist = double.NaN;
                }

                foreach (var edge in g.OutEdges(i))
                {
                    dTemp[i, edge.To].Dist = edge.Weight;
                    dTemp[i, edge.To].Last = edge;
                }
                if (dTemp[i, i].Dist.IsNaN() || dTemp[i, i].Dist >= 0.0)
                {
                    dTemp[i, i].Dist = 0.0;
                    dTemp[i, i].Last = null;
                }
                if (dTemp[i, i].Dist < 0.0)
                {
                    pls.Stop();
                }
            }

            var parallelLoopResult = Parallel.For(0, g.VerticesCount, Init);
            var k = 0;

            void Action(int i, ParallelLoopState pls)
            {
                for (var j = 0; j < g.VerticesCount; j++)
                {
                    if (!dTemp[i, j].Dist.IsNaN() && !(dTemp[i, j].Dist > dTemp[i, k].Dist + dTemp[k, j].Dist))
                    {
                        continue;
                    }
                    dTemp[i, j].Dist = dTemp[i, k].Dist + dTemp[k, j].Dist;
                    dTemp[i, j].Last = dTemp[k, j].Last;
                    if (i == j && dTemp[i, j].Dist < 0.0)
                    {
                        pls.Stop();
                    }
                }
            }

            while (k < g.VerticesCount)
            {
                if (!parallelLoopResult.IsCompleted)
                {
                    break;
                }
                parallelLoopResult = Parallel.For(0, g.VerticesCount, Action);
                k++;
            }
            d = parallelLoopResult.IsCompleted ? dTemp : null;
            return(parallelLoopResult.IsCompleted);
        }
コード例 #9
0
        /// <summary>
        /// Wyznacza najkrótsze ścieżki algorytmem Forda-Bellmana
        /// </summary>
        /// <param name="g">Badany graf</param>
        /// <param name="s">Wierzchołek źródłowy</param>
        /// <param name="d">Znalezione najkrótsze ścieżki (parametr wyjściowy)</param>
        /// <returns>Informacja czy graf spełnia założenia algorytmu Forda-Bellmana</returns>
        /// <exception cref="ArgumentException">Gdy uruchomiona dla grafu nieskierowanego</exception>
        /// <remarks>
        /// Elementy tablicy d zawierają odległości od źródła do wierzchołka określonego przez indeks elementu.<para/>
        /// Jeśli ścieżka od źródła do danego wierzchołka nie istnieje, to odległość ma wartość NaN.<para/>
        /// Metoda uruchomiona dla grafu nieskierowanego zgłasza wyjątek <see cref="ArgumentException"/>.<para/>
        /// Jeśli badany graf nie spełnia założeń algorytmu Forda-Bellmana, to metoda zwraca false.
        /// Parametr d zawiera wówczas informacje umożliwiające wyznaczenie cyklu o ujemnej długości.<para/>
        /// Założenia badane są jedynie częściowo, w zakresie mogącym wpłynąć na działanie algorytmu
        /// (na przykład dla grafu niespójnego cykle o ujemnej długości w innej składowej spójnej
        /// niż źródło nie zostaną wykryte - metoda zwróci true).<para/>
        /// Metoda wykonuje obliczenia równolegle w wielu wątkach.
        /// </remarks>
        /// <seealso cref="ShortestPathsGraphExtender"/>
        /// <seealso cref="ASD.Graphs"/>
        public static bool FordBellmanShortestPathsParallel(this ASD.Graphs.Graph g, int s, out PathsInfo[] d)
        {
            if (!g.Directed)
            {
                throw new ArgumentException("Undirected graphs are not allowed");
            }

            var change = g.VerticesCount > 1;

            var array1 = new bool[g.VerticesCount];
            var array2 = new bool[g.VerticesCount];
            var mutex  = new object[g.VerticesCount];

            var dTemp = new PathsInfo[g.VerticesCount];

            for (var i = 0; i < g.VerticesCount; i++)
            {
                dTemp[i].Dist = double.NaN;
            }
            dTemp[s].Dist = 0.0;

            array2[s] = true;

            var rotations = 0;

            for (var i = 0; i < g.VerticesCount; i++)
            {
                mutex[i] = new object();
            }

            void Action(int vert)
            {
                if (!array2[vert])
                {
                    return;
                }
                array2[vert] = false;
                foreach (var edge in g.OutEdges(vert))
                {
                    if (!dTemp[edge.To].Dist.IsNaN() && !(dTemp[edge.To].Dist > dTemp[edge.From].Dist + edge.Weight))
                    {
                        continue;
                    }
                    lock (mutex[edge.To])
                    {
                        if (!dTemp[edge.To].Dist.IsNaN() && !(dTemp[edge.To].Dist > dTemp[edge.From].Dist + edge.Weight))
                        {
                            continue;
                        }
                        change              = true;
                        array1[edge.To]     = true;
                        dTemp[edge.To].Dist = dTemp[vert].Dist + edge.Weight;
                        dTemp[edge.To].Last = edge;
                    }
                }
            }

            while (change && rotations++ != g.VerticesCount)
            {
                change = false;
                Parallel.For(0, g.VerticesCount, Action);
                var temp = array2;
                array2 = array1;
                array1 = temp;
            }
            d = dTemp;
            return(!change);
        }