From 2881b3f18dc83c301eb88ee40d4e86bf71ffdd45 Mon Sep 17 00:00:00 2001 From: Ethienne Graveline Date: Fri, 22 May 2020 20:06:17 -0300 Subject: [PATCH 1/4] Integer partition (#24) * Fixed error in Integer Partition returns the wrong total for some values of n e.g. when n is 6 returns 14 instead of 11 This more closely resembles the code from referenced in the README: https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/integer-partition * added some simple comments * fixed error in Integer Partition on some values it would print wrong partitions e.g. when n is 6 [3,3,1] is printed but is not a valid output * fixed a typo Co-authored-by: Eti --- Dynamic Programming/Integer Partition/code.js | 75 +++++++++++-------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/Dynamic Programming/Integer Partition/code.js b/Dynamic Programming/Integer Partition/code.js index 3618098c..da4d0380 100644 --- a/Dynamic Programming/Integer Partition/code.js +++ b/Dynamic Programming/Integer Partition/code.js @@ -8,48 +8,61 @@ const logger = new LogTracer(); Layout.setRoot(new VerticalLayout([tracer, logger])); const integer = Randomize.Integer({ min: 5, max: 14 }); const D = []; -const A = []; +const A = ""; for (let i = 0; i <= integer; i++) { D.push([]); - D[0][i] = 1; - D[i][1] = 1; - for (let j = 0; j <= integer; j++) D[i][j] = 0; + D[i][0] = 1 + for (let j = 1; j <= integer; j++) D[i][j] = 0; } tracer.set(D); Tracer.delay(); // } function partition(A, n, p) { - // logger { - if (n === 0) logger.println(`[${A.join(', ')}]`); - // } - else { - let end = n; - if (p !== 0 && A[p - 1] < n) end = A[p - 1]; - for (let i = end; i > 0; i--) { - A[p] = i; - partition(A, n - i, p + 1); + // logger { + if (p == 0) logger.println(`[${A.split('').join(', ')}]`); + // } + else { + if (n > 1) partition(A, n - 1, p); + if (n <= p) partition(n + A, n, p - n); } - } } function integerPartition(n) { - // Calculate number of partitions for all numbers from 1 to n - for (let i = 2; i <= n; i++) { - // We are allowed to use numbers from 2 to i - for (let j = 1; j <= i; j++) { - // Number of partitions without j number + number of partitions with max j - // visualize { - tracer.select(i, j); - Tracer.delay(); - // } - D[i][j] = D[i][j - 1] + D[i - j][Math.max(j, i - j)]; - // visualize { - tracer.patch(i, j, D[i][j]); - Tracer.delay(); - tracer.depatch(i, j); - tracer.deselect(i, j); - // } + + // cycle through each cell of matrix + for (let i = 1; i <= n; i++) { + for (let j = 1; j <= n; j++) { + if (i > j) { + // visualize { + tracer.select(i, j); + Tracer.delay(); + // } + // set cell to cell above it + D[i][j] = D[i - 1][j]; + // visualize { + tracer.patch(i, j, D[i][j]); + Tracer.delay(); + tracer.depatch(i, j); + tracer.deselect(i, j); + // } + } + else { + // visualize { + tracer.select(i, j); + Tracer.delay(); + // } + // grab above cell and add it to previous cell + const above = D[i - 1][j]; + const left = D[i][j - i]; + D[i][j] = above + left; + // visualize { + tracer.patch(i, j, D[i][j]); + Tracer.delay(); + tracer.depatch(i, j); + tracer.deselect(i, j); + // } + } } } return D[n][n]; @@ -58,7 +71,7 @@ function integerPartition(n) { // logger { logger.println(`Partitioning: ${integer}`); // } -partition(A, integer, 0); +partition(A, integer, integer); const part = integerPartition(integer); // logger { logger.println(part); From cbb3b2de24d2b922983c80058f3c08b6321d3cab Mon Sep 17 00:00:00 2001 From: Jinseo Park Date: Thu, 28 Jan 2021 02:24:04 -0500 Subject: [PATCH 2/4] Update algorithm-visualizer version --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index da6ea512..6f6db3b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { - "name": "algorithms-validator", + "name": "algorithms", "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { "algorithm-visualizer": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/algorithm-visualizer/-/algorithm-visualizer-2.3.5.tgz", - "integrity": "sha512-c1jJmJ1ba4qfDrWfW6TMWXs1mvj6uyUva2uhbNwg655r8/VXauvbXrYkF0E1j7JBTA2POep4Kl4AD1T/UKSuRA==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/algorithm-visualizer/-/algorithm-visualizer-2.3.8.tgz", + "integrity": "sha512-0oNDbQRpf2P8F3FgyrehBde+c23R/wYbrqb2jSChDof2dZT/U/iQQ0WHE10MEmD7zucNHxmt60RNZDGu7CQ2BA==", "requires": { "axios": "^0.18.0", "opn": "^5.4.0" @@ -124,9 +124,9 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" }, "is-wsl": { "version": "1.1.0", From 713869809f514f73631b157de3718545053bf958 Mon Sep 17 00:00:00 2001 From: Jeff Tian Date: Thu, 28 Jan 2021 15:24:28 +0800 Subject: [PATCH 3/4] feat: Add k-means clustering (#27) --- Uncategorized/K-Means Clustering/README.md | 13 ++ Uncategorized/K-Means Clustering/code.js | 172 +++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 Uncategorized/K-Means Clustering/README.md create mode 100644 Uncategorized/K-Means Clustering/code.js diff --git a/Uncategorized/K-Means Clustering/README.md b/Uncategorized/K-Means Clustering/README.md new file mode 100644 index 00000000..6e2a759c --- /dev/null +++ b/Uncategorized/K-Means Clustering/README.md @@ -0,0 +1,13 @@ +# K-Means Clustering +K-means clustering is a method to partition _n_ observations into _k_ clusters in which each observation belongs to +the cluster with the nearest mean (cluster centers or cluster centroid). + +Given a set of observations, where each observation is a d-dimensional real vector, _k-means_ clustering aims to +partition the _n_ observations into _k(≤ n)_ sets so as to minimize the within-cluster sum of squares (i.e. variance). + +## Complexity +* **Time**: ![$O(n^{2k+1})$](https://latex.codecogs.com/svg.latex?O(n^{2k+1})) for 2-dimensional real vector + +## References +* [Wikipedia](https://en.wikipedia.org/wiki/K-means_clustering) +* [Inspired by kmeans.js.org](https://kmeans.js.org/) diff --git a/Uncategorized/K-Means Clustering/code.js b/Uncategorized/K-Means Clustering/code.js new file mode 100644 index 00000000..a82deedb --- /dev/null +++ b/Uncategorized/K-Means Clustering/code.js @@ -0,0 +1,172 @@ +// import visualization libraries { +const { + Array2DTracer, + Layout, + LogTracer, + Tracer, + VerticalLayout, + ScatterTracer, + Randomize, +} = require('algorithm-visualizer') +// } + +// define helper functions { +const shuffle = a => { + const array = a.slice(0) + const copy = [] + let n = array.length + + while (n) { + let i = Math.floor(Math.random() * n--) + copy.push(array.splice(i, 1)[0]) + } + + return copy +} + +const sum = (x, y) => x + y +const chooseRandomCenters = (data, k) => shuffle(data).slice(0, k) +const pointify = ([x, y]) => `(${x}, ${y})` +const arrayify = a => a.map(pointify) +const stringify = a => arrayify(a).join(', ') +const distance = ([x1, y1], [x2, y2]) => sum(Math.pow(x1 - x2, 2), + Math.pow(y1 - y2, 2)) +const col = (a, i) => a.map(p => p[i]) +const mean = a => a.reduce(sum, 0) / a.length +const centerOfCluster = cluster => [ + mean(col(cluster, 0)), + mean(col(cluster, 1)), +] +const reCalculateCenters = clusters => clusters.map(centerOfCluster) +const areCentersEqual = (c1, c2) => !!c1 && !!c2 && !(c1 < c2 || c2 < c1) + +function cluster(data, centers) { + const clusters = centers.map(() => []) + + for (let i = 0; i < data.length; i++) { + const point = data[i] + let minDistance = Infinity + let minDistanceIndex = -1 + + for (let j = 0; j < centers.length; j++) { + const d = distance(point, centers[j]) + + if (d < minDistance) { + minDistance = d + minDistanceIndex = j + } + } + + if (!clusters[minDistanceIndex] instanceof Array) { + clusters[minDistanceIndex] = [] + } + + clusters[minDistanceIndex].push(point) + } + + return clusters +} + +// } + +// define tracer variables { +const array2dTracer = new Array2DTracer('Grid') +const logTracer = new LogTracer('Console') +const scatterTracer = new ScatterTracer('Scatter') +// } + +// define input variables +const unClusteredData = Randomize.Array2D( + { N: Randomize.Integer({ min: 10, max: 25 }) }) +const k = Randomize.Integer( + { min: 2, max: Math.floor(unClusteredData.length / 5) }) + +const recenterAndCluster = (originalClusters) => { + const centers = reCalculateCenters(originalClusters) + const clusters = cluster(unClusteredData, centers) + return { centers, clusters } +} + +const improve = (loops, clusters, centers) => { + const allowImprove = () => loops < 1000 + + if (!allowImprove()) { + return { clusters, centers } + } + + loops++ + + const ret = recenterAndCluster(clusters) + + // trace { + array2dTracer.set(clusters.map(c => c.map(pointify))) + scatterTracer.set([unClusteredData, ...ret.clusters, ret.centers]) + + logTracer.println('') + logTracer.println(`Iteration #${loops} Result: `) + logTracer.println(`\tClusters:`) + logTracer.println( + `\t\t${ret.clusters.map(c => stringify(c)).join(`\n\t\t`)}`) + logTracer.println(`\tCenters:`) + logTracer.println(`\t\t${stringify(ret.centers)}`) + logTracer.println('') + + Tracer.delay() + // } + + if (!allowImprove() || areCentersEqual(centers, ret.centers)) { + return ret + } + + return improve(loops, ret.clusters, ret.centers) +} + +(function main() { + // visualize { + Layout.setRoot(new VerticalLayout([scatterTracer, array2dTracer, logTracer])) + + logTracer.println(`Un-clustered data = ${stringify(unClusteredData)}`) + array2dTracer.set([unClusteredData.map(pointify)]) + scatterTracer.set([unClusteredData]) + + Tracer.delay() + // } + + // Start with random centers + const centers = chooseRandomCenters(unClusteredData, k) + + // trace { + logTracer.println( + `Initial random selected centers = ${stringify(centers)}`) + scatterTracer.set([unClusteredData, ...[[], []], centers]) + + Tracer.delay() + // } + + // Cluster to the random centers + const clusters = cluster(unClusteredData, centers) + + // trace { + logTracer.println( + `Initial clusters = \n\t${clusters.map(stringify).join('\n\t')}`) + array2dTracer.set(clusters.map(c => c.map(pointify))) + scatterTracer.set([unClusteredData, ...clusters, centers]) + + Tracer.delay() + // } + + // start iterations here + const ret = improve(0, clusters, centers) + + // trace { + Tracer.delay() + + logTracer.println( + `Final clustered data = \n\t${ret.clusters.map(stringify) + .join('\n\t')}`) + logTracer.println(`Best centers = ${stringify(ret.centers)}`) + array2dTracer.set(ret.clusters.map(c => c.map(pointify))) + scatterTracer.set([unClusteredData, ...ret.clusters, ret.centers]) + Tracer.delay() + // } +})() From fe3adcf890da652e5cda842d7f90b50fa7242e3a Mon Sep 17 00:00:00 2001 From: William E Bodell III Date: Sun, 21 Mar 2021 17:48:14 -0500 Subject: [PATCH 4/4] Fix counts array visualization in Counting Sort (#28) * Fix counts array visualization in Counting Sort Final step did not correctly visualize decrementing the value in the counts array. It was decrementing the value but not updating the visualization. * Add countsTracer.depatch My previously committed code left the boxes in the counts array colored red, so I realized I had forgotten to add a depatch after the patch that I added at line 66 --- Divide and Conquer/Counting Sort/code.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Divide and Conquer/Counting Sort/code.js b/Divide and Conquer/Counting Sort/code.js index aea80f8f..4293fade 100644 --- a/Divide and Conquer/Counting Sort/code.js +++ b/Divide and Conquer/Counting Sort/code.js @@ -58,15 +58,17 @@ const array = Randomize.Array1D({ N, value: () => Randomize.Integer({ min: 0, ma const number = array[i]; const count = counts[number]; sortedArray[count - 1] = number; + counts[number]--; // visualize { arrayTracer.select(i); countsTracer.select(number); sortedArrayTracer.patch(count - 1, sortedArray[count - 1]); + countsTracer.patch(number, counts[number]); Tracer.delay(); sortedArrayTracer.depatch(count - 1); + countsTracer.depatch(number); countsTracer.deselect(number); arrayTracer.deselect(i); // } - counts[number]--; } })();