diff --git a/Task02/GeneralResources.cs b/Task02/GeneralResources.cs new file mode 100644 index 0000000..d6b9492 --- /dev/null +++ b/Task02/GeneralResources.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; + +namespace Task02 +{ + public class Graph + { + public const int INF = 1000000000; + public int[,] graphMatrix; + public (int, int, int)[] graphListEdges; + public int graphAmountVertexes; + static int NumberOfConventionalUnitsInTotal = 1000000; + + public Graph(int amountVertexes, int approximateAmountEdges, int minWeight, int maxWeight) + { + graphAmountVertexes = amountVertexes; + graphMatrix = new int[amountVertexes, amountVertexes]; + Random random = new Random(); + int partAmountEdgesFromMax = (int)((long)approximateAmountEdges * NumberOfConventionalUnitsInTotal / + (amountVertexes * (amountVertexes - 1) / 2)); + int exectAmountEdges = 0; + for (int i = 0; i < amountVertexes; i++) + { + for (int j = i + 1; j < amountVertexes; j++) + { + if (random.Next(NumberOfConventionalUnitsInTotal) < partAmountEdgesFromMax) + { + int weight = random.Next(minWeight, maxWeight); + graphMatrix[i, j] = graphMatrix[j, i] = weight; + exectAmountEdges++; + } + else + { + graphMatrix[i, j] = graphMatrix[j, i] = INF; + } + } + } + + graphListEdges = new (int, int, int)[exectAmountEdges]; + int k = 0; + for (int i = 0; i < amountVertexes; i++) + { + for (int j = i + 1; j < amountVertexes; j++) + { + if (graphMatrix[i, j] != INF) + { + graphListEdges[k] = (graphMatrix[i, j], i, j); + k++; + } + } + } + + StreamWriter foutMatrix = new StreamWriter("matrix.txt"); + foutMatrix.Write($"{amountVertexes} {exectAmountEdges}\n"); + for (k = 0; k < exectAmountEdges; k++) + { + foutMatrix.Write($"{graphListEdges[k].Item2 + 1} " + + $"{graphListEdges[k].Item3 + 1} " + + $"{graphListEdges[k].Item1}\n"); + } + foutMatrix.Close(); + } + } +} diff --git a/Task02/ParallelAlgoFloyd.cs b/Task02/ParallelAlgoFloyd.cs new file mode 100644 index 0000000..c7c74a4 --- /dev/null +++ b/Task02/ParallelAlgoFloyd.cs @@ -0,0 +1,54 @@ +using System; +using System.Threading.Tasks; + +namespace Task02 +{ + public class ParallelAlgoFloyd + { + private static int[,] dist; + + static void parallelProc(int n, int k, int i) + { + for (int j = 0; j < n; j++) + { + if (j != k) + { + if (dist[i, j] > dist[i, k] + dist[k, j]) + dist[i, j] = dist[i, k] + dist[k, j]; + } + } + } + + public static int[,] Execute(Graph graph) + { + int n = graph.graphAmountVertexes; + dist = new int[n, n]; + Task[] tasks = new Task[n - 1]; + Array.Copy(graph.graphMatrix, dist, n * n); + + for (int k = 0; k < n; k++) + { + for (int i = 0; i < n; i++) + { + if (i != k) + { + int newK = k; + int newI = i; + // передаём newK и newI для того, чтобы избежать замыкания + tasks[i < k ? i : i - 1] = Task.Run(() => parallelProc(n, newK, newI)); + // так как i из 0..(n-1), но одно из значений i не используется (блокируется + // условием i != k), то для единообразия обработки Task tasks.Length == n - 1, + // и выражение (i < k ? i : i - 1) задаёт биекцию из [0; k-1] U [k+1; n-1] в + // [0; n-2] + } + } + // ждём выполнения всех задач для того, чтобы случайно не получилось так, что + // задача с большим k не обогнала задачу с меньшим k (и чтобы нам всегда хватило + // ровно n - 1 задачи) + Task.WaitAll(tasks); + } + + return dist; + } + } +} diff --git a/Task02/ParallelAlgoKruskal.cs b/Task02/ParallelAlgoKruskal.cs new file mode 100644 index 0000000..4feee45 --- /dev/null +++ b/Task02/ParallelAlgoKruskal.cs @@ -0,0 +1,86 @@ +using System; +using System.Threading; + +namespace Task02 +{ + public class ParallelAlgoKruskal + { + static Random rand = new Random(); + static int[] dsu; + static int parallelDepth = 3; // глубина рекурсии, до которой происходит распараллеливание сортировки рёбер + + static int getDsu(int x) + { + return dsu[x] == -1 ? x : dsu[x] = getDsu(dsu[x]); + } + + static bool unionDsu(int x, int y) + { + x = getDsu(x); + y = getDsu(y); + if (x == y) + return false; + if (rand.Next(0, 1) == 1) // псевдорандом + dsu[y] = x; + else + dsu[x] = y; + return true; + } + + static void parallelSort(T[] edges, T[] buffer, int LIndex, int RIndex, int parallelDepth) where T : IComparable + { + // parallelSort до глубины parallelDepth реализуется как параллельная MergeSort, а глубже - встроенную сортировку + if (parallelDepth <= 0) + { + Array.Sort(edges, LIndex, RIndex - LIndex); + return; + } + if (RIndex - LIndex <= 1) + return; + int MIndex = (LIndex + RIndex) / 2; + + // чтобы поток просто так не стоял, он берёт на себя сортировку половины массива, а другую отдаёт новому потоку + Thread helperThread = new Thread(() => parallelSort(edges, buffer, LIndex, MIndex, parallelDepth - 1)); + helperThread.Start(); + parallelSort(edges, buffer, MIndex, RIndex, parallelDepth - 1); + helperThread.Join(); + + // объединение двух отсортированных массивов через buffer + int i = LIndex, j = MIndex; + for (int k = LIndex; k < RIndex; k++) + { + if (i == MIndex || (j != RIndex && edges[i].CompareTo(edges[j]) >= 0)) + { + buffer[k] = edges[j]; + j++; + } + else + { + buffer[k] = edges[i]; + i++; + } + } + for (int k = LIndex; k < RIndex; k++) + edges[k] = buffer[k]; + } + + public static int Execute(Graph graph) + { + int ans = 0; + (int, int, int)[] edges = graph.graphListEdges; + parallelSort(edges, new (int, int, int)[edges.Length], 0, edges.Length, parallelDepth); + + dsu = new int[graph.graphAmountVertexes]; + Array.Fill(dsu, -1); + + for (int i = 0; i < edges.Length; i++) + { + if (unionDsu(edges[i].Item2, edges[i].Item3)) + { + ans += edges[i].Item1; + } + } + return ans; + } + } +} diff --git a/Task02/ParallelAlgoPrim.cs b/Task02/ParallelAlgoPrim.cs new file mode 100644 index 0000000..9734ae9 --- /dev/null +++ b/Task02/ParallelAlgoPrim.cs @@ -0,0 +1,91 @@ +using System; +using System.Threading; +using System.Collections.Generic; + +namespace Task02 +{ + public class ParallelAlgoPrim + { + static int minDistVertex; // вершина, не лежащая в дереве, расстояние до которой от дерева минимально + static int newMinDistVertex; + static int amountThreads = 6; + static int runningThreads; // количество выполняющихся потоков + static int chunkSize; + static Mutex mutRunningThreads = new Mutex(); // блокировка при изменении runningThreads + static Mutex mutNewV = new Mutex(); // блокировка при чтении и изменении newV + static HashSet tree; // вершины, лежащие в остовном дереве + static int[] minDistToTree; + + static void FindNewV(Graph graph, int LIndex, int RIndex) + { + // нахождение очередной вершины с минимальным расстоянием до дерева + int localMinDistVertex = -1; // локальный ответ + + for (int to = LIndex; to < RIndex; to++) + { + if (!tree.Contains(to)) + { + // обновление минимального расстояния после добавления v в дерево + if (minDistToTree[to] > graph.graphMatrix[minDistVertex, to]) + { + minDistToTree[to] = graph.graphMatrix[minDistVertex, to]; + } + + // обновление локального ответа + if (localMinDistVertex == -1 || minDistToTree[localMinDistVertex] > minDistToTree[to]) + localMinDistVertex = to; + } + } + + if (localMinDistVertex != -1) + { + mutNewV.WaitOne(); + // обновление глобального ответа + if (newMinDistVertex == -1 || minDistToTree[newMinDistVertex] > minDistToTree[localMinDistVertex]) + newMinDistVertex = localMinDistVertex; + mutNewV.ReleaseMutex(); + } + + mutRunningThreads.WaitOne(); + runningThreads--; + mutRunningThreads.ReleaseMutex(); + } + + public static int Execute(Graph graph) + { + int ans = 0; + tree = new HashSet(); + minDistToTree = new int[graph.graphAmountVertexes]; + Array.Fill(minDistToTree, Graph.INF); + chunkSize = graph.graphAmountVertexes / amountThreads; + + // начинаем с вершины 0 + minDistToTree[0] = 0; + minDistVertex = 0; + + while (minDistVertex != -1) + { + ans += minDistToTree[minDistVertex]; + tree.Add(minDistVertex); + + newMinDistVertex = -1; + + runningThreads = amountThreads; + for (int i = 0; i < amountThreads - 1; i++) + { + int LIndex = chunkSize * i; + int RIndex = chunkSize * (i + 1); + ThreadPool.QueueUserWorkItem(_ => FindNewV(graph, LIndex, RIndex)); + } + // загрзим главный поток чтобы он просто так не ждал + FindNewV(graph, chunkSize * (amountThreads - 1), graph.graphAmountVertexes); + + while (runningThreads > 0) {} + + minDistVertex = newMinDistVertex; + } + + return ans; + } + } +} diff --git a/Task02/Program.cs b/Task02/Program.cs new file mode 100644 index 0000000..ad6bf93 --- /dev/null +++ b/Task02/Program.cs @@ -0,0 +1,110 @@ +using System; +using System.IO; +using System.Diagnostics; + +namespace Task02 +{ + class Program + { + static void Main() + { + StreamWriter foutFloyd = new StreamWriter("floyd.txt"); + StreamWriter foutKruskal = new StreamWriter("kruskal.txt"); + StreamWriter foutPrim = new StreamWriter("prim.txt"); + + Console.WriteLine("Введите количество вершин нового графа:"); + string inputN = Console.ReadLine(); + int n = int.Parse(inputN); + Console.WriteLine("Введите примерное количество рёбер нового графа:"); + string inputM = Console.ReadLine(); + int m = int.Parse(inputM); + Console.WriteLine("Введите два числа - диапазон весов рёбер нового графа:"); + string[] inputW = Console.ReadLine().Split(" "); + int minWeight = int.Parse(inputW[0]); + int maxWeight = int.Parse(inputW[1]); + + Console.WriteLine("\nСоздание нового графа..."); + Graph graph = new Graph(n, m, minWeight, maxWeight); + Console.WriteLine("Новый граф успешно создан.\n"); + + Stopwatch timeSequentialFloyd = Stopwatch.StartNew(); + int[,] ansSequentialFloyd = SequentialAlgoFloyd.Execute(graph); + timeSequentialFloyd.Stop(); + Console.WriteLine($"Время исполнения последовательного алгоритма Флойда = {timeSequentialFloyd.ElapsedMilliseconds} мс."); + + Stopwatch timeParallelFloyd = Stopwatch.StartNew(); + int[,] ansParallelFloyd = ParallelAlgoFloyd.Execute(graph); + timeParallelFloyd.Stop(); + Console.WriteLine($"Время исполнения параллельного алгоритма Флойда = {timeParallelFloyd.ElapsedMilliseconds} мс."); + + Stopwatch timeSequentialKruskal = Stopwatch.StartNew(); + int ansSequentialKruskal = SequentialAlgoKruskal.Execute(graph); + timeSequentialKruskal.Stop(); + Console.WriteLine($"Время исполнения последовательного алгоритма Краскала = {timeSequentialKruskal.ElapsedMilliseconds} мс."); + + Stopwatch timeParallelKruskal = Stopwatch.StartNew(); + int ansParallelKruskal = ParallelAlgoKruskal.Execute(graph); + timeParallelKruskal.Stop(); + Console.WriteLine($"Время исполнения параллельного алгоритма Краскала = {timeParallelKruskal.ElapsedMilliseconds} мс."); + + Stopwatch timeParallelPrim = Stopwatch.StartNew(); + int ansParallelPrim = ParallelAlgoPrim.Execute(graph); + timeParallelPrim.Stop(); + Console.WriteLine($"Время исполнения параллельного алгоритма Прима = {timeParallelPrim.ElapsedMilliseconds} мс."); + + Console.WriteLine(); + + bool ansEquals = true; + for (int i = 0; i < n; i++) + { + for (int j = 0; j < n; j++) + { + if (ansSequentialFloyd[i, j] != ansParallelFloyd[i, j]) + { + ansEquals = false; + break; + } + } + } + if (ansEquals) + { + Console.WriteLine("OK: Ответ параллельного алгоритма Флойда совпал с последовательным."); + } + else + { + Console.WriteLine("WA: Ответ параллельного алгоритма Флойда не совпал с последовательным."); + } + + if (ansSequentialKruskal == ansParallelKruskal) + { + Console.WriteLine("OK: Ответ параллельного алгоритма Краскала совпал с последовательным."); + } + else + { + Console.WriteLine("WA: Ответ параллельного алгоритма Краскала не совпал с последовательным."); + } + + if (ansSequentialKruskal == ansParallelPrim) + { + Console.WriteLine("OK: Ответ параллельного алгоритма Прима совпал с последовательным алгоритмом Краскала."); + } + else + { + Console.WriteLine("WA: Ответ параллельного алгоритма Прима не совпал с последовательным алгоритмом Краскала."); + } + + for (int i = 0; i < n; i++) + { + for (int j = 0; j < n; j++) + { + foutFloyd.Write($"{ansParallelFloyd[i, j]}{(j + 1 == n ? '\n' : ' ')}"); + } + } + foutKruskal.Write(ansParallelKruskal); + foutPrim.Write(ansParallelPrim); + foutFloyd.Close(); + foutKruskal.Close(); + foutPrim.Close(); + } + } +} diff --git a/Task02/SequentialAlgoFloyd.cs b/Task02/SequentialAlgoFloyd.cs new file mode 100644 index 0000000..2586ae1 --- /dev/null +++ b/Task02/SequentialAlgoFloyd.cs @@ -0,0 +1,27 @@ +using System; + +namespace Task02 +{ + public class SequentialAlgoFloyd + { + public static int[,] Execute(Graph graph) + { + int n = graph.graphAmountVertexes; + int[,] dist = new int[n, n]; + Array.Copy(graph.graphMatrix, dist, n * n); + + for (int k = 0; k < n; k++) + { + for (int i = 0; i < n; i++) + { + for (int j = 0; j < n; j++) + { + if (dist[i, j] > dist[i, k] + dist[k, j]) + dist[i, j] = dist[i, k] + dist[k, j]; + } + } + } + return dist; + } + } +} diff --git a/Task02/SequentialAlgoKruskal.cs b/Task02/SequentialAlgoKruskal.cs new file mode 100644 index 0000000..a371dbc --- /dev/null +++ b/Task02/SequentialAlgoKruskal.cs @@ -0,0 +1,46 @@ +using System; + +namespace Task02 +{ + public class SequentialAlgoKruskal + { + static int[] dsu; + + static int getDsu(int x) + { + return dsu[x] == -1 ? x : dsu[x] = getDsu(dsu[x]); + } + + static bool unionDsu(int x, int y) + { + x = getDsu(x); + y = getDsu(y); + if (x == y) + return false; + if ((x + 2 * y) % 4 * 2 % 6 != 0) // псевдорандом + dsu[y] = x; + else + dsu[x] = y; + return true; + } + + public static int Execute(Graph graph) + { + int ans = 0; + (int, int, int)[] edges = graph.graphListEdges; + Array.Sort(edges); + + dsu = new int[graph.graphAmountVertexes]; + Array.Fill(dsu, -1); + + for (int i = 0; i < edges.Length; i++) + { + if (unionDsu(edges[i].Item2, edges[i].Item3)) + { + ans += edges[i].Item1; + } + } + return ans; + } + } +}