minhasapov_ruslan_lab_6

This commit is contained in:
Safgerd 2024-11-18 05:37:56 +04:00
parent 3b9698ac38
commit 17d5f3321a
2 changed files with 160 additions and 0 deletions

View File

@ -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)

View File

@ -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} |")