import numpy as np
from concurrent.futures import ProcessPoolExecutor
import time

#Функция умножения матриц
def multi(A, B):
    n = len(A)
    k = len(B)
    C = np.zeros((n, n))
    
    for i in range(n):
        for j in range(n):
            C[i][j] = sum(A[i][p] * B[p][j] for p in range(k))
    
    return C

# Функция последовательного умножения матриц
def multi_sequential(A, B):
    n = len(A)
    C = np.zeros((n, n))
    for i in range(n):
        for k in range(n):
            temp = A[i][k]
            for j in range(n):
                C[i][j] += temp * B[k][j]
    return C

# Функция умножения матриц с numpy
def multi_numpy(A, B):
    return np.dot(A, B)

# Параллельное умножение матриц
def multi_parallel(A, B, num_threads):
    n = len(A)
    C = np.zeros((n, n))
    step = n // num_threads
    
    with ProcessPoolExecutor(max_workers=num_threads) as executor:
        futures = []
        for i in range(num_threads):
            start_row = i * step
            end_row = (i + 1) * step if i != num_threads - 1 else n

            a_slice = A[:, i*step: (i+1)*step]
            b_slice = B[start_row:end_row]

            futures.append(executor.submit(multi, a_slice, b_slice))

        for future in futures:
            C += future.result()

    return C

# Пример использования
if __name__ == "__main__":
    matrix_sizes = [100, 300, 500] 
    num_threads = [2, 4, 5, 10]
    for n in matrix_sizes:
        A = np.random.rand(n, n)
        B = np.random.rand(n, n)
        
        # Умножение с numpy
        start_np = time.time() 
        nump = multi_numpy(A, B)
        end_np = time.time() 
        print(f'Умножение матриц {n}x{n} последовательно с numpy: {(end_np - start_np):.6f} с.')

        # Последовательное умножение
        start_seq = time.time() 
        sequential = multi_sequential(A, B)
        end_seq = time.time() 
        print(f'Умножение матриц {n}x{n} последовательно: {(end_seq - start_seq):.6f} с.')

        # Параллельное умножение
        for thread in num_threads:
            start_par = time.time() 
            parallel = multi_parallel(A, B, thread)
            end_par = time.time() 
            print(f'Умножение матриц {n}x{n} параллельно для {thread} потоков: {(end_par - start_par):.3f} с.')

        print('')