Merge pull request 'malafeev_leonid_lab_6' (#398) from malafeev_leonid_lab_6 into main
Reviewed-on: #398
This commit was merged in pull request #398.
This commit is contained in:
39
malafeev_leonid_lab_6/README.md
Normal file
39
malafeev_leonid_lab_6/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Лабораторная работа по параллельному вычислению детерминанта матрицы (LU-разложение)
|
||||
|
||||
Сделал, как просили — два алгоритма вычисления детерминанта квадратной матрицы: последовательный и параллельный. Использовал LU-разложение (метод Гаусса). Сделал бенчмарки. Работает, считает быстро.
|
||||
|
||||
## Что делает
|
||||
Программа реализует:
|
||||
1. **Последовательное вычисление** детерминанта через LU-разложение (метод Гаусса с выбором ведущего элемента).
|
||||
2. **Параллельное вычисление** детерминанта тем же методом, где **внутри каждой итерации** внешнего цикла (когда обновляются строки под ведущим элементом) вычисления для **разных строк** распределяются между потоками.
|
||||
3. **Задание со ***: параллельный алгоритм с 1 потоком работает как обычный последовательный (внутри итерации распределяется 1 строка, обработка которой происходит последовательно).
|
||||
4. **Бенчмарки**: измеряет время выполнения для матриц размером 100x100, 300x300, 500x500 с разным количеством потоков.
|
||||
5. **Сравнение**: выводит таблицу результатов (время, ускорение) и сохраняет её в файл `benchmark_determinant_lu_results.csv`.
|
||||
|
||||
## Технологии
|
||||
1. Python 3
|
||||
2. Библиотека `numpy` для работы с матрицами
|
||||
3. Библиотека `pandas` для форматирования таблицы результатов
|
||||
4. Модуль `concurrent.futures` для управления потоками
|
||||
|
||||
## Как запустить
|
||||
1. Убедиться, что установлен Python 3.
|
||||
2. Установить зависимости:
|
||||
`pip install numpy pandas`
|
||||
3. В терминале перейти в папку с файлом `main_determinant.py`.
|
||||
4. Выполнить команду:
|
||||
`python main_determinant.py`
|
||||
|
||||
## Тесты
|
||||
Программа запускает серию тестов:
|
||||
1. Генерирует "устойчивые" случайные матрицы заданных размеров (100x100, 300x300, 500x500).
|
||||
2. Вычисляет детерминант с помощью последовательного алгоритма (LU-разложение).
|
||||
3. Вычисляет детерминант с помощью параллельного алгоритма (LU-разложение с распределением строк между потоками внутри итерации).
|
||||
4. Сравнивает значения детерминантов (могут отличаться из-за погрешностей вычислений с плавающей точкой).
|
||||
5. Замеряет и выводит время выполнения для каждого теста.
|
||||
6. Вычисляет и выводит ускорение (`sequential_time / parallel_time`).
|
||||
7. Собирает все результаты (размер, алгоритм, потоки, время, детерминант, ускорение) в `pandas.DataFrame` и выводит в консоль в виде таблицы.
|
||||
8. Сохраняет таблицу с результатами в файл `benchmark_determinant_lu_results.csv`.
|
||||
|
||||
## Видео
|
||||
https://vkvideo.ru/video-233205700_456239023
|
||||
13
malafeev_leonid_lab_6/benchmark_determinant_lu_results.csv
Normal file
13
malafeev_leonid_lab_6/benchmark_determinant_lu_results.csv
Normal file
@@ -0,0 +1,13 @@
|
||||
Размер,Алгоритм,Потоки,Время (с),Детерминант,Ускорение
|
||||
100,Последовательный,1,0.02842402458190918,25297894.350354116,
|
||||
100,Параллельный,2,0.1195516586303711,25297894.350354116,0.2377551671599167
|
||||
100,Параллельный,4,0.16419363021850586,25297894.350354116,0.17311283357389085
|
||||
100,Параллельный,8,0.21109366416931152,25297894.350354116,0.13465124447842816
|
||||
300,Последовательный,1,0.2608449459075928,9.962891199441524e+21,
|
||||
300,Параллельный,2,0.5623910427093506,9.962891199441524e+21,0.463814189946834
|
||||
300,Параллельный,4,0.6823325157165527,9.962891199441524e+21,0.38228420879762115
|
||||
300,Параллельный,8,0.8545553684234619,9.962891199441524e+21,0.30524054443519105
|
||||
500,Последовательный,1,0.7417495250701904,8.547565819508747e+37,
|
||||
500,Параллельный,2,1.2983334064483643,8.547565819508747e+37,0.571308973015854
|
||||
500,Параллельный,4,1.52494215965271,8.547565819508747e+37,0.4864115798589478
|
||||
500,Параллельный,8,1.7622523307800293,8.547565819508747e+37,0.42090994128058173
|
||||
|
163
malafeev_leonid_lab_6/main.py
Normal file
163
malafeev_leonid_lab_6/main.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import numpy as np
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
import pandas as pd
|
||||
import threading
|
||||
|
||||
def sequential_lu_determinant(matrix):
|
||||
"""
|
||||
Последовательный алгоритм вычисления детерминанта через LU-разложение
|
||||
(метод Гаусса с выбором главного элемента по столбцу).
|
||||
"""
|
||||
n = matrix.shape[0]
|
||||
A = matrix.astype(np.float64).copy()
|
||||
sign = 1
|
||||
|
||||
for i in range(n):
|
||||
# Поиск максимального элемента в столбце i, начиная с диагонали
|
||||
max_row = i + np.argmax(np.abs(A[i:n, i]))
|
||||
if max_row != i:
|
||||
A[[i, max_row]] = A[[max_row, i]]
|
||||
sign *= -1 # Смена строк меняет знак детерминанта
|
||||
|
||||
# Если ведущий элемент близок к нулю, детерминант = 0
|
||||
if abs(A[i, i]) < 1e-15:
|
||||
return 0.0
|
||||
|
||||
# Исключение столбца i
|
||||
for k in range(i + 1, n):
|
||||
factor = A[k, i] / A[i, i]
|
||||
A[k, i:] -= factor * A[i, i:]
|
||||
|
||||
# Детерминант = произведение диагональных элементов
|
||||
diag_product = np.prod(np.diag(A))
|
||||
return sign * diag_product
|
||||
|
||||
def worker_update_rows(args):
|
||||
A_full, i, start_k, end_k, pivot_row, pivot_elem = args
|
||||
# Извлекаем срезы для удобства
|
||||
A_rows_to_update = A_full[start_k:end_k, :]
|
||||
A_pivot_row = pivot_row # Уже передаём нужную строку
|
||||
|
||||
for idx, k in enumerate(range(start_k, end_k)):
|
||||
factor = A_rows_to_update[idx, i] / pivot_elem
|
||||
A_rows_to_update[idx, i:] -= factor * A_pivot_row[i:]
|
||||
|
||||
|
||||
|
||||
def parallel_lu_determinant(matrix, num_threads=4):
|
||||
"""
|
||||
Параллельный алгоритм вычисления детерминанта через LU-разложение.
|
||||
Параллелизация происходит внутри каждой итерации внешнего цикла (i):
|
||||
строки, подлежащие обновлению (k от i+1 до n-1), распределяются между потоками.
|
||||
"""
|
||||
n = matrix.shape[0]
|
||||
A = matrix.astype(np.float64).copy()
|
||||
sign = 1
|
||||
|
||||
for i in range(n):
|
||||
# Поиск максимального элемента в столбце i
|
||||
max_row = i + np.argmax(np.abs(A[i:n, i]))
|
||||
if max_row != i:
|
||||
A[[i, max_row]] = A[[max_row, i]]
|
||||
sign *= -1
|
||||
|
||||
if abs(A[i, i]) < 1e-15:
|
||||
return 0.0
|
||||
|
||||
rows_to_update = list(range(i + 1, n))
|
||||
if not rows_to_update:
|
||||
continue
|
||||
|
||||
# Распределяем строки rows_to_update между потоками
|
||||
chunk_size = len(rows_to_update) // num_threads
|
||||
remainder = len(rows_to_update) % num_threads
|
||||
|
||||
futures = []
|
||||
start_idx = 0
|
||||
with ThreadPoolExecutor(max_workers=num_threads) as executor:
|
||||
for t in range(num_threads):
|
||||
# Определяем, какие строки обработает поток t
|
||||
chunk_size_t = chunk_size + (1 if t < remainder else 0)
|
||||
end_idx = start_idx + chunk_size_t
|
||||
|
||||
if chunk_size_t > 0:
|
||||
start_k = rows_to_update[start_idx]
|
||||
end_k = rows_to_update[end_idx - 1] + 1
|
||||
|
||||
# Подготовка аргументов для задачи
|
||||
task_args = (A, i, start_k, end_k, A[i, :], A[i, i])
|
||||
future = executor.submit(worker_update_rows, task_args)
|
||||
futures.append(future)
|
||||
|
||||
start_idx = end_idx
|
||||
|
||||
# Ждём завершения всех задач для текущей итерации i
|
||||
for future in futures:
|
||||
future.result()
|
||||
|
||||
diag_product = np.prod(np.diag(A))
|
||||
return sign * diag_product
|
||||
|
||||
|
||||
def generate_stable_matrix(size, scale=1.0):
|
||||
"""
|
||||
Генерирует устойчивую (по определителю) случайную матрицу.
|
||||
"""
|
||||
np.random.seed(13)
|
||||
random_matrix = np.random.randn(size, size)
|
||||
Q, R = np.linalg.qr(random_matrix)
|
||||
eigenvalues = np.random.uniform(0.5, 2.0, size) * scale
|
||||
D = np.diag(eigenvalues)
|
||||
|
||||
A = Q @ D @ Q.T
|
||||
return A
|
||||
|
||||
|
||||
def benchmark_determinant():
|
||||
"""Функция для проведения бенчмарков детерминанта с LU-разложением."""
|
||||
sizes = [100, 300, 500]
|
||||
thread_counts = [1, 2, 4, 8]
|
||||
|
||||
results = []
|
||||
|
||||
print("=" * 80)
|
||||
print("БЕНЧМАРК ВЫЧИСЛЕНИЯ ДЕТЕРМИНАНТА (LU-РАЗЛОЖЕНИЕ)")
|
||||
print("=" * 80)
|
||||
|
||||
for size in sizes:
|
||||
print(f"\n--- Тестирование для матриц размером {size}x{size} ---")
|
||||
matrix = generate_stable_matrix(size)
|
||||
|
||||
# --- Последовательный алгоритм (1 поток = обычный алгоритм) ---
|
||||
start_time = time.time()
|
||||
det_seq = sequential_lu_determinant(matrix.copy()) # Копируем, чтобы не изменять исходную
|
||||
seq_time = time.time() - start_time
|
||||
print(f"Последовательный: {seq_time:.4f} секунд, детерминант = {det_seq:.6e}")
|
||||
results.append({'Размер': size, 'Алгоритм': 'Последовательный', 'Потоки': 1, 'Время (с)': seq_time, 'Детерминант': det_seq})
|
||||
|
||||
# --- Параллельный алгоритм для разных чисел потоков ---
|
||||
for num_threads in thread_counts:
|
||||
if num_threads == 1:
|
||||
continue # Пропускаем 1 поток, так как это последовательный (уже посчитано)
|
||||
|
||||
start_time = time.time()
|
||||
det_par = parallel_lu_determinant(matrix.copy(), num_threads)
|
||||
par_time = time.time() - start_time
|
||||
print(f"Параллельный ({num_threads} потоков): {par_time:.4f} секунд, детерминант = {det_par:.6e}")
|
||||
speedup = seq_time / par_time if par_time > 0 else 0
|
||||
results.append({'Размер': size, 'Алгоритм': 'Параллельный', 'Потоки': num_threads, 'Время (с)': par_time, 'Детерминант': det_par, 'Ускорение': speedup})
|
||||
print(f" Ускорение: {speedup:.2f}x")
|
||||
|
||||
|
||||
# Создание и вывод таблицы результатов
|
||||
df = pd.DataFrame(results)
|
||||
print("\n--- Результаты бенчмарков ---")
|
||||
print(df.to_string(index=False))
|
||||
|
||||
# Сохранение таблицы в файл
|
||||
df.to_csv('benchmark_determinant_lu_results.csv', index=False)
|
||||
print("\nРезультаты сохранены в файл 'benchmark_determinant_lu_results.csv'")
|
||||
|
||||
if __name__ == "__main__":
|
||||
benchmark_determinant()
|
||||
Reference in New Issue
Block a user