From 30e6b47386ac5e458a6deba0135d1ecc360859c0 Mon Sep 17 00:00:00 2001 From: Andrii Venher Date: Sun, 19 Nov 2023 14:55:07 +0100 Subject: [PATCH 1/5] Create measurement project --- dijkstra-prim-measurement/algorithms/graph.js | 57 +++++++++ .../algorithms/linkedList.js | 76 +++++++++++ .../algorithms/minHeap.js | 96 ++++++++++++++ .../algorithms/mstPrim.js | 103 +++++++++++++++ .../algorithms/spDijkstra.js | 99 +++++++++++++++ .../measurement/dataset.js | 107 ++++++++++++++++ .../measurement/measure.js | 119 ++++++++++++++++++ .../measurement/prepare.js | 31 +++++ dijkstra-prim-measurement/package-lock.json | 13 ++ dijkstra-prim-measurement/package.json | 12 ++ 10 files changed, 713 insertions(+) create mode 100644 dijkstra-prim-measurement/algorithms/graph.js create mode 100644 dijkstra-prim-measurement/algorithms/linkedList.js create mode 100644 dijkstra-prim-measurement/algorithms/minHeap.js create mode 100644 dijkstra-prim-measurement/algorithms/mstPrim.js create mode 100644 dijkstra-prim-measurement/algorithms/spDijkstra.js create mode 100644 dijkstra-prim-measurement/measurement/dataset.js create mode 100644 dijkstra-prim-measurement/measurement/measure.js create mode 100644 dijkstra-prim-measurement/measurement/prepare.js create mode 100644 dijkstra-prim-measurement/package-lock.json create mode 100644 dijkstra-prim-measurement/package.json diff --git a/dijkstra-prim-measurement/algorithms/graph.js b/dijkstra-prim-measurement/algorithms/graph.js new file mode 100644 index 0000000..ae33d74 --- /dev/null +++ b/dijkstra-prim-measurement/algorithms/graph.js @@ -0,0 +1,57 @@ +const createGraphFromComponent = function (nodes, edges) { + const mappedNodes = nodes.map((node) => node.id); + const mappedEdges = edges.map((edge) => { + return [edge.firstNode.id, edge.secondNode.id, edge.weight]; + }); + + return new createGraph(mappedNodes, mappedEdges); +}; + +const createGraph = function (nodes, edges) { + this.nodes = nodes; + this.edges = new Array(edges.length); + + for (let index = 0; index < edges.length; index++) { + const [from, to, weight] = edges[index]; + this.edges[index] = { + from: from, + to: to, + weight: weight, + }; + } +}; + +const buildAdjacencyList = (graph) => { + const adjacencyList = new Array(graph.nodes.length).fill(null); + + const createAdjacencyListEntry = (node, weight) => { + const entry = { + node: node, + weight: weight, + }; + return entry; + }; + + graph.edges.forEach((e) => { + if (!Array.isArray(adjacencyList[e.from])) { + adjacencyList[e.from] = new Array(); + } + if (adjacencyList[e.from].find((x) => x.node == e.to) == undefined) { + adjacencyList[e.from].push(createAdjacencyListEntry(e.to, e.weight)); + } + + if (!Array.isArray(adjacencyList[e.to])) { + adjacencyList[e.to] = new Array(); + } + if (adjacencyList[e.to].find((x) => x.node == e.from) == undefined) { + adjacencyList[e.to].push(createAdjacencyListEntry(e.from, e.weight)); + } + }); + + return adjacencyList; +}; + +module.exports = { + createGraph: createGraph, + buildAdjacencyList: buildAdjacencyList, +}; diff --git a/dijkstra-prim-measurement/algorithms/linkedList.js b/dijkstra-prim-measurement/algorithms/linkedList.js new file mode 100644 index 0000000..29c96e7 --- /dev/null +++ b/dijkstra-prim-measurement/algorithms/linkedList.js @@ -0,0 +1,76 @@ +const createLinkedList = function (capacity) { + const createNode = (key, value, next = null) => { + const node = { + key: key, + value: value, + next: next, + }; + + return node; + }; + + let head = null; + let currentSize = 0; + + this.isEmpty = () => currentSize == 0; + + this.insert = (key, value) => { + currentSize++; + const newNode = createNode(key, value); + + if (head == null) { + head = newNode; + } else { + let tmp = head; + + while (tmp.next != null) { + tmp = tmp.next; + } + + tmp.next = newNode; + } + }; + + this.extractMin = () => { + let minValue = Infinity; + let minNode = null; + let tmp = createNode(-1, -1, head); + + //find the min node + while (tmp.next) { + if (tmp.next.value < minValue) { + minValue = tmp.next.value; + minNode = tmp; + } + + tmp = tmp.next; + } + tmp = minNode.next; + + //delete the min node + if (minNode.next == head) { + head = head.next; + } else { + minNode.next = minNode.next.next; + } + --currentSize; + + return tmp; + }; + + this.decreaseKey = (key, newValue) => { + let tmp = createNode(-1, -1, head); + + while (tmp.next.key != key) { + tmp = tmp.next; + } + + tmp.next.value = newValue; + }; +}; + +module.exports = { + createLinkedList, + default: createLinkedList, +}; + diff --git a/dijkstra-prim-measurement/algorithms/minHeap.js b/dijkstra-prim-measurement/algorithms/minHeap.js new file mode 100644 index 0000000..05d88e6 --- /dev/null +++ b/dijkstra-prim-measurement/algorithms/minHeap.js @@ -0,0 +1,96 @@ +const createMinHeap = function (capacity) { + const createNode = (key, value) => { + const node = { + key: key, + value: value, + }; + return node; + }; + + let currentSize = 0; + const nodes = new Array(capacity + 1); + const keysIndices = new Array(capacity); + + nodes[0] = createNode(-1, -Infinity); + + const swapNodes = (index1, index2) => { + [nodes[index1], nodes[index2]] = [nodes[index2], nodes[index1]]; + }; + + const bubbleUp = (index) => { + let parentIndex = Math.floor(index / 2); + let currentIndex = index; + while ( + currentIndex > 0 && + nodes[parentIndex].value > nodes[currentIndex].value + ) { + const parentNode = nodes[parentIndex]; + const currentNode = nodes[currentIndex]; + + keysIndices[currentNode.key] = parentIndex; + keysIndices[parentNode.key] = currentIndex; + swapNodes(currentIndex, parentIndex); + currentIndex = parentIndex; + parentIndex = Math.floor(parentIndex / 2); + } + }; + + const sinkDown = (k) => { + let smallest = k; + let leftChildIndex = 2 * k; + let rightChildIndex = 2 * k + 1; + if ( + leftChildIndex < currentSize && + nodes[smallest].value > nodes[leftChildIndex].value + ) { + smallest = leftChildIndex; + } + if ( + rightChildIndex < currentSize && + nodes[smallest].value > nodes[rightChildIndex].value + ) { + smallest = rightChildIndex; + } + if (smallest != k) { + const smallestNode = nodes[smallest]; + const kNode = nodes[k]; + + keysIndices[smallestNode.key] = k; + keysIndices[kNode.key] = smallest; + swapNodes(k, smallest); + sinkDown(smallest); + } + }; + + this.isEmpty = () => currentSize == 0; + + this.insert = (key, value) => { + currentSize++; + nodes[currentSize] = createNode(key, value); + keysIndices[key] = currentSize; + bubbleUp(currentSize); + }; + + this.extractMin = () => { + const min = nodes[1]; + const last = nodes[currentSize]; + keysIndices[last.key] = 1; + nodes[1] = last; + nodes[currentSize] = null; + sinkDown(1); + currentSize--; + return min; + }; + + this.decreaseKey = (key, newValue) => { + const index = keysIndices[key]; + const node = nodes[index]; + node.value = newValue; + bubbleUp(index); + }; +}; + +module.exports = { + createMinHeap, + default: createMinHeap, +}; diff --git a/dijkstra-prim-measurement/algorithms/mstPrim.js b/dijkstra-prim-measurement/algorithms/mstPrim.js new file mode 100644 index 0000000..0c5c3f1 --- /dev/null +++ b/dijkstra-prim-measurement/algorithms/mstPrim.js @@ -0,0 +1,103 @@ +const { createMinHeap } = require("./minHeap"); +const { createLinkedList } = require("./linkedList"); +const { createGraph, buildAdjacencyList } = require("./graph"); + +const prim = (adjacencyList, createFringe) => { + // const adjacencyList = buildAdjacencyList(graph); + + const nodesCount = adjacencyList.length; + + const fringe = new createFringe(nodesCount); + const isInHeap = new Array(nodesCount); + const results = new Array(nodesCount); + const keys = new Array(nodesCount); + + // Insert node 0 with value 0 + fringe.insert(0, 0); + isInHeap[0] = true; + keys[0] = Infinity; + results[0] = { + parent: -1, + weight: null, + }; + + for (let index = 1; index < nodesCount; index++) { + isInHeap[index] = true; + keys[index] = Infinity; + fringe.insert(index, Infinity); + } + + while (!fringe.isEmpty()) { + const extractedNode = fringe.extractMin(); + isInHeap[extractedNode.key] = false; + + const neightbours = adjacencyList[extractedNode.key]; + neightbours.forEach((n) => { + if (isInHeap[n.node]) { + if (keys[n.node] > n.weight) { + fringe.decreaseKey(n.node, n.weight); + keys[n.node] = n.weight; + results[n.node] = { + from: extractedNode.key, + to: n.node, + weight: n.weight, + }; + } + } + }); + } + + // results.sort((a, b) => a.weight - b.weight); + + let mstTotal = 0; + + for (let index = 1; index < nodesCount; index++) { + mstTotal += results[index].weight; + } + + return { + steps: results, + mstTotalWeight: mstTotal, + }; +}; + +const displayPrimResult = (result) => { + console.log("MST steps:"); + + for (let index = 1; index < result.steps.length; index++) { + console.log( + `From ${result.steps[index].from} to ${result.steps[index].to} with weight ${result.steps[index].weight}` + ); + } + + console.log(`MST total weight: ${result.mstTotalWeight}`); +}; + +const computeMst = () => { + console.log("------- MST PRIM BEGIN -------"); + + const graph = new createGraph( + [0, 1, 2, 3, 4], + [ + [0, 1, 5], + [0, 2, 1], + [0, 3, 5], + [1, 2, 3], + [1, 4, 4], + [2, 4, 8], + [3, 4, 9], + ] + ); + + const result = prim(graph); + + displayPrimResult(result); + + console.log("------- MST PRIM END -------"); +}; + +// computeMst(); + +module.exports = { + prim: prim, +}; diff --git a/dijkstra-prim-measurement/algorithms/spDijkstra.js b/dijkstra-prim-measurement/algorithms/spDijkstra.js new file mode 100644 index 0000000..ef5086b --- /dev/null +++ b/dijkstra-prim-measurement/algorithms/spDijkstra.js @@ -0,0 +1,99 @@ +const { createMinHeap } = require("./minHeap"); +const { createLinkedList } = require("./linkedList"); +const { createGraph, buildAdjacencyList } = require("./graph"); + +const dijkstra = (adjacencyList) => { + // const adjacencyList = buildAdjacencyList(graph); + const nodesCount = adjacencyList.length; + + const startNode = 0; + const finishNode = nodesCount - 1; + + const fringe = new createMinHeap(nodesCount); + const keys = new Array(nodesCount); + const parents = new Array(nodesCount); + + fringe.insert(0, 0); + keys[0] = 0; + + for (let i = 1; i < nodesCount; ++i) { + fringe.insert(i, Infinity); + keys[i] = Infinity; + } + + while (!fringe.isEmpty()) { + const node = fringe.extractMin(); + const neighbours = adjacencyList[node.key]; + + if (node.key == finishNode) { + break; + } + + neighbours.forEach((neighbour) => { + const newWeight = keys[node.key] + neighbour.weight; + if (newWeight < keys[neighbour.node]) { + keys[neighbour.node] = newWeight; + parents[neighbour.node] = { key: node.key, weight: neighbour.weight }; + fringe.decreaseKey(neighbour.node, newWeight); + } + }); + } + + const sp = new Array(); + let node = finishNode; + + while (node != startNode) { + sp.push({ + from: parents[node].key, + to: node, + weight: parents[node].weight, + }); + node = parents[node].key; + } + + return { + steps: sp.reverse(), + spTotalWeight: keys[finishNode], + }; +}; + +const displayDijkstraResult = (result) => { + console.log("SP steps:"); + + for (let index = 0; index < result.steps.length; index++) { + console.log( + `From ${result.steps[index].from} to ${result.steps[index].to} with weight ${result.steps[index].weight}` + ); + } + + console.log(`SP total weight: ${result.spTotalWeight}`); +}; + +const computeSp = () => { + console.log("------- SP DIJKSTRA BEGIN -------"); + + const graph = new createGraph( + [0, 1, 2, 3, 4, 5], + [ + [0, 1, 4], + [0, 2, 5], + [1, 2, 11], + [1, 3, 9], + [1, 4, 7], + [2, 4, 3], + [3, 4, 13], + [3, 5, 2], + [4, 5, 6], + ] + ); + + const result = dijkstra(graph, 0, 5); + + displayDijkstraResult(result); + + console.log("------- SP DIJKSTRA END -------"); +}; + +module.exports = { + dijkstra: dijkstra, +}; diff --git a/dijkstra-prim-measurement/measurement/dataset.js b/dijkstra-prim-measurement/measurement/dataset.js new file mode 100644 index 0000000..8324342 --- /dev/null +++ b/dijkstra-prim-measurement/measurement/dataset.js @@ -0,0 +1,107 @@ +const fs = require("fs"); +const path = require("path"); + +const directory = "datasets"; + +const generateRandomNumber = (a, b) => Math.floor(Math.random() * (b - a) + a); + +const createAdjacencyListEntry = (node, weight) => { + const entry = { + node: node, + weight: weight, + }; + return entry; +}; + +const generateAdjacencyList = ( + nodesCount, + probability, + minWeight, + maxWeight +) => { + const adjacencyList = new Array(nodesCount); + + for (let i = 0; i < nodesCount; i++) { + adjacencyList[i] = new Array(); + } + + for (let i = 0; i < nodesCount; i++) { + for (let j = i + 1; j < nodesCount; j++) { + if (Math.random() < probability) { + const weight = generateRandomNumber(minWeight, maxWeight); + adjacencyList[i].push(createAdjacencyListEntry(j, weight)); + adjacencyList[j].push(createAdjacencyListEntry(i, weight)); + } + } + } + + return adjacencyList; +}; + +const ensureDirectoryExists = () => { + if (!fs.existsSync(directory)) { + fs.mkdirSync(directory); + } +}; + +const generateFilePath = (nodesCount) => + path.join(directory, `al_${nodesCount}.txt`); + +const saveAdjacencyList = (dataset) => { + ensureDirectoryExists(); + const writer = fs.createWriteStream(generateFilePath(dataset.length), { + flags: "w", + }); + + writer.write(`${dataset.length}\n`); + + dataset.forEach((row) => { + row.forEach((element) => { + const chunk = `${element.node},${element.weight} `; + writer.write(chunk); + }); + writer.write("\n"); + }); + + writer.close(); +}; + +const readAdjacencyList = (nodesCount) => { + ensureDirectoryExists(); + const rawDataset = fs.readFileSync(generateFilePath(nodesCount), "utf-8"); + + const lines = rawDataset.trim().split("\n"); + + const adjacencyList = new Array(nodesCount); + + for (let i = 1; i < lines.length; i++) { + const row = lines[i] + .trim() + .split(" ") + .map((chunk) => { + const [node, weight] = chunk.split(","); + return createAdjacencyListEntry( + Number.parseInt(node), + Number.parseInt(weight) + ); + }); + adjacencyList[i - 1] = row; + } + + return adjacencyList; +}; + +const getDatasets = () => { + ensureDirectoryExists(); + const datasets = fs.readdirSync(directory); + return datasets + .map((x) => Number.parseInt(x.replace(/^al_/, "").replace(/.txt$/))) + .sort((a, b) => a - b); +}; + +module.exports = { + generateAdjacencyList: generateAdjacencyList, + saveAdjacencyList: saveAdjacencyList, + readAdjacencyList: readAdjacencyList, + getDatasets: getDatasets, +}; diff --git a/dijkstra-prim-measurement/measurement/measure.js b/dijkstra-prim-measurement/measurement/measure.js new file mode 100644 index 0000000..3488c12 --- /dev/null +++ b/dijkstra-prim-measurement/measurement/measure.js @@ -0,0 +1,119 @@ +const fs = require("fs"); +const { prim } = require("../algorithms/mstPrim"); +const { dijkstra } = require("../algorithms/spDijkstra"); +const { generateAdjacencyList } = require("./dataset"); +const path = require("path"); +const { createMinHeap } = require("../algorithms/minHeap"); +const { createLinkedList } = require("../algorithms/linkedList"); + +const args = process.argv.slice(2); + +const config = { + algorithm: "prim", + startNodeCount: Number.parseInt(args[0] ?? 1000), + finishNodeCount: Number.parseInt(args[1] ?? 5000), + stepNodeCount: Number.parseInt(args[2] ?? 100), + edgeProbability: 1, + minEdgeWeight: 1, + maxEdgeWeght: 15, + measurementSteps: 10, +}; + +console.log(config); + +const measureTime = (func) => { + const before = performance.now(); + func(); + const after = performance.now(); + return after - before; +}; + +const measureAverageTime = (func) => { + const deltas = []; + + for ( + let measurementStep = 0; + measurementStep < config.measurementSteps; + measurementStep++ + ) { + const delta = measureTime(func); + deltas.push(delta); + } + + return Math.floor(deltas.reduce((a, b) => a + b, 0) / deltas.length); +}; + +const saveResults = (results, fringe) => { + const directory = "results"; + + if (!fs.existsSync(directory)) { + fs.mkdirSync(directory); + } + + const randomTag = (Math.random() + 1).toString(36).substring(7); + const filename = `${config.algorithm}_${fringe}_${config.startNodeCount}_${config.finishNodeCount}_${config.stepNodeCount}_${config.edgeProbability}_${config.minEdgeWeight}_${config.maxEdgeWeght}_${randomTag}.txt`; + + const writer = fs.createWriteStream(path.join(directory, filename), { + flags: "w", + }); + + results.forEach((x) => { + writer.write(`${x.nodesCount} ${x.delta}\n`); + }); + + writer.close(); + + return filename; +}; + +const measure = () => { + console.log("Started measurement"); + + const resultsMinHeap = []; + const resultsLinkedList = []; + + for ( + let nodesCount = config.startNodeCount; + nodesCount <= config.finishNodeCount; + nodesCount += config.stepNodeCount + ) { + // console.log(`Started step ${nodesCount}`); + + const adjacencyList = generateAdjacencyList( + nodesCount, + config.edgeProbability, + config.minEdgeWeight, + config.maxEdgeWeght + ); + // console.log("Generated adjacency list"); + + const deltaMinHeap = measureAverageTime(() => + prim(adjacencyList, createMinHeap) + ); + const deltaLinkedList = measureAverageTime(() => + prim(adjacencyList, createLinkedList) + ); + + resultsMinHeap.push({ + nodesCount: nodesCount, + delta: deltaMinHeap, + }); + resultsLinkedList.push({ + nodesCount: nodesCount, + delta: deltaLinkedList, + }); + + console.log( + `Result (min heap): nodesCount = ${nodesCount}, delta = ${deltaMinHeap} ms` + ); + console.log( + `Result (linked list): nodesCount = ${nodesCount}, delta = ${deltaLinkedList} ms` + ); + } + + const filenameMinHeap = saveResults(resultsMinHeap, "minheap"); + const filenameLinkedList = saveResults(resultsLinkedList, "linkedlist"); + console.log(`Saved result to ${filenameMinHeap} and ${filenameLinkedList}`); +}; + +measure(); diff --git a/dijkstra-prim-measurement/measurement/prepare.js b/dijkstra-prim-measurement/measurement/prepare.js new file mode 100644 index 0000000..591ffd7 --- /dev/null +++ b/dijkstra-prim-measurement/measurement/prepare.js @@ -0,0 +1,31 @@ +const { generateAdjacencyList, saveAdjacencyList } = require("./dataset"); + +const args = process.argv.slice(2); + +console.log(args); + +const startNodeCount = Number.parseInt(args[0] ?? 1000); +const finishNodeCount = Number.parseInt(args[1] ?? 5000); +const stepNodeCount = Number.parseInt(args[2] ?? 100); + +const edgeProbability = 0.5; + +const minEdgeWeight = 1; +const maxEdgeWeght = 15; + +for ( + let nodesCount = startNodeCount; + nodesCount <= finishNodeCount; + nodesCount += stepNodeCount +) { + const dataset = generateAdjacencyList( + nodesCount, + edgeProbability, + minEdgeWeight, + maxEdgeWeght + ); + // saveAdjacencyList(dataset); + console.log( + `Prepared an adjacency list for ${nodesCount} nodes with edge probability of ${edgeProbability}` + ); +} diff --git a/dijkstra-prim-measurement/package-lock.json b/dijkstra-prim-measurement/package-lock.json new file mode 100644 index 0000000..ef72f32 --- /dev/null +++ b/dijkstra-prim-measurement/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "benchmark", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "benchmark", + "version": "1.0.0", + "license": "ISC" + } + } +} diff --git a/dijkstra-prim-measurement/package.json b/dijkstra-prim-measurement/package.json new file mode 100644 index 0000000..edb1f4c --- /dev/null +++ b/dijkstra-prim-measurement/package.json @@ -0,0 +1,12 @@ +{ + "name": "benchmark", + "version": "1.0.0", + "description": "", + "main": "test.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} From 83f4ed8dca02e73adf265348fe095f745d678033 Mon Sep 17 00:00:00 2001 From: Andrii Venher Date: Sun, 19 Nov 2023 17:56:47 +0100 Subject: [PATCH 2/5] Refactor code to use ES modules in measurement project --- dijkstra-prim-measurement/algorithms/graph.js | 57 ---------- .../algorithms/linkedList.js | 76 ------------- .../algorithms/minHeap.js | 96 ---------------- .../algorithms/mstPrim.js | 103 ------------------ .../algorithms/spDijkstra.js | 99 ----------------- .../measurement/dataset.js | 23 ++-- .../measurement/measure.js | 16 +-- .../measurement/prepare.js | 4 +- dijkstra-prim-measurement/package.json | 1 + .../algorithms/{spDijkstra.js => dijkstra.js} | 22 ++-- .../src/algorithms/graph.js | 63 ++++++++--- .../src/algorithms/{mstPrim.js => prim.js} | 31 ++++-- .../src/components/Navbar/Navbar.jsx | 11 +- 13 files changed, 103 insertions(+), 499 deletions(-) delete mode 100644 dijkstra-prim-measurement/algorithms/graph.js delete mode 100644 dijkstra-prim-measurement/algorithms/linkedList.js delete mode 100644 dijkstra-prim-measurement/algorithms/minHeap.js delete mode 100644 dijkstra-prim-measurement/algorithms/mstPrim.js delete mode 100644 dijkstra-prim-measurement/algorithms/spDijkstra.js rename dijkstra-prim-visualization/src/algorithms/{spDijkstra.js => dijkstra.js} (81%) rename dijkstra-prim-visualization/src/algorithms/{mstPrim.js => prim.js} (75%) diff --git a/dijkstra-prim-measurement/algorithms/graph.js b/dijkstra-prim-measurement/algorithms/graph.js deleted file mode 100644 index ae33d74..0000000 --- a/dijkstra-prim-measurement/algorithms/graph.js +++ /dev/null @@ -1,57 +0,0 @@ -const createGraphFromComponent = function (nodes, edges) { - const mappedNodes = nodes.map((node) => node.id); - const mappedEdges = edges.map((edge) => { - return [edge.firstNode.id, edge.secondNode.id, edge.weight]; - }); - - return new createGraph(mappedNodes, mappedEdges); -}; - -const createGraph = function (nodes, edges) { - this.nodes = nodes; - this.edges = new Array(edges.length); - - for (let index = 0; index < edges.length; index++) { - const [from, to, weight] = edges[index]; - this.edges[index] = { - from: from, - to: to, - weight: weight, - }; - } -}; - -const buildAdjacencyList = (graph) => { - const adjacencyList = new Array(graph.nodes.length).fill(null); - - const createAdjacencyListEntry = (node, weight) => { - const entry = { - node: node, - weight: weight, - }; - return entry; - }; - - graph.edges.forEach((e) => { - if (!Array.isArray(adjacencyList[e.from])) { - adjacencyList[e.from] = new Array(); - } - if (adjacencyList[e.from].find((x) => x.node == e.to) == undefined) { - adjacencyList[e.from].push(createAdjacencyListEntry(e.to, e.weight)); - } - - if (!Array.isArray(adjacencyList[e.to])) { - adjacencyList[e.to] = new Array(); - } - if (adjacencyList[e.to].find((x) => x.node == e.from) == undefined) { - adjacencyList[e.to].push(createAdjacencyListEntry(e.from, e.weight)); - } - }); - - return adjacencyList; -}; - -module.exports = { - createGraph: createGraph, - buildAdjacencyList: buildAdjacencyList, -}; diff --git a/dijkstra-prim-measurement/algorithms/linkedList.js b/dijkstra-prim-measurement/algorithms/linkedList.js deleted file mode 100644 index 29c96e7..0000000 --- a/dijkstra-prim-measurement/algorithms/linkedList.js +++ /dev/null @@ -1,76 +0,0 @@ -const createLinkedList = function (capacity) { - const createNode = (key, value, next = null) => { - const node = { - key: key, - value: value, - next: next, - }; - - return node; - }; - - let head = null; - let currentSize = 0; - - this.isEmpty = () => currentSize == 0; - - this.insert = (key, value) => { - currentSize++; - const newNode = createNode(key, value); - - if (head == null) { - head = newNode; - } else { - let tmp = head; - - while (tmp.next != null) { - tmp = tmp.next; - } - - tmp.next = newNode; - } - }; - - this.extractMin = () => { - let minValue = Infinity; - let minNode = null; - let tmp = createNode(-1, -1, head); - - //find the min node - while (tmp.next) { - if (tmp.next.value < minValue) { - minValue = tmp.next.value; - minNode = tmp; - } - - tmp = tmp.next; - } - tmp = minNode.next; - - //delete the min node - if (minNode.next == head) { - head = head.next; - } else { - minNode.next = minNode.next.next; - } - --currentSize; - - return tmp; - }; - - this.decreaseKey = (key, newValue) => { - let tmp = createNode(-1, -1, head); - - while (tmp.next.key != key) { - tmp = tmp.next; - } - - tmp.next.value = newValue; - }; -}; - -module.exports = { - createLinkedList, - default: createLinkedList, -}; - diff --git a/dijkstra-prim-measurement/algorithms/minHeap.js b/dijkstra-prim-measurement/algorithms/minHeap.js deleted file mode 100644 index 05d88e6..0000000 --- a/dijkstra-prim-measurement/algorithms/minHeap.js +++ /dev/null @@ -1,96 +0,0 @@ -const createMinHeap = function (capacity) { - const createNode = (key, value) => { - const node = { - key: key, - value: value, - }; - return node; - }; - - let currentSize = 0; - const nodes = new Array(capacity + 1); - const keysIndices = new Array(capacity); - - nodes[0] = createNode(-1, -Infinity); - - const swapNodes = (index1, index2) => { - [nodes[index1], nodes[index2]] = [nodes[index2], nodes[index1]]; - }; - - const bubbleUp = (index) => { - let parentIndex = Math.floor(index / 2); - let currentIndex = index; - while ( - currentIndex > 0 && - nodes[parentIndex].value > nodes[currentIndex].value - ) { - const parentNode = nodes[parentIndex]; - const currentNode = nodes[currentIndex]; - - keysIndices[currentNode.key] = parentIndex; - keysIndices[parentNode.key] = currentIndex; - swapNodes(currentIndex, parentIndex); - currentIndex = parentIndex; - parentIndex = Math.floor(parentIndex / 2); - } - }; - - const sinkDown = (k) => { - let smallest = k; - let leftChildIndex = 2 * k; - let rightChildIndex = 2 * k + 1; - if ( - leftChildIndex < currentSize && - nodes[smallest].value > nodes[leftChildIndex].value - ) { - smallest = leftChildIndex; - } - if ( - rightChildIndex < currentSize && - nodes[smallest].value > nodes[rightChildIndex].value - ) { - smallest = rightChildIndex; - } - if (smallest != k) { - const smallestNode = nodes[smallest]; - const kNode = nodes[k]; - - keysIndices[smallestNode.key] = k; - keysIndices[kNode.key] = smallest; - swapNodes(k, smallest); - sinkDown(smallest); - } - }; - - this.isEmpty = () => currentSize == 0; - - this.insert = (key, value) => { - currentSize++; - nodes[currentSize] = createNode(key, value); - keysIndices[key] = currentSize; - bubbleUp(currentSize); - }; - - this.extractMin = () => { - const min = nodes[1]; - const last = nodes[currentSize]; - keysIndices[last.key] = 1; - nodes[1] = last; - nodes[currentSize] = null; - sinkDown(1); - currentSize--; - return min; - }; - - this.decreaseKey = (key, newValue) => { - const index = keysIndices[key]; - const node = nodes[index]; - node.value = newValue; - bubbleUp(index); - }; -}; - -module.exports = { - createMinHeap, - default: createMinHeap, -}; diff --git a/dijkstra-prim-measurement/algorithms/mstPrim.js b/dijkstra-prim-measurement/algorithms/mstPrim.js deleted file mode 100644 index 0c5c3f1..0000000 --- a/dijkstra-prim-measurement/algorithms/mstPrim.js +++ /dev/null @@ -1,103 +0,0 @@ -const { createMinHeap } = require("./minHeap"); -const { createLinkedList } = require("./linkedList"); -const { createGraph, buildAdjacencyList } = require("./graph"); - -const prim = (adjacencyList, createFringe) => { - // const adjacencyList = buildAdjacencyList(graph); - - const nodesCount = adjacencyList.length; - - const fringe = new createFringe(nodesCount); - const isInHeap = new Array(nodesCount); - const results = new Array(nodesCount); - const keys = new Array(nodesCount); - - // Insert node 0 with value 0 - fringe.insert(0, 0); - isInHeap[0] = true; - keys[0] = Infinity; - results[0] = { - parent: -1, - weight: null, - }; - - for (let index = 1; index < nodesCount; index++) { - isInHeap[index] = true; - keys[index] = Infinity; - fringe.insert(index, Infinity); - } - - while (!fringe.isEmpty()) { - const extractedNode = fringe.extractMin(); - isInHeap[extractedNode.key] = false; - - const neightbours = adjacencyList[extractedNode.key]; - neightbours.forEach((n) => { - if (isInHeap[n.node]) { - if (keys[n.node] > n.weight) { - fringe.decreaseKey(n.node, n.weight); - keys[n.node] = n.weight; - results[n.node] = { - from: extractedNode.key, - to: n.node, - weight: n.weight, - }; - } - } - }); - } - - // results.sort((a, b) => a.weight - b.weight); - - let mstTotal = 0; - - for (let index = 1; index < nodesCount; index++) { - mstTotal += results[index].weight; - } - - return { - steps: results, - mstTotalWeight: mstTotal, - }; -}; - -const displayPrimResult = (result) => { - console.log("MST steps:"); - - for (let index = 1; index < result.steps.length; index++) { - console.log( - `From ${result.steps[index].from} to ${result.steps[index].to} with weight ${result.steps[index].weight}` - ); - } - - console.log(`MST total weight: ${result.mstTotalWeight}`); -}; - -const computeMst = () => { - console.log("------- MST PRIM BEGIN -------"); - - const graph = new createGraph( - [0, 1, 2, 3, 4], - [ - [0, 1, 5], - [0, 2, 1], - [0, 3, 5], - [1, 2, 3], - [1, 4, 4], - [2, 4, 8], - [3, 4, 9], - ] - ); - - const result = prim(graph); - - displayPrimResult(result); - - console.log("------- MST PRIM END -------"); -}; - -// computeMst(); - -module.exports = { - prim: prim, -}; diff --git a/dijkstra-prim-measurement/algorithms/spDijkstra.js b/dijkstra-prim-measurement/algorithms/spDijkstra.js deleted file mode 100644 index ef5086b..0000000 --- a/dijkstra-prim-measurement/algorithms/spDijkstra.js +++ /dev/null @@ -1,99 +0,0 @@ -const { createMinHeap } = require("./minHeap"); -const { createLinkedList } = require("./linkedList"); -const { createGraph, buildAdjacencyList } = require("./graph"); - -const dijkstra = (adjacencyList) => { - // const adjacencyList = buildAdjacencyList(graph); - const nodesCount = adjacencyList.length; - - const startNode = 0; - const finishNode = nodesCount - 1; - - const fringe = new createMinHeap(nodesCount); - const keys = new Array(nodesCount); - const parents = new Array(nodesCount); - - fringe.insert(0, 0); - keys[0] = 0; - - for (let i = 1; i < nodesCount; ++i) { - fringe.insert(i, Infinity); - keys[i] = Infinity; - } - - while (!fringe.isEmpty()) { - const node = fringe.extractMin(); - const neighbours = adjacencyList[node.key]; - - if (node.key == finishNode) { - break; - } - - neighbours.forEach((neighbour) => { - const newWeight = keys[node.key] + neighbour.weight; - if (newWeight < keys[neighbour.node]) { - keys[neighbour.node] = newWeight; - parents[neighbour.node] = { key: node.key, weight: neighbour.weight }; - fringe.decreaseKey(neighbour.node, newWeight); - } - }); - } - - const sp = new Array(); - let node = finishNode; - - while (node != startNode) { - sp.push({ - from: parents[node].key, - to: node, - weight: parents[node].weight, - }); - node = parents[node].key; - } - - return { - steps: sp.reverse(), - spTotalWeight: keys[finishNode], - }; -}; - -const displayDijkstraResult = (result) => { - console.log("SP steps:"); - - for (let index = 0; index < result.steps.length; index++) { - console.log( - `From ${result.steps[index].from} to ${result.steps[index].to} with weight ${result.steps[index].weight}` - ); - } - - console.log(`SP total weight: ${result.spTotalWeight}`); -}; - -const computeSp = () => { - console.log("------- SP DIJKSTRA BEGIN -------"); - - const graph = new createGraph( - [0, 1, 2, 3, 4, 5], - [ - [0, 1, 4], - [0, 2, 5], - [1, 2, 11], - [1, 3, 9], - [1, 4, 7], - [2, 4, 3], - [3, 4, 13], - [3, 5, 2], - [4, 5, 6], - ] - ); - - const result = dijkstra(graph, 0, 5); - - displayDijkstraResult(result); - - console.log("------- SP DIJKSTRA END -------"); -}; - -module.exports = { - dijkstra: dijkstra, -}; diff --git a/dijkstra-prim-measurement/measurement/dataset.js b/dijkstra-prim-measurement/measurement/dataset.js index 8324342..717c1ab 100644 --- a/dijkstra-prim-measurement/measurement/dataset.js +++ b/dijkstra-prim-measurement/measurement/dataset.js @@ -1,18 +1,11 @@ -const fs = require("fs"); -const path = require("path"); +import fs from "fs"; +import path from "path"; +import { createAdjacencyListEntry } from "../../dijkstra-prim-visualization/src/algorithms/graph.js"; const directory = "datasets"; const generateRandomNumber = (a, b) => Math.floor(Math.random() * (b - a) + a); -const createAdjacencyListEntry = (node, weight) => { - const entry = { - node: node, - weight: weight, - }; - return entry; -}; - const generateAdjacencyList = ( nodesCount, probability, @@ -99,9 +92,9 @@ const getDatasets = () => { .sort((a, b) => a - b); }; -module.exports = { - generateAdjacencyList: generateAdjacencyList, - saveAdjacencyList: saveAdjacencyList, - readAdjacencyList: readAdjacencyList, - getDatasets: getDatasets, +export { + generateAdjacencyList, + saveAdjacencyList, + readAdjacencyList, + getDatasets, }; diff --git a/dijkstra-prim-measurement/measurement/measure.js b/dijkstra-prim-measurement/measurement/measure.js index 3488c12..e8ee5dc 100644 --- a/dijkstra-prim-measurement/measurement/measure.js +++ b/dijkstra-prim-measurement/measurement/measure.js @@ -1,10 +1,9 @@ -const fs = require("fs"); -const { prim } = require("../algorithms/mstPrim"); -const { dijkstra } = require("../algorithms/spDijkstra"); -const { generateAdjacencyList } = require("./dataset"); -const path = require("path"); -const { createMinHeap } = require("../algorithms/minHeap"); -const { createLinkedList } = require("../algorithms/linkedList"); +import fs from "fs"; +import path from "path"; +import { generateAdjacencyList } from "./dataset.js"; +import { prim } from "../../dijkstra-prim-visualization/src/algorithms/prim.js"; +import createMinHeap from "../../dijkstra-prim-visualization/src/algorithms/minHeap.js"; +import createLinkedList from "../../dijkstra-prim-visualization/src/algorithms/linkedList.js"; const args = process.argv.slice(2); @@ -77,15 +76,12 @@ const measure = () => { nodesCount <= config.finishNodeCount; nodesCount += config.stepNodeCount ) { - // console.log(`Started step ${nodesCount}`); - const adjacencyList = generateAdjacencyList( nodesCount, config.edgeProbability, config.minEdgeWeight, config.maxEdgeWeght ); - // console.log("Generated adjacency list"); const deltaMinHeap = measureAverageTime(() => prim(adjacencyList, createMinHeap) diff --git a/dijkstra-prim-measurement/measurement/prepare.js b/dijkstra-prim-measurement/measurement/prepare.js index 591ffd7..5e3a6ad 100644 --- a/dijkstra-prim-measurement/measurement/prepare.js +++ b/dijkstra-prim-measurement/measurement/prepare.js @@ -1,4 +1,4 @@ -const { generateAdjacencyList, saveAdjacencyList } = require("./dataset"); +import { generateAdjacencyList, saveAdjacencyList } from "./dataset.js"; const args = process.argv.slice(2); @@ -24,7 +24,7 @@ for ( minEdgeWeight, maxEdgeWeght ); - // saveAdjacencyList(dataset); + saveAdjacencyList(dataset); console.log( `Prepared an adjacency list for ${nodesCount} nodes with edge probability of ${edgeProbability}` ); diff --git a/dijkstra-prim-measurement/package.json b/dijkstra-prim-measurement/package.json index edb1f4c..76ecacc 100644 --- a/dijkstra-prim-measurement/package.json +++ b/dijkstra-prim-measurement/package.json @@ -1,6 +1,7 @@ { "name": "benchmark", "version": "1.0.0", + "type": "module", "description": "", "main": "test.js", "scripts": { diff --git a/dijkstra-prim-visualization/src/algorithms/spDijkstra.js b/dijkstra-prim-visualization/src/algorithms/dijkstra.js similarity index 81% rename from dijkstra-prim-visualization/src/algorithms/spDijkstra.js rename to dijkstra-prim-visualization/src/algorithms/dijkstra.js index 5cfb709..91c6750 100644 --- a/dijkstra-prim-visualization/src/algorithms/spDijkstra.js +++ b/dijkstra-prim-visualization/src/algorithms/dijkstra.js @@ -1,15 +1,16 @@ -import { createGraphFromComponent, buildAdjacencyList } from "./graph.js"; +import { + buildAdjacencyList, + buildAdjacencyListFromComponent, +} from "./graph.js"; import createMinHeap from "./minHeap.js"; -import createLinkedList from "./linkedList.js"; -const dijkstra = (graph) => { - const adjacencyList = buildAdjacencyList(graph); +const dijkstra = (adjacencyList, createFringe) => { const nodesCount = adjacencyList.length; const startNode = 0; const finishNode = nodesCount - 1; - const fringe = new createLinkedList(nodesCount); + const fringe = new createFringe(nodesCount); const keys = new Array(nodesCount); const parents = new Array(nodesCount); @@ -57,6 +58,11 @@ const dijkstra = (graph) => { }; }; +const dijkstraWrapper = (nodes, edges) => { + const adjacencyList = buildAdjacencyListFromComponent(nodes, edges); + return dijkstra(adjacencyList, createMinHeap); +}; + const displayDijkstraResult = (result) => { console.log("SP steps:"); @@ -87,11 +93,13 @@ const computeSp = () => { ], ); - const result = dijkstra(graph, 0, 5); + const adjacencyList = buildAdjacencyList(graph); + + const result = dijkstra(adjacencyList, createMinHeap); displayDijkstraResult(result); console.log("------- SP DIJKSTRA END -------"); }; -export default dijkstra; +export { dijkstra, dijkstraWrapper }; diff --git a/dijkstra-prim-visualization/src/algorithms/graph.js b/dijkstra-prim-visualization/src/algorithms/graph.js index 6d9780c..5bc43dd 100644 --- a/dijkstra-prim-visualization/src/algorithms/graph.js +++ b/dijkstra-prim-visualization/src/algorithms/graph.js @@ -1,13 +1,21 @@ -const createGraphFromComponent = function (nodes, edges) { - const mappedNodes = nodes.map((node) => node.id); - const mappedEdges = edges.map((edge) => { - return [edge.firstNode.id, edge.secondNode.id, edge.weight]; - }); +// const createGraphFromComponent = function (nodes, edges) { +// const mappedNodes = nodes.map((node) => node.id); +// const mappedEdges = edges.map((edge) => { +// return [edge.firstNode.id, edge.secondNode.id, edge.weight]; +// }); + +// return new createGraph(mappedNodes, mappedEdges); +// }; - return new createGraph(mappedNodes, mappedEdges); +const createAdjacencyListEntry = (node, weight) => { + const entry = { + node: node, + weight: weight, + }; + return entry; }; -const createGraph = function (nodes, edges) { +const createGraph = (nodes, edges) => { this.nodes = nodes; this.edges = new Array(edges.length); @@ -24,14 +32,6 @@ const createGraph = function (nodes, edges) { const buildAdjacencyList = (graph) => { const adjacencyList = new Array(graph.nodes.length).fill(null); - const createAdjacencyListEntry = (node, weight) => { - const entry = { - node: node, - weight: weight, - }; - return entry; - }; - graph.edges.forEach((e) => { if (!Array.isArray(adjacencyList[e.from])) { adjacencyList[e.from] = new Array(); @@ -47,4 +47,35 @@ const buildAdjacencyList = (graph) => { return adjacencyList; }; -export { createGraphFromComponent, buildAdjacencyList }; +const buildAdjacencyListFromComponent = (nodes, edges) => { + const adjacencyList = new Array(nodes.length).fill(null); + + edges + .map((e) => { + return { + from: e.firstNode.id, + to: e.secondNode.id, + weight: e.weight, + }; + }) + .forEach((e) => { + if (!Array.isArray(adjacencyList[e.from])) { + adjacencyList[e.from] = new Array(); + } + adjacencyList[e.from].push(createAdjacencyListEntry(e.to, e.weight)); + + if (!Array.isArray(adjacencyList[e.to])) { + adjacencyList[e.to] = new Array(); + } + adjacencyList[e.to].push(createAdjacencyListEntry(e.from, e.weight)); + }); + + return adjacencyList; +}; + +export { + createAdjacencyListEntry, + createGraph, + buildAdjacencyList, + buildAdjacencyListFromComponent, +}; diff --git a/dijkstra-prim-visualization/src/algorithms/mstPrim.js b/dijkstra-prim-visualization/src/algorithms/prim.js similarity index 75% rename from dijkstra-prim-visualization/src/algorithms/mstPrim.js rename to dijkstra-prim-visualization/src/algorithms/prim.js index 9c54294..39a13c2 100644 --- a/dijkstra-prim-visualization/src/algorithms/mstPrim.js +++ b/dijkstra-prim-visualization/src/algorithms/prim.js @@ -1,13 +1,13 @@ -import { createGraphFromComponent, buildAdjacencyList } from "./graph"; -import createMinHeap from "./minHeap"; -import createLinkedList from "./linkedList"; - -const prim = (graph) => { - const adjacencyList = buildAdjacencyList(graph); +import { + buildAdjacencyList, + buildAdjacencyListFromComponent, +} from "./graph.js"; +import createMinHeap from "./minHeap.js"; +const prim = (adjacencyList, createFringe) => { const nodesCount = adjacencyList.length; - const fringe = new createLinkedList(nodesCount); + const fringe = new createFringe(nodesCount); const isInHeap = new Array(nodesCount); const results = new Array(nodesCount); const keys = new Array(nodesCount); @@ -47,11 +47,13 @@ const prim = (graph) => { }); } + results.shift(1); + results.sort((a, b) => a.weight - b.weight); let mstTotal = 0; - for (let index = 1; index < nodesCount; index++) { + for (let index = 0; index < nodesCount - 1; index++) { mstTotal += results[index].weight; } @@ -61,10 +63,15 @@ const prim = (graph) => { }; }; +const primWrapper = (nodes, edges) => { + const adjacencyList = buildAdjacencyListFromComponent(nodes, edges); + return prim(adjacencyList, createMinHeap); +}; + const displayPrimResult = (result) => { console.log("MST steps:"); - for (let index = 1; index < result.steps.length; index++) { + for (let index = 0; index < result.steps.length; index++) { console.log( `From ${result.steps[index].from} to ${result.steps[index].to} with weight ${result.steps[index].weight}`, ); @@ -89,11 +96,13 @@ const computeMst = () => { ], ); - const result = prim(graph); + const adjacencyList = buildAdjacencyList(graph); + + const result = prim(adjacencyList); displayPrimResult(result); console.log("------- MST PRIM END -------"); }; -export default prim; +export { prim, primWrapper }; diff --git a/dijkstra-prim-visualization/src/components/Navbar/Navbar.jsx b/dijkstra-prim-visualization/src/components/Navbar/Navbar.jsx index b6e26c1..4de23c0 100644 --- a/dijkstra-prim-visualization/src/components/Navbar/Navbar.jsx +++ b/dijkstra-prim-visualization/src/components/Navbar/Navbar.jsx @@ -1,8 +1,7 @@ import { useContext } from "react"; import { GraphParamsContext } from "../../GraphParamsContext"; -import { createGraphFromComponent } from "../../algorithms/graph"; -import prim from "../../algorithms/mstPrim"; -import dijkstra from "../../algorithms/spDijkstra"; +import { primWrapper } from "../../algorithms/prim"; +import { dijkstraWrapper } from "../../algorithms/dijkstra"; /** * Navbar component displays buttons to print nodes and edges. @@ -12,14 +11,12 @@ const Navbar = () => { const { nodes, edges } = useContext(GraphParamsContext); const runPrim = () => { - const graph = createGraphFromComponent(nodes, edges); - const result = prim(graph); + const result = primWrapper(nodes, edges); console.log(result); }; const runDijkstra = () => { - const graph = createGraphFromComponent(nodes, edges); - const result = dijkstra(graph); + const result = dijkstraWrapper(nodes, edges); console.log(result); }; From 0181c243b316f5b70e86f799de702db18ee303a0 Mon Sep 17 00:00:00 2001 From: Andrii Venher Date: Sun, 19 Nov 2023 18:13:52 +0100 Subject: [PATCH 3/5] Extract sort and reverse operations from basic algorithms and move them to wrappers --- .../src/algorithms/dijkstra.js | 21 ++++++++---- .../src/algorithms/prim.js | 33 +++++++++++-------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/dijkstra-prim-visualization/src/algorithms/dijkstra.js b/dijkstra-prim-visualization/src/algorithms/dijkstra.js index 91c6750..ddc1f7c 100644 --- a/dijkstra-prim-visualization/src/algorithms/dijkstra.js +++ b/dijkstra-prim-visualization/src/algorithms/dijkstra.js @@ -40,11 +40,11 @@ const dijkstra = (adjacencyList, createFringe) => { }); } - const sp = new Array(); + const steps = new Array(); let node = finishNode; while (node != startNode) { - sp.push({ + steps.push({ from: parents[node].key, to: node, weight: parents[node].weight, @@ -53,14 +53,22 @@ const dijkstra = (adjacencyList, createFringe) => { } return { - steps: sp.reverse(), - spTotalWeight: keys[finishNode], + steps: steps, + total: keys[finishNode], + }; +}; + +const transformResult = (result) => { + return { + steps: result.steps.reverse(), + total: result.total, }; }; const dijkstraWrapper = (nodes, edges) => { const adjacencyList = buildAdjacencyListFromComponent(nodes, edges); - return dijkstra(adjacencyList, createMinHeap); + const result = dijkstra(adjacencyList, createMinHeap); + return transformResult(result); }; const displayDijkstraResult = (result) => { @@ -96,8 +104,9 @@ const computeSp = () => { const adjacencyList = buildAdjacencyList(graph); const result = dijkstra(adjacencyList, createMinHeap); + const transformedResult = transformResult(result); - displayDijkstraResult(result); + displayDijkstraResult(transformedResult); console.log("------- SP DIJKSTRA END -------"); }; diff --git a/dijkstra-prim-visualization/src/algorithms/prim.js b/dijkstra-prim-visualization/src/algorithms/prim.js index 39a13c2..8bbc682 100644 --- a/dijkstra-prim-visualization/src/algorithms/prim.js +++ b/dijkstra-prim-visualization/src/algorithms/prim.js @@ -9,14 +9,14 @@ const prim = (adjacencyList, createFringe) => { const fringe = new createFringe(nodesCount); const isInHeap = new Array(nodesCount); - const results = new Array(nodesCount); + const steps = new Array(nodesCount); const keys = new Array(nodesCount); // Insert node 0 with value 0 fringe.insert(0, 0); isInHeap[0] = true; keys[0] = Infinity; - results[0] = { + steps[0] = { parent: -1, weight: null, }; @@ -37,7 +37,7 @@ const prim = (adjacencyList, createFringe) => { if (keys[n.node] > n.weight) { fringe.decreaseKey(n.node, n.weight); keys[n.node] = n.weight; - results[n.node] = { + steps[n.node] = { from: extractedNode.key, to: n.node, weight: n.weight, @@ -47,25 +47,29 @@ const prim = (adjacencyList, createFringe) => { }); } - results.shift(1); - - results.sort((a, b) => a.weight - b.weight); + return steps; +}; - let mstTotal = 0; +const transformSteps = (steps) => { + steps.shift(1); + steps.sort((a, b) => a.weight - b.weight); - for (let index = 0; index < nodesCount - 1; index++) { - mstTotal += results[index].weight; + let total = 0; + for (let i = 0; i < steps.length; i++) { + total += steps[i].weight; } return { - steps: results, - mstTotalWeight: mstTotal, + steps: steps, + total: total, }; }; const primWrapper = (nodes, edges) => { const adjacencyList = buildAdjacencyListFromComponent(nodes, edges); - return prim(adjacencyList, createMinHeap); + const steps = prim(adjacencyList, createMinHeap); + + return transformSteps(steps); }; const displayPrimResult = (result) => { @@ -98,9 +102,10 @@ const computeMst = () => { const adjacencyList = buildAdjacencyList(graph); - const result = prim(adjacencyList); + const steps = prim(adjacencyList); + const transformedSteps = transformSteps(steps); - displayPrimResult(result); + displayPrimResult(transformedSteps); console.log("------- MST PRIM END -------"); }; From b1d056d4c7aefbeec90b9d49dddc755c8f44b636 Mon Sep 17 00:00:00 2001 From: Andrii Venher Date: Sun, 19 Nov 2023 18:17:24 +0100 Subject: [PATCH 4/5] Rename isInHeap to isInFringe to reduce coupling between Prim's algorithm and fringe data structure --- dijkstra-prim-visualization/src/algorithms/prim.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dijkstra-prim-visualization/src/algorithms/prim.js b/dijkstra-prim-visualization/src/algorithms/prim.js index 8bbc682..fe269db 100644 --- a/dijkstra-prim-visualization/src/algorithms/prim.js +++ b/dijkstra-prim-visualization/src/algorithms/prim.js @@ -8,13 +8,13 @@ const prim = (adjacencyList, createFringe) => { const nodesCount = adjacencyList.length; const fringe = new createFringe(nodesCount); - const isInHeap = new Array(nodesCount); + const isInFringe = new Array(nodesCount); const steps = new Array(nodesCount); const keys = new Array(nodesCount); // Insert node 0 with value 0 fringe.insert(0, 0); - isInHeap[0] = true; + isInFringe[0] = true; keys[0] = Infinity; steps[0] = { parent: -1, @@ -22,18 +22,18 @@ const prim = (adjacencyList, createFringe) => { }; for (let index = 1; index < nodesCount; index++) { - isInHeap[index] = true; + isInFringe[index] = true; keys[index] = Infinity; fringe.insert(index, Infinity); } while (!fringe.isEmpty()) { const extractedNode = fringe.extractMin(); - isInHeap[extractedNode.key] = false; + isInFringe[extractedNode.key] = false; const neightbours = adjacencyList[extractedNode.key]; neightbours.forEach((n) => { - if (isInHeap[n.node]) { + if (isInFringe[n.node]) { if (keys[n.node] > n.weight) { fringe.decreaseKey(n.node, n.weight); keys[n.node] = n.weight; From 3654c52560451373f490f300dffea294a4d6a022 Mon Sep 17 00:00:00 2001 From: Andrii Venher Date: Sun, 19 Nov 2023 20:22:53 +0100 Subject: [PATCH 5/5] Move measurement tools to visualization project --- dijkstra-prim-measurement/package-lock.json | 13 ------------- dijkstra-prim-measurement/package.json | 13 ------------- .../src}/measurement/dataset.js | 6 +++--- .../src}/measurement/measure.js | 16 ++++++++-------- .../src}/measurement/prepare.js | 0 5 files changed, 11 insertions(+), 37 deletions(-) delete mode 100644 dijkstra-prim-measurement/package-lock.json delete mode 100644 dijkstra-prim-measurement/package.json rename {dijkstra-prim-measurement => dijkstra-prim-visualization/src}/measurement/dataset.js (93%) rename {dijkstra-prim-measurement => dijkstra-prim-visualization/src}/measurement/measure.js (86%) rename {dijkstra-prim-measurement => dijkstra-prim-visualization/src}/measurement/prepare.js (100%) diff --git a/dijkstra-prim-measurement/package-lock.json b/dijkstra-prim-measurement/package-lock.json deleted file mode 100644 index ef72f32..0000000 --- a/dijkstra-prim-measurement/package-lock.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "benchmark", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "benchmark", - "version": "1.0.0", - "license": "ISC" - } - } -} diff --git a/dijkstra-prim-measurement/package.json b/dijkstra-prim-measurement/package.json deleted file mode 100644 index 76ecacc..0000000 --- a/dijkstra-prim-measurement/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "benchmark", - "version": "1.0.0", - "type": "module", - "description": "", - "main": "test.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC" -} diff --git a/dijkstra-prim-measurement/measurement/dataset.js b/dijkstra-prim-visualization/src/measurement/dataset.js similarity index 93% rename from dijkstra-prim-measurement/measurement/dataset.js rename to dijkstra-prim-visualization/src/measurement/dataset.js index 717c1ab..8a55155 100644 --- a/dijkstra-prim-measurement/measurement/dataset.js +++ b/dijkstra-prim-visualization/src/measurement/dataset.js @@ -1,6 +1,6 @@ import fs from "fs"; import path from "path"; -import { createAdjacencyListEntry } from "../../dijkstra-prim-visualization/src/algorithms/graph.js"; +import { createAdjacencyListEntry } from "../algorithms/graph.js"; const directory = "datasets"; @@ -10,7 +10,7 @@ const generateAdjacencyList = ( nodesCount, probability, minWeight, - maxWeight + maxWeight, ) => { const adjacencyList = new Array(nodesCount); @@ -75,7 +75,7 @@ const readAdjacencyList = (nodesCount) => { const [node, weight] = chunk.split(","); return createAdjacencyListEntry( Number.parseInt(node), - Number.parseInt(weight) + Number.parseInt(weight), ); }); adjacencyList[i - 1] = row; diff --git a/dijkstra-prim-measurement/measurement/measure.js b/dijkstra-prim-visualization/src/measurement/measure.js similarity index 86% rename from dijkstra-prim-measurement/measurement/measure.js rename to dijkstra-prim-visualization/src/measurement/measure.js index e8ee5dc..6d6bf78 100644 --- a/dijkstra-prim-measurement/measurement/measure.js +++ b/dijkstra-prim-visualization/src/measurement/measure.js @@ -1,9 +1,9 @@ import fs from "fs"; import path from "path"; import { generateAdjacencyList } from "./dataset.js"; -import { prim } from "../../dijkstra-prim-visualization/src/algorithms/prim.js"; -import createMinHeap from "../../dijkstra-prim-visualization/src/algorithms/minHeap.js"; -import createLinkedList from "../../dijkstra-prim-visualization/src/algorithms/linkedList.js"; +import { prim } from "../algorithms/prim.js"; +import createMinHeap from "../algorithms/minHeap.js"; +import createLinkedList from "../algorithms/linkedList.js"; const args = process.argv.slice(2); @@ -80,14 +80,14 @@ const measure = () => { nodesCount, config.edgeProbability, config.minEdgeWeight, - config.maxEdgeWeght + config.maxEdgeWeght, ); const deltaMinHeap = measureAverageTime(() => - prim(adjacencyList, createMinHeap) + prim(adjacencyList, createMinHeap), ); const deltaLinkedList = measureAverageTime(() => - prim(adjacencyList, createLinkedList) + prim(adjacencyList, createLinkedList), ); resultsMinHeap.push({ @@ -100,10 +100,10 @@ const measure = () => { }); console.log( - `Result (min heap): nodesCount = ${nodesCount}, delta = ${deltaMinHeap} ms` + `Result (min heap): nodesCount = ${nodesCount}, delta = ${deltaMinHeap} ms`, ); console.log( - `Result (linked list): nodesCount = ${nodesCount}, delta = ${deltaLinkedList} ms` + `Result (linked list): nodesCount = ${nodesCount}, delta = ${deltaLinkedList} ms`, ); } diff --git a/dijkstra-prim-measurement/measurement/prepare.js b/dijkstra-prim-visualization/src/measurement/prepare.js similarity index 100% rename from dijkstra-prim-measurement/measurement/prepare.js rename to dijkstra-prim-visualization/src/measurement/prepare.js