From 17d5f3321a4b20d98561b0b8e6e5d10d118f0564 Mon Sep 17 00:00:00 2001 From: Safgerd Date: Mon, 18 Nov 2024 05:37:56 +0400 Subject: [PATCH] minhasapov_ruslan_lab_6 --- minhasapov_ruslan_lab_6/README.md | 78 +++++++++++++++++++++++++++++ minhasapov_ruslan_lab_6/main.py | 82 +++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 minhasapov_ruslan_lab_6/README.md create mode 100644 minhasapov_ruslan_lab_6/main.py diff --git a/minhasapov_ruslan_lab_6/README.md b/minhasapov_ruslan_lab_6/README.md new file mode 100644 index 0000000..6addbbc --- /dev/null +++ b/minhasapov_ruslan_lab_6/README.md @@ -0,0 +1,78 @@ +# Лабораторная работа №6 +#### ПИбд-42. Минхасапов Руслан. + +--- + +#### Выполнение +В работе реализованы два алгоритма вычисления определителя матрицы: + +1. **Последовательный:** Рекурсивный алгоритм, основанный на разложении по первой строке. +2. **Параллельный:** Алгоритм, использующий `multiprocessing.Pool` для распределения вычислений между несколькими процессами. Каждый процесс вычисляет часть определителя, суммируя результаты которых получается окончательный определитель. + +Для тестирования алгоритмов использовались квадратные матрицы различных размеров, заполненные случайными целыми числами. Производительность алгоритмов оценивалась по времени выполнения в миллисекундах. + +--- + +#### Результаты бенчмарков: + +Тесты проведены для матриц размером [2, 4, 6, 8, 10, 11, 12] с разным количеством процессов [2, 4, 8]. +Большие размеры не использовались из-за высокой вычислительной сложности рекурсивного алгоритма. Результаты представлены в таблице: + +| Размер | Метод | Кол-во процессов | Время (мс) | Детерминант | +|---|---|---|---|---| +| 2x2 | Последовательный | - | 0.0 | 42 | +| 2x2 | Параллельный | 2 | 128.10182571411133 | 42 | +| 2x2 | Параллельный | 4 | 115.54121971130371 | 42 | +| 2x2 | Параллельный | 8 | 162.1556282043457 | 42 | +| 4x4 | Последовательный | - | 0.0 | -306 | +| 4x4 | Параллельный | 2 | 93.99938583374023 | -306 | +| 4x4 | Параллельный | 4 | 115.57316780090332 | -306 | +| 4x4 | Параллельный | 8 | 163.00582885742188 | -306 | +| 6x6 | Последовательный | - | 0.0 | 10976 | +| 6x6 | Параллельный | 2 | 95.99781036376953 | 10976 | +| 6x6 | Параллельный | 4 | 118.65568161010742 | 10976 | +| 6x6 | Параллельный | 8 | 174.55458641052246 | 10976 | +| 8x8 | Последовательный | - | 31.999588012695312 | 600600 | +| 8x8 | Параллельный | 2 | 110.0003719329834 | 600600 | +| 8x8 | Параллельный | 4 | 140.5773162841797 | 600600 | +| 8x8 | Параллельный | 8 | 207.80611038208008 | 600600 | +| 10x10 | Последовательный | - | 2616.307020187378 | -226825156 | +| 10x10 | Параллельный | 2 | 1449.5408535003662 | -226825156 | +| 10x10 | Параллельный | 4 | 1156.5566062927246 | -226825156 | +| 10x10 | Параллельный | 8 | 1194.1299438476562 | -226825156 | +| 11x11 | Последовательный | - | 27917.847871780396 | 1673573946 | +| 11x11 | Параллельный | 2 | 16098.727703094482 | 1673573946 | +| 11x11 | Параллельный | 4 | 11074.35154914856 | 1673573946 | +| 11x11 | Параллельный | 8 | 10499.537706375122 | 1673573946 | +| 12x12 | Последовательный | - | 342880.4175853729 | -126767598056 | +| 12x12 | Параллельный | 2 | 196262.00246810913 | -126767598056 | +| 12x12 | Параллельный | 4 | 130018.41878890991 | -126767598056 | +| 12x12 | Параллельный | 8 | 119694.18478012085 | -126767598056 | +--- + +## Анализ + +**1. Влияние размера матрицы:** + +Как и ожидалось, время вычисления определителя резко возрастает с увеличением размера матрицы. Это связано с экспоненциальной сложностью рекурсивного алгоритма. Разница особенно заметна при переходе от матриц 10x10 к 11x11 и 12x12, где время вычисления увеличивается на порядок. + +**2. Эффективность параллелизма:** + +* **Малые матрицы (2x2, 4x4, 6x6):** Параллельное вычисление для малых матриц неэффективно. Время выполнения параллельного алгоритма значительно превышает время последовательного вычисления. Это объясняется накладными расходами на создание и управление процессами, которые в данном случае превышают выигрыш от распараллеливания. + +* **Средние матрицы (8x8, 10x10):** Для матриц среднего размера начинает проявляться преимущество параллельного алгоритма. Например, для матрицы 10x10 использование 4 и 8 процессов сокращает время вычисления почти в два раза по сравнению с последовательным алгоритмом. + +* **Большие матрицы (11x11, 12x12):** Для больших матриц эффективность параллелизма становится наиболее очевидной. Использование нескольких процессов существенно сокращает время вычисления. Например, для матрицы 12x12 использование 8 процессов уменьшает время вычисления более чем в 2.5 раза по сравнению с последовательным методом. + +**3. Оптимальное количество процессов:** + +Результаты показывают, что оптимальное количество процессов зависит от размера матрицы. Для больших матриц (11x11, 12x12) использование 4 или 8 процессов дает наилучшие результаты. Дальнейшее увеличение числа процессов может не давать значительного улучшения или даже привести к небольшому замедлению из-за накладных расходов на межпроцессное взаимодействие. Для меньших матриц оптимальным может быть меньшее число процессов, или даже последовательное выполнение. + +**Вывод:** + +Параллельное вычисление определителя матрицы эффективно для больших матриц, позволяя значительно сократить время вычисления. Однако для малых матриц накладные расходы на параллелизм могут перевесить преимущества. Выбор оптимального количества процессов зависит от размера матрицы и аппаратных ресурсов системы. В данном случае, для представленных размеров матриц, 4 или 8 процессов обеспечивают наилучшую производительность. + +--- + +#### Демонстрация +Видео доступно по [ссылке](https://disk.yandex.ru/i/eJaYxGy2Jr_SFA) \ No newline at end of file diff --git a/minhasapov_ruslan_lab_6/main.py b/minhasapov_ruslan_lab_6/main.py new file mode 100644 index 0000000..a7b8509 --- /dev/null +++ b/minhasapov_ruslan_lab_6/main.py @@ -0,0 +1,82 @@ +import time +import random +from multiprocessing import Pool + +# Генерация квадратной матрицы со случайными элементами +def generate_matrix(size): + return [[random.randint(0, 10) for _ in range(size)] for _ in range(size)] + +# Рекурсивное вычисление по определителю (по первой строке) +def determinant_recursive(matrix): + n = len(matrix) + if n == 1: + return matrix[0][0] + elif n == 2: + return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0] + else: + det = 0 + for c in range(n): + if n > 1: + minor = [row[:c] + row[c+1:] for row in matrix[1:]] + else: + minor = [] + det += ((-1)**c) * matrix[0][c] * determinant_recursive(minor) + return det + +# Вычисление части определителя, выполняется каждым процессом +def worker(chunk): + start, end, matrix = chunk + local_det = 0 + + if len(matrix) == 1: + local_det = matrix[0][0] + else: + for c in range(start, end): + minor = [row[:c] + row[c + 1:] for row in matrix[1:]] + local_det += ((-1)**c) * matrix[0][c] * determinant_recursive(minor) + + return local_det + +# Параллельное вычисление определителя матрицы +def parallel_determinant(matrix, num_processes): + n = len(matrix) + + with Pool(processes=num_processes) as pool: + num_chunks = min(n, num_processes) + chunk_size = n // num_chunks + remainder = n % num_chunks + + chunks = [] + start_col = 0 + for i in range(num_chunks): + end_col = start_col + chunk_size + (1 if i < remainder else 0) + chunks.append((start_col, end_col, matrix)) + start_col = end_col + + results = pool.map(worker, chunks) + + return sum(results) + +if __name__ == "__main__": + sizes = [2, 4, 6, 8, 10] + process_counts = [2, 4, 8] + + print("| Размер | Метод | Кол-во процессов | Время (мс) | Детерминант |") + + for size in sizes: + matrix = generate_matrix(size) + + # Последовательное + start_time = time.time() + det_recursive = determinant_recursive(matrix) + end_time = time.time() + recursive_time_ms = (end_time - start_time) * 1000 + print(f"| {size}x{size} | Последовательный | - | {recursive_time_ms} | {det_recursive} |") + + # Параллельное + for num_processes in process_counts: + start_time = time.time() + det_parallel = parallel_determinant(matrix, num_processes) + end_time = time.time() + parallel_time_ms = (end_time - start_time) * 1000 + print(f"| {size}x{size} | Параллельный | {num_processes} | {parallel_time_ms} | {det_parallel} |") \ No newline at end of file