diff --git a/README.md b/README.md index 3751d89c..bb69b6ef 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,9 @@ * [Power Set](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/power-set) * [Primality Test](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/primality-test) (trial division) * [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the greatest common divisor (GCD) + * [Least Common Multiple (LCM)](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/least-common-multiple) * [Fisher–Yates Shuffle](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/fisher-yates) - random permutation of a finite sequence * Collatz Conjecture algorithm - * Greatest Difference - * Least Common Multiple * Newton's square * Shannon Entropy * **String** diff --git a/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js b/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js index 5c08c01e..01d4f4e1 100644 --- a/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js +++ b/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js @@ -20,5 +20,7 @@ describe('euclideanAlgorithm', () => { expect(euclideanAlgorithm(105, 252)).toBe(21); expect(euclideanAlgorithm(1071, 462)).toBe(21); expect(euclideanAlgorithm(462, 1071)).toBe(21); + expect(euclideanAlgorithm(462, -1071)).toBe(21); + expect(euclideanAlgorithm(-462, -1071)).toBe(21); }); }); diff --git a/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js b/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js index 422b23fb..1532ea35 100644 --- a/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js +++ b/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js @@ -1,9 +1,12 @@ /** - * @param {number} a - * @param {number} b + * @param {number} originalA + * @param {number} originalB * @return {number|null} */ -export default function euclideanAlgorithm(a, b) { +export default function euclideanAlgorithm(originalA, originalB) { + const a = Math.abs(originalA); + const b = Math.abs(originalB); + if (a === 0 && b === 0) { return null; } @@ -16,9 +19,14 @@ export default function euclideanAlgorithm(a, b) { return a; } + // Normally we need to do subtraction (a - b) but to prevent + // recursion occurs to often we may shorten subtraction to (a % b). + // Since (a % b) is normally means that we've subtracted b from a + // many times until the difference became less then a. + if (a > b) { - return euclideanAlgorithm(a - b, b); + return euclideanAlgorithm(a % b, b); } - return euclideanAlgorithm(b - a, a); + return euclideanAlgorithm(b % a, a); } diff --git a/src/algorithms/math/least-common-multiple/README.md b/src/algorithms/math/least-common-multiple/README.md new file mode 100644 index 00000000..3e49af9a --- /dev/null +++ b/src/algorithms/math/least-common-multiple/README.md @@ -0,0 +1,62 @@ +# Least common multiple + +In arithmetic and number theory, the least common multiple, +lowest common multiple, or smallest common multiple of +two integers `a` and `b`, usually denoted by `LCM(a, b)`, is +the smallest positive integer that is divisible by +both `a` and `b`. Since division of integers by zero is +undefined, this definition has meaning only if `a` and `b` are +both different from zero. However, some authors define `lcm(a,0)` +as `0` for all `a`, which is the result of taking the `lcm` +to be the least upper bound in the lattice of divisibility. + +## Example + +What is the LCM of 4 and 6? + +Multiples of `4` are: + +``` +4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, ... +``` + +and the multiples of `6` are: + +``` +6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, ... +``` + +Common multiples of `4` and `6` are simply the numbers +that are in both lists: + +``` +12, 24, 36, 48, 60, 72, .... +``` + +So, from this list of the first few common multiples of +the numbers `4` and `6`, their least common multiple is `12`. + +## Computing the least common multiple + +The following formula reduces the problem of computing the +least common multiple to the problem of computing the greatest +common divisor (GCD), also known as the greatest common factor: + +``` +lcm(a, b) = |a * b| / gcd(a, b) +``` + +![LCM](https://upload.wikimedia.org/wikipedia/commons/c/c9/Symmetrical_5-set_Venn_diagram_LCM_2_3_4_5_7.svg) + +A Venn diagram showing the least common multiples of +combinations of `2`, `3`, `4`, `5` and `7` (`6` is skipped as +it is `2 × 3`, both of which are already represented). + +For example, a card game which requires its cards to be +divided equally among up to `5` players requires at least `60` +cards, the number at the intersection of the `2`, `3`, `4` +and `5` sets, but not the `7` set. + +## References + +[Wikipedia](https://en.wikipedia.org/wiki/Least_common_multiple) diff --git a/src/algorithms/math/least-common-multiple/__test__/leastCommonMultiple.test.js b/src/algorithms/math/least-common-multiple/__test__/leastCommonMultiple.test.js new file mode 100644 index 00000000..2152e110 --- /dev/null +++ b/src/algorithms/math/least-common-multiple/__test__/leastCommonMultiple.test.js @@ -0,0 +1,18 @@ +import leastCommonMultiple from '../leastCommonMultiple'; + +describe('leastCommonMultiple', () => { + it('should find least common multiple', () => { + expect(leastCommonMultiple(0, 0)).toBe(0); + expect(leastCommonMultiple(1, 0)).toBe(0); + expect(leastCommonMultiple(0, 1)).toBe(0); + expect(leastCommonMultiple(4, 6)).toBe(12); + expect(leastCommonMultiple(6, 21)).toBe(42); + expect(leastCommonMultiple(7, 2)).toBe(14); + expect(leastCommonMultiple(3, 5)).toBe(15); + expect(leastCommonMultiple(7, 3)).toBe(21); + expect(leastCommonMultiple(1000000, 2)).toBe(1000000); + expect(leastCommonMultiple(-9, -18)).toBe(18); + expect(leastCommonMultiple(-7, -9)).toBe(63); + expect(leastCommonMultiple(-7, 9)).toBe(63); + }); +}); diff --git a/src/algorithms/math/least-common-multiple/leastCommonMultiple.js b/src/algorithms/math/least-common-multiple/leastCommonMultiple.js new file mode 100644 index 00000000..e40f9c95 --- /dev/null +++ b/src/algorithms/math/least-common-multiple/leastCommonMultiple.js @@ -0,0 +1,15 @@ +import euclideanAlgorithm from '../euclidean-algorithm/euclideanAlgorithm'; + +/** + * @param {number} a + * @param {number} b + * @return {number} + */ + +export default function leastCommonMultiple(a, b) { + if (a === 0 && b === 0) { + return 0; + } + + return Math.abs(a * b) / euclideanAlgorithm(a, b); +}