Merge pull request 'Bazunov Andrew Lab 6 ready' (#236) from bazunov_andrew_lab_6 into main
Reviewed-on: #236
This commit is contained in:
commit
3ecb3ad391
57
bazunov_andrew_lab_6/README.md
Normal file
57
bazunov_andrew_lab_6/README.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Распределенные вычисления и приложения Л6
|
||||
|
||||
## _Автор Базунов Андрей Игревич ПИбд-42_
|
||||
|
||||
---
|
||||
> ### Задание
|
||||
> - Кратко: реализовать нахождение детерминанта квадратной матрицы. Что такое детерминант матрицы (или определитель)
|
||||
> - Подробно: в лабораторной работе требуется сделать два алгоритма: обычный и параллельный (задание со * - реализовать это в рамках одного алгоритма). В параллельном алгоритме предусмотреть ручное задание количества потоков (число потоков = 1 как раз и реализует задание со *), каждый из которых будет выполнять нахождение отдельной группы множителей.
|
||||
---
|
||||
|
||||
## Алгоритм:
|
||||
|
||||
### Функция вычисления определителя матрицы
|
||||
|
||||
<details>
|
||||
<summary>Код</summary>
|
||||
|
||||
```python
|
||||
def minor(matrix, i, j):
|
||||
return [row[:j] + row[j + 1:] for row in (matrix[:i] + matrix[i + 1:])]
|
||||
|
||||
|
||||
def determinant(matrix: list, threads=1) -> float:
|
||||
if len(matrix) == 1:
|
||||
return matrix[0][0]
|
||||
elif len(matrix) == 2:
|
||||
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]
|
||||
|
||||
det = 0
|
||||
futures = []
|
||||
with ThreadPoolExecutor(max_workers=threads) as executor:
|
||||
for col in range(len(matrix)):
|
||||
cofactor = (-1) ** col * matrix[0][col]
|
||||
futures.append(executor.submit(lambda m, cof: cof * determinant(m), minor(matrix, 0, col), cofactor))
|
||||
|
||||
for future in futures:
|
||||
det += future.result()
|
||||
|
||||
return det
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
| Размер матрицы | 1 Поток (сек) | 5 Потоков (сек) | 20 Потоков (сек) |
|
||||
|----------------|---------------|-----------------|------------------|
|
||||
| 1x1 | 0.0 | 0.0 | 0.0 |
|
||||
| 2x2 | 0.0 | 0.0 | 0.0 |
|
||||
| 3x3 | 0.00018 | 0.00019 | 0.00017 |
|
||||
| 4x4 | 0.00061 | 0.00072 | 0.0007 |
|
||||
| 5x5 | 0.00247 | 0.00359 | 0.00336 |
|
||||
| 6x6 | 0.01306 | 0.01931 | 0.02129 |
|
||||
| 7x7 | 0.08692 | 0.14062 | 0.15682 |
|
||||
| 8x8 | 0.59383 | 1.03971 | 1.27658 |
|
||||
| 9x9 | 5.23021 | 9.72031 | 11.71564 |
|
||||
|
||||
Видео отчет: https://vkvideo.ru/video236673313_456239579
|
26
bazunov_andrew_lab_6/main.py
Normal file
26
bazunov_andrew_lab_6/main.py
Normal file
@ -0,0 +1,26 @@
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
from random import random
|
||||
|
||||
from matrix import Matrix
|
||||
|
||||
_THREADS = 20
|
||||
|
||||
|
||||
def measure_time(func: Callable, *args) -> float:
|
||||
t1 = time.process_time()
|
||||
func(*args)
|
||||
t2 = time.process_time()
|
||||
return round(t2 - t1, 5)
|
||||
|
||||
|
||||
tests = [i for i in range(1, 10)]
|
||||
|
||||
for test in tests:
|
||||
mt1 = Matrix(size=test, suplyer=random)
|
||||
|
||||
t1 = measure_time(lambda: mt1.det())
|
||||
t5 = measure_time(lambda: mt1.det(threads=5))
|
||||
t20 = measure_time(lambda: mt1.det(threads=20))
|
||||
|
||||
print(f"|{f'{test}x{test}':<16}|{t1:^11}|{t5:^11}|{t20:^12}|")
|
171
bazunov_andrew_lab_6/matrix.py
Normal file
171
bazunov_andrew_lab_6/matrix.py
Normal file
@ -0,0 +1,171 @@
|
||||
import itertools
|
||||
from collections.abc import Callable
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from queue import Queue
|
||||
from typing import Tuple, List
|
||||
import numpy as np
|
||||
|
||||
_SUPLYER_TYPE = Callable[[], int | float] | int | float
|
||||
_QUEUE_TYPE = Queue[Tuple[List[float | int], List[float | int], int]]
|
||||
|
||||
|
||||
class Matrix:
|
||||
def __init__(self, size: int, suplyer: _SUPLYER_TYPE = 0):
|
||||
self.__size = size
|
||||
self.__matrix = self._generate_matrix(suplyer)
|
||||
|
||||
def _generate_matrix(self, suplyer: _SUPLYER_TYPE):
|
||||
if suplyer:
|
||||
match suplyer:
|
||||
case int() | float():
|
||||
return [[suplyer for _ in range(self.__size)] for _ in range(self.__size)]
|
||||
case Callable():
|
||||
return [[suplyer() for _ in range(self.__size)] for _ in range(self.__size)]
|
||||
return [[0 for _ in range(self.__size)] for _ in range(self.__size)]
|
||||
|
||||
def from_flat(self, numbers: List[int | float]):
|
||||
if len(numbers) != self.__size ** 2:
|
||||
raise Exception(f"Invalid matrix size {self.__size} ^ 2 != {len(numbers)}")
|
||||
x, y = 0, 0
|
||||
for number in numbers:
|
||||
self.__matrix[y][x] = number
|
||||
x += 1
|
||||
if x >= self.__size:
|
||||
x = 0
|
||||
y += 1
|
||||
|
||||
@property
|
||||
def rows(self):
|
||||
return self.__matrix
|
||||
|
||||
@property
|
||||
def columns(self):
|
||||
return [[self.__matrix[i][j] for i in range(self.__size)] for j in range(self.__size)]
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self.__size
|
||||
|
||||
@staticmethod
|
||||
def random(*, size: int):
|
||||
import random
|
||||
return Matrix(size=size, suplyer=random.random)
|
||||
|
||||
def to_numpy(self):
|
||||
return np.array(self.__matrix)
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, Matrix)
|
||||
and self.__size == other.__size)
|
||||
|
||||
def __str__(self):
|
||||
return f"Matrix {self.__size}x{self.__size} \n" + "\n".join([str(
|
||||
" ".join([f"{round(element, 3):<10}" for element in row])
|
||||
) for row in self.__matrix])
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.__matrix)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.__matrix[index]
|
||||
|
||||
def __mul__(self, other):
|
||||
match other:
|
||||
case Matrix():
|
||||
return mul_matrixs(self, other)
|
||||
case tuple():
|
||||
other_matrix, count_threads = other
|
||||
return mul_matrixs(self, other_matrix, count_threads)
|
||||
return None
|
||||
|
||||
def toLU(self, threads: int = 1):
|
||||
L = Matrix(size=self.__size, suplyer=lambda: 0)
|
||||
U = Matrix(size=self.__size, suplyer=lambda: 0)
|
||||
n = self.__size
|
||||
|
||||
for i in range(n):
|
||||
for k in range(i, n):
|
||||
sum_upper = sum(L[i][j] * U[j][k] for j in range(i))
|
||||
U[i][k] = self.__matrix[i][k] - sum_upper
|
||||
|
||||
L[i][i] = 1
|
||||
for k in range(i + 1, n):
|
||||
sum_lower = sum(L[k][j] * U[j][i] for j in range(i))
|
||||
L[k][i] = (self.__matrix[k][i] - sum_lower) / U[i][i]
|
||||
|
||||
return L, U
|
||||
|
||||
def triangle_det(self):
|
||||
det = 1
|
||||
for i in range(self.__size):
|
||||
det *= self.__matrix[i][i]
|
||||
|
||||
return det
|
||||
|
||||
def det(self, threads=1) -> float:
|
||||
return determinant(self.__matrix, threads)
|
||||
|
||||
|
||||
def minor(matrix, i, j):
|
||||
return [row[:j] + row[j + 1:] for row in (matrix[:i] + matrix[i + 1:])]
|
||||
|
||||
|
||||
def determinant(matrix: list, threads=1) -> float:
|
||||
if len(matrix) == 1:
|
||||
return matrix[0][0]
|
||||
elif len(matrix) == 2:
|
||||
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]
|
||||
|
||||
det = 0
|
||||
futures = []
|
||||
with ThreadPoolExecutor(max_workers=threads) as executor:
|
||||
for col in range(len(matrix)):
|
||||
cofactor = (-1) ** col * matrix[0][col]
|
||||
futures.append(executor.submit(lambda m, cof: cof * determinant(m), minor(matrix, 0, col), cofactor))
|
||||
|
||||
for future in futures:
|
||||
det += future.result()
|
||||
|
||||
return det
|
||||
|
||||
|
||||
def mul_row_and_column_in_thread(queue: _QUEUE_TYPE) -> list[tuple[int | float, int]]:
|
||||
result = []
|
||||
while queue.qsize():
|
||||
local_result = 0
|
||||
row, column, place = queue.get()
|
||||
for k in range(len(row)):
|
||||
local_result += row[k] * column[k]
|
||||
result.append((local_result, place))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def mul_matrixs(m1: Matrix, m2: Matrix, threads: int = 0):
|
||||
if m1.size != m2.size:
|
||||
return None
|
||||
|
||||
if threads == 0:
|
||||
threads = 1
|
||||
|
||||
result = Matrix(size=m1.size, suplyer=0)
|
||||
|
||||
thread_queues = [Queue() for _ in range(threads)]
|
||||
thread_iterator = 0
|
||||
|
||||
for row_m1, column_m2 in itertools.product(m1.rows, m2.columns):
|
||||
thread_queues[thread_iterator].put((row_m1, column_m2, thread_iterator))
|
||||
thread_iterator += 1
|
||||
if thread_iterator >= threads:
|
||||
thread_iterator = 0
|
||||
|
||||
with ThreadPoolExecutor(max_workers=threads) as executor:
|
||||
flat = []
|
||||
|
||||
for item in executor.map(mul_row_and_column_in_thread, thread_queues):
|
||||
flat += item
|
||||
|
||||
flat.sort(key=lambda x: x[1])
|
||||
result.from_flat([*map(lambda x: x[0], flat)])
|
||||
|
||||
return result
|
Loading…
Reference in New Issue
Block a user