diff --git a/bondarenko_max_lab_6/README.md b/bondarenko_max_lab_6/README.md new file mode 100644 index 0000000..f71aea3 --- /dev/null +++ b/bondarenko_max_lab_6/README.md @@ -0,0 +1,34 @@ +# Лабораторная работа 6 - Определение детерминанта матрицы с помощью параллельных вычислений +### ПИбд-42 || Бондаренко Максим + +# Описание работы + +## Задание +> [!NOTE] +> Кратко: реализовать нахождение детерминанта квадратной матрицы. +> +> Подробно: в лабораторной работе требуется сделать два алгоритма: обычный и параллельный (задание со * - реализовать это в рамках одного алгоритма). В параллельном алгоритме предусмотреть ручное задание количества потоков (число потоков = 1 как раз и реализует задание со *), каждый из которых будет выполнять нахождение отдельной группы множителей. + +## Краткое описание модулей и их работа +1. matrix.operations.js +- getMinor(matrix, row, col): Получает минор матрицы для заданной строки и столбца. +- determinant(matrix): Вычисляет детерминант матрицы рекурсивно (последовательный алгоритм). +- determinantParallel(matrix, numThreads): Вычисляет детерминант матрицы параллельно, распределяя задачи по заданному количеству потоков. + +2. det.worker.js +- Получает данные о своей части работы (миноры и знаки) и вычисляет частичный детерминант, который затем отправляет обратно главному потоку. + +3. benchmark.js +- generateMatrix(size): Генерирует случайную квадратную матрицу заданного размера. +- benchmark(): Выполняет бенчмарки для матриц различных размеров, сравнивая время выполнения последовательного и параллельного алгоритмов с различным количеством потоков. + +## Запуск +``` +node benchmark.js +``` + +## Результат работы +> [!IMPORTANT] +> ![benchmark.png](./benchmark.png) + +Ссылка на видео: https://cloud.mail.ru/public/9Bky/mjwK7bqBL \ No newline at end of file diff --git a/bondarenko_max_lab_6/benchmark.js b/bondarenko_max_lab_6/benchmark.js new file mode 100644 index 0000000..a9baf34 --- /dev/null +++ b/bondarenko_max_lab_6/benchmark.js @@ -0,0 +1,37 @@ +const { determinant, determinantParallel } = require('./matrix.operations'); + +function generateMatrix(size) { + const matrix = new Array(size); + for (let i = 0; i < size; i++) { + matrix[i] = new Array(size); + for (let j = 0; j < size; j++) { + matrix[i][j] = Math.floor(Math.random() * 20) - 10; + } + } + return matrix; +} + +async function benchmark() { + const sizes = [5, 7, 10]; + const numThreads = [2, 4, 8]; + + for (const size of sizes) { + const matrix = generateMatrix(size); + + console.log(`\nМатрица (${size}x${size}):`); + + console.time('Последовательно'); + const detSeq = determinant(matrix); + console.timeEnd('Последовательно'); + console.log(`Детерминант: ${detSeq}`); + + for (const threads of numThreads) { + console.time(`Параллельно (${threads} потоков)`); + const detPar = await determinantParallel(matrix, threads); + console.timeEnd(`Параллельно (${threads} потоков)`); + console.log(`Детерминант: ${detPar}`); + } + } +} + +benchmark(); diff --git a/bondarenko_max_lab_6/benchmark.png b/bondarenko_max_lab_6/benchmark.png new file mode 100644 index 0000000..86ab7f6 Binary files /dev/null and b/bondarenko_max_lab_6/benchmark.png differ diff --git a/bondarenko_max_lab_6/det.worker.js b/bondarenko_max_lab_6/det.worker.js new file mode 100644 index 0000000..84520b0 --- /dev/null +++ b/bondarenko_max_lab_6/det.worker.js @@ -0,0 +1,8 @@ +const { parentPort, workerData } = require('worker_threads'); +const { determinant } = require('./matrix.operations'); + +const partialDet = workerData.reduce((acc, job) => { + return acc + job.value * determinant(job.minor) * job.sign; +}, 0); + +parentPort.postMessage(partialDet); diff --git a/bondarenko_max_lab_6/matrix.operations.js b/bondarenko_max_lab_6/matrix.operations.js new file mode 100644 index 0000000..87591c7 --- /dev/null +++ b/bondarenko_max_lab_6/matrix.operations.js @@ -0,0 +1,60 @@ +const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); + +function getMinor(matrix, row, col) { + return matrix + .filter((_, i) => i !== row) + .map(row => row.filter((_, j) => j !== col)); +} + +function determinant(matrix) { + const n = matrix.length; + if (n === 1) return matrix[0][0]; + if (n === 2) return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]; + + let det = 0; + for (let j = 0; j < n; j++) { + det += matrix[0][j] * determinant(getMinor(matrix, 0, j)) * (j % 2 === 0 ? 1 : -1); + } + return det; +} + +function determinantParallel(matrix, numThreads) { + return new Promise((resolve, reject) => { + const n = matrix.length; + if (n === 1) return resolve(matrix[0][0]); + if (n === 2) return resolve(matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0]); + + let det = 0; + let completed = 0; + const jobs = []; + + for (let j = 0; j < n; j++) { + const sign = j % 2 === 0 ? 1 : -1; + const minor = getMinor(matrix, 0, j); + jobs.push({ minor, sign, value: matrix[0][j] }); + } + + const chunkSize = Math.ceil(jobs.length / numThreads); + const results = Array(numThreads).fill(0); + + for (let i = 0; i < numThreads; i++) { + const chunk = jobs.slice(i * chunkSize, (i + 1) * chunkSize); + const worker = new Worker('./det.worker.js', { workerData: chunk }); + + worker.on('message', (partialDet) => { + results[i] = partialDet; + completed++; + if (completed === numThreads) { + resolve(results.reduce((acc, val) => acc + val, 0)); + } + }); + + worker.on('error', reject); + worker.on('exit', (code) => { + if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`)); + }); + } + }); +} + +module.exports = { determinant, determinantParallel };