import numpy as np
import time
from multiprocessing import Pool

# Функция для вычисления детерминанта с использованием метода Гаусса
def determinant_gauss(matrix):
    n = matrix.shape[0]
    A = matrix.astype(float)  # Создаём копию матрицы, чтобы не изменять исходную
    det = 1  # Начальная величина детерминанта
    for i in range(n):
        # Ищем максимальный элемент в текущем столбце для уменьшения ошибок округления
        max_row = np.argmax(np.abs(A[i:n, i])) + i
        if A[max_row, i] == 0:
            return 0  # Если на диагонали ноль, то детерминант равен нулю
        # Переставляем строки
        if max_row != i:
            A[[i, max_row]] = A[[max_row, i]]
            det *= -1  # Каждая перестановка меняет знак детерминанта
        # Обнуляем элементы ниже диагонали
        for j in range(i + 1, n):
            factor = A[j, i] / A[i, i]
            A[j, i:] -= factor * A[i, i:]
    # Произведение элементов на диагонали
    for i in range(n):
        det *= A[i, i]
    return det


# Функция для вычисления детерминанта с многопроцессностью
def parallel_determinant_worker(index_range, matrix):
    n = matrix.shape[0]
    A = matrix.astype(float)
    det = 1
    for i in range(index_range[0], index_range[1]):
        # Ищем максимальный элемент в текущем столбце
        max_row = np.argmax(np.abs(A[i:n, i])) + i
        if A[max_row, i] == 0:
            return 0
        if max_row != i:
            A[[i, max_row]] = A[[max_row, i]]
            det *= -1
        for j in range(i + 1, n):
            factor = A[j, i] / A[i, i]
            A[j, i:] -= factor * A[i, i:]
    return det


# Функция для параллельного вычисления детерминанта
def determinant_parallel(matrix, num_processes):
    n = matrix.shape[0]
    # Делим работу на блоки
    block_size = n // num_processes
    blocks = [(i * block_size, (i + 1) * block_size) for i in range(num_processes)]

    with Pool(processes=num_processes) as pool:
        results = pool.starmap(parallel_determinant_worker, [(block, matrix) for block in blocks])

    # Объединяем результаты
    det = sum(results)
    return det


# Функция для запуска бенчмарков
def run_benchmarks():
    matrix_sizes = [100, 300, 500,1000,1200]  # Размеры матриц
    for size in matrix_sizes:
        matrix = np.random.rand(size, size)  # Генерация случайной матрицы
        print(f"--- Benchmark для матрицы {size}x{size} ---")

        # Бенчмарк для последовательного вычисления детерминанта
        start_time = time.time()
        seq_det = determinant_gauss(matrix)
        seq_time = time.time() - start_time
        print(f"Последовательное время для {size}x{size}: {seq_time:.4f} секунд")

        # Бенчмарк для параллельного вычисления с разным числом потоков
        for num_processes in [1, 2, 4, 6, 8, 12, 16]:
            start_time = time.time()
            par_det = determinant_parallel(matrix, num_processes)
            par_time = time.time() - start_time
            speedup = seq_time / par_time if par_time > 0 else 0
            print(f"Параллельное время с {num_processes} потоками: {par_time:.4f} секунд, Ускорение: {speedup:.2f}")

# Запуск бенчмарков
if __name__ == '__main__':
    run_benchmarks()