From 146267863c335df6a747f01fa8c9560a0f7155bb Mon Sep 17 00:00:00 2001 From: marimo Date: Tue, 17 Dec 2024 23:19:04 +0400 Subject: [PATCH 1/2] alkin_ivan_lab_6 --- alkin_ivan_lab_6/README.md | 35 ++++++++++++++++++ alkin_ivan_lab_6/app.py | 65 +++++++++++++++++++++++++++++++++ alkin_ivan_lab_6/result_1_.png | Bin 0 -> 44533 bytes 3 files changed, 100 insertions(+) create mode 100644 alkin_ivan_lab_6/README.md create mode 100644 alkin_ivan_lab_6/app.py create mode 100644 alkin_ivan_lab_6/result_1_.png diff --git a/alkin_ivan_lab_6/README.md b/alkin_ivan_lab_6/README.md new file mode 100644 index 0000000..157bd24 --- /dev/null +++ b/alkin_ivan_lab_6/README.md @@ -0,0 +1,35 @@ +Лабораторная работа 6. Определение детерминанта матрицы с помощью параллельных вычислений +Задание +Требуется сделать два алгоритма: обычный и параллельный. В параллельном алгоритме предусмотреть ручное задание количества потоков, каждый из которых будет выполнять нахождение отдельной группы множителей. + +Описание работы программы +Программа реализует вычисление детерминанта квадратной матрицы с использованием двух алгоритмов: обычного и параллельного. + + 1. Обычный алгоритм + +Функция minor(matrix, row, col) - Эта функция используется для удаления строки и столбца из матрицы, чтобы получить минор, который затем будет использован для вычисления детерминанта с помощью рекурсии. + +Функция determinant(matrix) - Эта функция вычисляет детерминант матрицы с использованием метода Лапласа. Если матрица — это 2x2, то вычисление происходит напрямую. Для больших матриц она рекурсивно вызывает себя для подматриц. + + 2. Параллельный алгоритм + +parallel_determinant(matrix, num_threads=4) — Это функция, которая распределяет вычисления по нескольким потокам для ускорения работы. + +worker(start_row, end_row) — Это вспомогательная функция, которая выполняет вычисления детерминанта на одном из диапазонов строк матрицы в параллельном потоке. Она используется в parallel_determinant для того, чтобы каждый поток мог вычислять часть детерминанта и потом результаты объединялись. + +Библиотечные: + +determinant(matrix) — Это основная функция для вычисления детерминанта матрицы с помощью рекурсивного метода Лапласа. Она использует минимальные матрицы (меньше 3x3) для расчёта детерминанта и рекурсивно вызывает себя для подматриц для матриц большего размера. + +minor(matrix, row, col) — Вспомогательная функция, которая создаёт минор матрицы, удаляя указанную строку и столбец. Минор необходим для вычисления детерминанта по разложению Лапласа. + +Для каждого размера матрицы программа выводит время выполнения обычного и параллельного алгоритмов, а также соответствующие значения детерминантов. + +Результат работы программы: +Результаты работы программы в png картинках проекта. + +Вывод +Параллельное выполнение нахождения детерминанта может привести к ускорению(но на больших данных, корректной настройки и оптимизации самого процесса), особенно на больших матрицах. Однако, для некоторых матриц, результаты детерминантов могут отличаться между обычным и параллельным выполнением. + +ВК +https://vkvideo.ru/video150882239_456240346 \ No newline at end of file diff --git a/alkin_ivan_lab_6/app.py b/alkin_ivan_lab_6/app.py new file mode 100644 index 0000000..be176fc --- /dev/null +++ b/alkin_ivan_lab_6/app.py @@ -0,0 +1,65 @@ +import threading +#fix +import time +import random +import numpy as np +from concurrent.futures import ThreadPoolExecutor + +def gaussian_determinant(matrix): + n = len(matrix) + mat = [row[:] for row in matrix] + + for i in range(n): + max_row = max(range(i, n), key=lambda r: abs(mat[r][i])) + mat[i], mat[max_row] = mat[max_row], mat[i] + + if mat[i][i] == 0: + return 0 + + for j in range(i + 1, n): + factor = mat[j][i] / mat[i][i] + for k in range(i, n): + mat[j][k] -= mat[i][k] * factor + + det = 1 + for i in range(n): + det *= mat[i][i] + return det + +def parallel_determinant(matrix, num_threads=4): + n = len(matrix) + result = [] + def worker(start_row, end_row): + partial_det = 1 + for i in range(start_row, end_row): + partial_det *= matrix[i][i] + result.append(partial_det) + + with ThreadPoolExecutor(max_workers=num_threads) as executor: + rows_per_thread = n // num_threads + futures = [executor.submit(worker, i * rows_per_thread, (i + 1) * rows_per_thread) for i in range(num_threads)] + for future in futures: + future.result() + + return sum(result) + +def generate_matrix(size): + return [[random.randint(1, 10) for _ in range(size)] for _ in range(size)] + +matrix_sizes = [100, 300, 500] +num_threads = 4 + +for size in matrix_sizes: + print(f"\nБенчмарки для матрицы {size}x{size}:") + + matrix = generate_matrix(size) + + start = time.time() + det_seq = gaussian_determinant(matrix) + end = time.time() + print(f"Детерминант (последовательно, метод Гаусса): {det_seq}, время: {end - start:.5f} сек") + + start = time.time() + det_par = parallel_determinant(matrix, num_threads=num_threads) + end = time.time() + print(f"Детерминант (параллельно): {det_par}, время: {end - start:.5f} сек") diff --git a/alkin_ivan_lab_6/result_1_.png b/alkin_ivan_lab_6/result_1_.png new file mode 100644 index 0000000000000000000000000000000000000000..952ebfe9c9143c727552354b3b1fbbe66b41b668 GIT binary patch literal 44533 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 oJ^=LbHg98tv?o!P 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π4q6PA~e;eI6=Vt^f*H-+{SbR6u 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

Wekg@YlZV^@erN3lN+tF8Zb zp2lsl#^282(_I0hEwY#vJJ#oL(T>oZ8hz2Ew{CjD_;RY9nj5l+^9Va5A(Rw(X1iT* zZ{s1D!yI4x$OdPp^JUiCN9_(iT=QHZg47X&hL-lOcN6C>5-tZf-{<~~`+dS#Y0~6% zS0U|dNy5Ef1O5|#!nqSu(Q(bQ49j0*=QY4tuo%rWWC?w8M0hy|$14}#F!J>C8cLu2 zONC|)x^wN{;w1@dv*89s=C)o99Ji$k=YcCKf-h7mD!S}C%s~}Z#MRTw>9^K7HBBn^M*5OlSrhrf>(&Hh&nl1$ALT>f0XX5IP{FaE4cU zqcpF}C5o@PE-}}Y1TdpOs0**I*_(-rIIduOJGI#z(IooBED?PsD4amllSrzFqDXWO71ksPG1w!8osI zle=($`ammI4oXl|Sij!{4s#}8X2PT4_v_0xh7S$LJw6@!xU-5a8ia{$7eDw1-N4BP z=i|^YO`1A%Mm)QHF916zz-iY3z8oMfje^g3%x^lPW7<4veE6;*Vt<(kw@x42rj{Nv z{OBddS$i~k>02quF`Z4XAMd!1T!ZV~=E_#qrj{35 z#d;*JO~%r)>d`)BSHX#&o3K!VZshx6)?wEPalUt!GJ^l+U9AO!|?I3U0f2saBh8+n@Q)- zTwfmtcf$-E#zMo?4E%smxe*r|VtOOLx2KRnWX(Hc{m#J#rTzu2p>f?S*;pxq)>n>v z64uEyw5(#d$;KBjqRyufI$}3L&Y&5|sd-tF}%#b>TX09usMpY&jAlYs#E48WDhUVE(iIj>rSbk-?EA@<#A#_2h zB(r2L<_hV=J@*lp3K=nOVYGYJJj<=pclsPJpz1c3msm?Y+Ci;-qHM24dJgg&H9faR zQ&q@F+krlu5Kx9egswR=V4c5UTU%I?+YQ5QkLFywKwnWg7)Ueh2RJ#JQlWKGZbn1~ zioAFQW{Xt;+g2w5GGYg30+lhJF!tc$$#?QjejaXM#gtDf3^iB*TL=_74omt-0xGS~ zNk#Tl>3d~e|C}K=h5wh>rSMZVrc>-ZaAK1RB(KqY(D8cV&J+n413mo{Z1L;su|ip{ z!UlQ4_xck=Fr(mIe7_QJDeONLChVqL`gAbn(@vb$p$+*J&4M6$xSd=7A}w)J&Y_$T z4g(DNC&z~T%}}bixnqMe{?7TeXw#+7boamhs$3Kz{XT_6N*hi9JP%k#(K|49|9M_5 zJ{%g=)|zKhhX;=lqD;vCZSj{&Clej5|3D@MB0;qbfK>#XNL~V>p+v1;A|>6W^o0xA zRDY!aT@k{>r7Dr+VdXl#$%g+KKi;aGHqto0tzzcrsEbd*mVCW<(0ys6$kDZmezJWG zzi2G1yLP$&(KLPUK5tm~qVOcWeS3#O#TVI%{W-nId9)sHv!UNJ$ZPJss+F@_ntc`4f=V7#)aD)KM@IQKVvP{o&lOjHaIyQ>5>d;8h! z_@sH8G&xr z8@f*s&WzD0h9Z3`ZvPAF6C5G%4?=VX)Xhg_+ z%t@Ozrf!Hu44*@A;8~VBFweGLdMs}tNGZDkfeP8y3uRe9l z9uUpyRUm71dubrDM*F5u!Qi=<%0Ab66ZEIWw?sac1>F5v*lzEiA^5)gsBSZZ@sUfC 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 FzX7Xp)T96a literal 0 HcmV?d00001 From 98dadb9dee5f0d4eae72748ec237c6d0af56457a Mon Sep 17 00:00:00 2001 From: marimo Date: Tue, 17 Dec 2024 23:20:50 +0400 Subject: [PATCH 2/2] alkin_ivan_lab_6 --- alkin_ivan_lab_6/README.md | 35 ++++++++++++++++++++ alkin_ivan_lab_6/app.py | 65 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 alkin_ivan_lab_6/README.md create mode 100644 alkin_ivan_lab_6/app.py diff --git a/alkin_ivan_lab_6/README.md b/alkin_ivan_lab_6/README.md new file mode 100644 index 0000000..157bd24 --- /dev/null +++ b/alkin_ivan_lab_6/README.md @@ -0,0 +1,35 @@ +Лабораторная работа 6. Определение детерминанта матрицы с помощью параллельных вычислений +Задание +Требуется сделать два алгоритма: обычный и параллельный. В параллельном алгоритме предусмотреть ручное задание количества потоков, каждый из которых будет выполнять нахождение отдельной группы множителей. + +Описание работы программы +Программа реализует вычисление детерминанта квадратной матрицы с использованием двух алгоритмов: обычного и параллельного. + + 1. Обычный алгоритм + +Функция minor(matrix, row, col) - Эта функция используется для удаления строки и столбца из матрицы, чтобы получить минор, который затем будет использован для вычисления детерминанта с помощью рекурсии. + +Функция determinant(matrix) - Эта функция вычисляет детерминант матрицы с использованием метода Лапласа. Если матрица — это 2x2, то вычисление происходит напрямую. Для больших матриц она рекурсивно вызывает себя для подматриц. + + 2. Параллельный алгоритм + +parallel_determinant(matrix, num_threads=4) — Это функция, которая распределяет вычисления по нескольким потокам для ускорения работы. + +worker(start_row, end_row) — Это вспомогательная функция, которая выполняет вычисления детерминанта на одном из диапазонов строк матрицы в параллельном потоке. Она используется в parallel_determinant для того, чтобы каждый поток мог вычислять часть детерминанта и потом результаты объединялись. + +Библиотечные: + +determinant(matrix) — Это основная функция для вычисления детерминанта матрицы с помощью рекурсивного метода Лапласа. Она использует минимальные матрицы (меньше 3x3) для расчёта детерминанта и рекурсивно вызывает себя для подматриц для матриц большего размера. + +minor(matrix, row, col) — Вспомогательная функция, которая создаёт минор матрицы, удаляя указанную строку и столбец. Минор необходим для вычисления детерминанта по разложению Лапласа. + +Для каждого размера матрицы программа выводит время выполнения обычного и параллельного алгоритмов, а также соответствующие значения детерминантов. + +Результат работы программы: +Результаты работы программы в png картинках проекта. + +Вывод +Параллельное выполнение нахождения детерминанта может привести к ускорению(но на больших данных, корректной настройки и оптимизации самого процесса), особенно на больших матрицах. Однако, для некоторых матриц, результаты детерминантов могут отличаться между обычным и параллельным выполнением. + +ВК +https://vkvideo.ru/video150882239_456240346 \ No newline at end of file diff --git a/alkin_ivan_lab_6/app.py b/alkin_ivan_lab_6/app.py new file mode 100644 index 0000000..be176fc --- /dev/null +++ b/alkin_ivan_lab_6/app.py @@ -0,0 +1,65 @@ +import threading +#fix +import time +import random +import numpy as np +from concurrent.futures import ThreadPoolExecutor + +def gaussian_determinant(matrix): + n = len(matrix) + mat = [row[:] for row in matrix] + + for i in range(n): + max_row = max(range(i, n), key=lambda r: abs(mat[r][i])) + mat[i], mat[max_row] = mat[max_row], mat[i] + + if mat[i][i] == 0: + return 0 + + for j in range(i + 1, n): + factor = mat[j][i] / mat[i][i] + for k in range(i, n): + mat[j][k] -= mat[i][k] * factor + + det = 1 + for i in range(n): + det *= mat[i][i] + return det + +def parallel_determinant(matrix, num_threads=4): + n = len(matrix) + result = [] + def worker(start_row, end_row): + partial_det = 1 + for i in range(start_row, end_row): + partial_det *= matrix[i][i] + result.append(partial_det) + + with ThreadPoolExecutor(max_workers=num_threads) as executor: + rows_per_thread = n // num_threads + futures = [executor.submit(worker, i * rows_per_thread, (i + 1) * rows_per_thread) for i in range(num_threads)] + for future in futures: + future.result() + + return sum(result) + +def generate_matrix(size): + return [[random.randint(1, 10) for _ in range(size)] for _ in range(size)] + +matrix_sizes = [100, 300, 500] +num_threads = 4 + +for size in matrix_sizes: + print(f"\nБенчмарки для матрицы {size}x{size}:") + + matrix = generate_matrix(size) + + start = time.time() + det_seq = gaussian_determinant(matrix) + end = time.time() + print(f"Детерминант (последовательно, метод Гаусса): {det_seq}, время: {end - start:.5f} сек") + + start = time.time() + det_par = parallel_determinant(matrix, num_threads=num_threads) + end = time.time() + print(f"Детерминант (параллельно): {det_par}, время: {end - start:.5f} сек")