diff --git a/kochkareva_elizaveta_lab_6/README.md b/kochkareva_elizaveta_lab_6/README.md new file mode 100644 index 0000000..0e5a847 --- /dev/null +++ b/kochkareva_elizaveta_lab_6/README.md @@ -0,0 +1,73 @@ + +# Лабораторная работа 6. + +### Задание + +**Задачи**: + +Реализовать нахождение детерминанта квадратной матрицы. Сделать два алгоритма: обычный и параллельный (задание со * - реализовать это в рамках одного алгоритма). В параллельном алгоритме предусмотреть ручное задание количества потоков (число потоков = 1 как раз и реализует задание со *), каждый из которых будет выполнять нахождение отдельной группы множителей. + + +### Как запустить лабораторную работу +В директории с файлом характеристик docker-compose.yaml выполнить команду: +``` +docker-compose -f docker-compose.yaml up +``` + +### Описание лабораторной работы + +Для реализации параллельного нахождения детерминанта квадратной матрицы с использованием многопоточности создадим несколько функций: + +1. Функция `calculate_determinant(args)` + +Данная функция принимает матрицу и номер элемента, по которому ищется минор. Если `i` нечетное число, множитель умножается на -1. +Удаляет первую строку из `matrix` с помощью функции `np.delete(matrix, 0, axis=0)`. +Удаляет столбец `i` из `matrix` с помощью функции `np.delete(matrix, i, axis=1)`. +Возвращает определитель полученной подматрицы, умноженный на множитель. + +```python +def calculate_determinant(args): + matrix, i = args + multiplier = matrix[0][i] + if i % 2 != 0: + multiplier *= -1 + matrix = np.delete(matrix, 0, axis=0) + submatrix = np.delete(matrix, i, axis=1) + return np.linalg.det(submatrix) * multiplier +``` + +2. Функция `parallel_determinant(matrix, parallel)` + +Данная функция принимает матрицу, для которой нужно вычислить определитель, и флаг, указывающий, следует ли использовать параллельное вычисление. Далее вычисляется размер матрицы `n` с `помощью matrix.shape[0]`. Если флаг указывает на использование параллельного вычисления, создается пул процессов с `n` процессами с помощью `Pool(processes=n)`. Затем создается цикл по значениям от `0` до `n`. +Для каждого значения `i` добавляет в список `results` результат выполнения функции `calculate_determinant` с аргументами `matrix` и `i`. Затем закрывается пул процессов и ожидается завершения всех процессов с помощью `pool.close()` и `pool.join()`. Далее суммируются все значения из списка results с помощью `np.sum([res.get() for res in results])`, сохраняет результат в переменную result и также вычисляется затраченное время на вычисление определителя. + +3. Функция `test(parallel)` + +Данная функция принимает значение флага, указывающего на использование параллельного или обычного вычисления. Также создает матрицу mx размером 3x3 и выполняет вычисление определителя матрицы mx с помощью функции `parallel_determinant(mx, parallel)`. + +4. Функции `matrix10x10(parallel)`, `matrix25x25(parallel)` и `matrix50x50(parallel)` + +Т.к. на моем устройстве не возможно вычислять детерминат матрицы размером более 100х100, то вместо использования матриц размером 100x100, 300x300, 500x500 элементов были созданы матрицы 10х10, 25х25, 50х50. + +В функциях `matrix10x10(parallel)`, `matrix25x25(parallel)` и `matrix50x50(parallel)` принимается аргумент `parallel`, указывающий, следует ли использовать параллельное вычисление. Создаются матрицы размером 10x10, 25x25 и 50x50 со случайными целочисленными значениями от 0 до 100. И выполяются вычисления определителя для каждой матрицы с помощью функции `parallel_determinant`. + +#### Результаты выполнения последовательного и параллельного алгоритма на вычисление детерминанта квадратных матриц 10х10, 25х25, 50х50. + +Результат вычисления детерминанта матрицы 10х10: + +![Результат вычисления детерминанта матрицы 10х10:](matrix10x10.jpg) + +Результат вычисления детерминанта матрицы 25х25: + +![Результат вычисления детерминанта матрицы 25х25:](matrix25x25.jpg) + +Результат вычисления детерминанта матрицы 50х50: + +![Результат перемножения матриц 500х500](matrix50x50.jpg) + +Таким образом, можно сделать вывод о том, что вычисления обычным алгоритмом выполняются быстрее, чем при использовании параллельного способа нахождения детерминанта. Если точность результата является наиболее приоритетным фактором, то использование параллельного способа может быть предпочтительным. В случае, когда время выполнения играет решающую роль, обычный алгоритм может быть более эффективным выбором. Кроме того, для дальнейшего улучшения производительности вычислений можно рассмотреть возможность оптимизации параллельного способа нахождения детерминанта, например, с использованием более эффективных алгоритмов или распределения вычислений на более мощные вычислительные узлы. + + +### Видео + +https://disk.yandex.ru/i/i8qDyMhn6nCk9A \ No newline at end of file diff --git a/kochkareva_elizaveta_lab_6/main.py b/kochkareva_elizaveta_lab_6/main.py new file mode 100644 index 0000000..8faa602 --- /dev/null +++ b/kochkareva_elizaveta_lab_6/main.py @@ -0,0 +1,79 @@ +import time +import numpy as np +from multiprocessing import Pool + + +def calculate_determinant(args): + matrix, i = args + multiplier = matrix[0][i] + if i % 2 != 0: + multiplier *= -1 + matrix = np.delete(matrix, 0, axis=0) + submatrix = np.delete(matrix, i, axis=1) + return np.linalg.det(submatrix) * multiplier + + +def parallel_determinant(matrix, parallel): + n = matrix.shape[0] + if parallel: + pool = Pool(processes=n) + results = [] + start_time = time.time() + for i in range(n): + results.append(pool.apply_async(calculate_determinant, args=((matrix, i),))) + pool.close() + pool.join() + result = np.sum([res.get() for res in results]) + end_time = time.time() + else: + start_time = time.time() + result = np.linalg.det(matrix) + end_time = time.time() + execution_time = end_time - start_time + return result, execution_time + + +def test(parallel): + mx = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + result = parallel_determinant(mx, parallel) + print(f"Определитель матрицы: {result[0]}") + print(f"Время выполнения: {result[1]} сек.") + + +def matrix10x10(parallel): + mx = np.random.randint(0, 100, size=(10, 10)) + result = parallel_determinant(mx, parallel) + print(f"Определитель матрицы: {result[0]}") + print(f"Время выполнения: {result[1]} сек.") + + +def matrix25x25(parallel): + mx = np.random.randint(0, 100, size=(25, 25)) + result = parallel_determinant(mx, parallel) + print(f"Определитель матрицы: {result[0]}") + print(f"Время выполнения: {result[1]} сек.") + + +def matrix50x50(parallel): + mx = np.random.randint(0, 100, size=(50, 50)) + result = parallel_determinant(mx, parallel) + print(f"Определитель матрицы: {result[0]}") + print(f"Время выполнения: {result[1]} сек.") + + +if __name__ == '__main__': + # print("Матрица 10x10:") + # print("Результат нахождения детерминанта параллельно:") + # matrix10x10(parallel=True) + # print("Результат нахождения детерминанта обычным способом:") + # matrix10x10(parallel=False) + # print("Матрица 25x25:") + # print("Результат нахождения детерминанта параллельно:") + # matrix25x25(parallel=True) + # print("Результат нахождения детерминанта обычным способом:") + # matrix25x25(parallel=False) + print("Матрица 50x50:") + print("Результат нахождения детерминанта параллельно:") + matrix50x50(parallel=True) + print("Результат нахождения детерминанта обычным способом:") + matrix50x50(parallel=False) diff --git a/kochkareva_elizaveta_lab_6/matrix10x10.jpg b/kochkareva_elizaveta_lab_6/matrix10x10.jpg new file mode 100644 index 0000000..6a089f6 Binary files /dev/null and b/kochkareva_elizaveta_lab_6/matrix10x10.jpg differ diff --git a/kochkareva_elizaveta_lab_6/matrix25x25.jpg b/kochkareva_elizaveta_lab_6/matrix25x25.jpg new file mode 100644 index 0000000..828ed03 Binary files /dev/null and b/kochkareva_elizaveta_lab_6/matrix25x25.jpg differ diff --git a/kochkareva_elizaveta_lab_6/matrix50x50.jpg b/kochkareva_elizaveta_lab_6/matrix50x50.jpg new file mode 100644 index 0000000..94c3cf9 Binary files /dev/null and b/kochkareva_elizaveta_lab_6/matrix50x50.jpg differ