/// <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); }
/// <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); }