From a0cd8cbd1280a52662b2d7ad0b8419cbeb7ed955 Mon Sep 17 00:00:00 2001 From: superhugebetformiladze Date: Mon, 22 Jan 2024 04:27:41 +0400 Subject: [PATCH] simonov nikita lab 5 done --- simonov_nikita_lab_5/main.py | 74 ++++++++++++++++++ simonov_nikita_lab_5/readme.md | 31 ++++++++ simonov_nikita_lab_5/screens/Screenshot_1.png | Bin 0 -> 11526 bytes 3 files changed, 105 insertions(+) create mode 100644 simonov_nikita_lab_5/main.py create mode 100644 simonov_nikita_lab_5/readme.md create mode 100644 simonov_nikita_lab_5/screens/Screenshot_1.png diff --git a/simonov_nikita_lab_5/main.py b/simonov_nikita_lab_5/main.py new file mode 100644 index 0000000..40c6615 --- /dev/null +++ b/simonov_nikita_lab_5/main.py @@ -0,0 +1,74 @@ +import multiprocessing +import numpy as np +import time + +def sequential_multiply_matrix(A, B): + rows_A = len(A) + cols_A = len(A[0]) + rows_B = len(B) + cols_B = len(B[0]) + + if cols_A != rows_B: + print("Умножение матриц невозможно.") + return + + result_matrix = [[0 for row in range(cols_B)] for col in range(rows_A)] + + for i in range(rows_A): + for j in range(cols_B): + for k in range(cols_A): + result_matrix[i][j] += A[i][k] * B[k][j] + + return result_matrix + +def parallel_multiply_matrix(A, B, num_processes): + rows_A = len(A) + cols_A = len(A[0]) + rows_B = len(B) + cols_B = len(B[0]) + + if cols_A != rows_B: + print("Умножение матриц невозможно.") + return + + result_matrix = [[0 for row in range(cols_B)] for col in range(rows_A)] + + chunk_size = int(rows_A / num_processes) + + processes = [] + for i in range(num_processes): + start = chunk_size * i + end = chunk_size * (i + 1) if i < num_processes - 1 else rows_A + + p = multiprocessing.Process(target=perform_multiplication, args=(A, B, result_matrix, start, end)) + processes.append(p) + p.start() + + for p in processes: + p.join() + + return result_matrix + +def perform_multiplication(A, B, result_matrix, start, end): + for i in range(start, end): + for j in range(len(B[0])): + for k in range(len(A[0])): + result_matrix[i][j] += A[i][k] * B[k][j] + +if __name__ == "__main__": + matrix_sizes = [100, 300, 500] + num_processes = 4 + + for n in matrix_sizes: + matrix_A = np.random.randint(10, size=(n, n)) + matrix_B = np.random.randint(10, size=(n, n)) + + start_time = time.time() + sequential_result = sequential_multiply_matrix(matrix_A, matrix_B) + end_time = time.time() + print(f"Последовательное умножение {n}x{n} матриц заняло: {end_time - start_time} секунд") + + start_time = time.time() + parallel_result = parallel_multiply_matrix(matrix_A, matrix_B, num_processes) + end_time = time.time() + print(f"Параллельное умножение {n}x{n} матриц заняло: {end_time - start_time}") diff --git a/simonov_nikita_lab_5/readme.md b/simonov_nikita_lab_5/readme.md new file mode 100644 index 0000000..9d2445e --- /dev/null +++ b/simonov_nikita_lab_5/readme.md @@ -0,0 +1,31 @@ +# Лабораторная работа №5. + +## Задание + +Реализовать умножение двух больших квадратных матриц + +## Ход выполнения + +- Реализовать алгоритм перемножение матриц для потокового выполнения +- Адаптировать алгоритм для параллельного выполнения: +- Разделить данные на чанки, сохранив корректность вычислений + +## Технологии +- `numpy`: библиотека для работы с многомерными массивами и матрицами в Python. +- `multiprocessing`: модуль Python, предоставляющий возможность создания и управления процессами, что позволяет реализовать параллельные вычисления. + +## Ход работы + +- `sequential_multiply_matrix`: Функция выполняет последовательное умножение двух матриц. Перед умножением производится проверка на возможность выполнения операции. Результат сохраняется в новой матрице. + +- `parallel_multiply_matrix`: Функция выполняет параллельное умножение двух матриц, разделяя работу между указанным числом процессов. Также производится проверка на возможность умножения матриц. Результат сохраняется в новой матрице. + +- `perform_multiplication`: Вспомогательная функция, используемая в параллельном умножении. Каждый процесс выполняет умножение только для своего "куска" матрицы. + +## Результаты + +![](screens/Screenshot_1.png) + +## Ссылка на видео + +https://drive.google.com/file/d/18GxoovYjWLUAOhtsORBzBUByu4HdIeY8/view?usp=sharing \ No newline at end of file diff --git a/simonov_nikita_lab_5/screens/Screenshot_1.png b/simonov_nikita_lab_5/screens/Screenshot_1.png new file mode 100644 index 0000000000000000000000000000000000000000..ed0051736b5e130bdf2b2ef74a49eaf92f3d06f0 GIT binary patch literal 11526 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 z