diff --git a/README.md b/README.md index 3b72e1ba..1688ba98 100644 --- a/README.md +++ b/README.md @@ -31,10 +31,10 @@ * [Fibonacci Number](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/fibonacci) * [Cartesian Product](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/cartesian-product) * [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) + * [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) * Collatz Conjecture algorithm * Extended Euclidean algorithm - * Euclidean algorithm to calculate the Greatest Common Divisor (GCD) * Find Divisors * Fisher-Yates * Greatest Difference diff --git a/src/algorithms/math/euclidean-algorithm/README.md b/src/algorithms/math/euclidean-algorithm/README.md new file mode 100644 index 00000000..a7276fd5 --- /dev/null +++ b/src/algorithms/math/euclidean-algorithm/README.md @@ -0,0 +1,57 @@ +# Euclidean algorithm + +In mathematics, the Euclidean algorithm, or Euclid's algorithm, +is an efficient method for computing the greatest common divisor +(GCD) of two numbers, the largest number that divides both of +them without leaving a remainder. + +The Euclidean algorithm is based on the principle that the +greatest common divisor of two numbers does not change if +the larger number is replaced by its difference with the +smaller number. For example, `21` is the GCD of `252` and +`105` (as `252 = 21 × 12` and `105 = 21 × 5`), and the same +number `21` is also the GCD of `105` and `252 − 105 = 147`. +Since this replacement reduces the larger of the two numbers, +repeating this process gives successively smaller pairs of +numbers until the two numbers become equal. +When that occurs, they are the GCD of the original two numbers. + +By reversing the steps, the GCD can be expressed as a sum of +the two original numbers each multiplied by a positive or +negative integer, e.g., `21 = 5 × 105 + (−2) × 252`. +The fact that the GCD can always be expressed in this way is +known as Bézout's identity. + +![GCD](https://upload.wikimedia.org/wikipedia/commons/3/37/Euclid%27s_algorithm_Book_VII_Proposition_2_3.png) + +Euclid's method for finding the greatest common divisor (GCD) +of two starting lengths `BA` and `DC`, both defined to be +multiples of a common "unit" length. The length `DC` being +shorter, it is used to "measure" `BA`, but only once because +remainder `EA` is less than `DC`. EA now measures (twice) +the shorter length `DC`, with remainder `FC` shorter than `EA`. +Then `FC` measures (three times) length `EA`. Because there is +no remainder, the process ends with `FC` being the `GCD`. +On the right Nicomachus' example with numbers `49` and `21` +resulting in their GCD of `7` (derived from Heath 1908:300). + +![GCD](https://upload.wikimedia.org/wikipedia/commons/7/74/24x60.svg) + +A `24-by-60` rectangle is covered with ten `12-by-12` square +tiles, where `12` is the GCD of `24` and `60`. More generally, +an `a-by-b` rectangle can be covered with square tiles of +side-length `c` only if `c` is a common divisor of `a` and `b`. + +![GCD](https://upload.wikimedia.org/wikipedia/commons/1/1c/Euclidean_algorithm_1071_462.gif) + +Subtraction-based animation of the Euclidean algorithm. +The initial rectangle has dimensions `a = 1071` and `b = 462`. +Squares of size `462×462` are placed within it leaving a +`462×147` rectangle. This rectangle is tiled with `147×147` +squares until a `21×147` rectangle is left, which in turn is +tiled with `21×21` squares, leaving no uncovered area. +The smallest square size, `21`, is the GCD of `1071` and `462`. + +## References + +[Wikipedia](https://en.wikipedia.org/wiki/Euclidean_algorithm) diff --git a/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js b/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js new file mode 100644 index 00000000..5c08c01e --- /dev/null +++ b/src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js @@ -0,0 +1,24 @@ +import euclideanAlgorithm from '../euclideanAlgorithm'; + +describe('euclideanAlgorithm', () => { + it('should calculate GCD', () => { + expect(euclideanAlgorithm(0, 0)).toBeNull(); + expect(euclideanAlgorithm(2, 0)).toBe(2); + expect(euclideanAlgorithm(0, 2)).toBe(2); + expect(euclideanAlgorithm(1, 2)).toBe(1); + expect(euclideanAlgorithm(2, 1)).toBe(1); + expect(euclideanAlgorithm(6, 6)).toBe(6); + expect(euclideanAlgorithm(2, 4)).toBe(2); + expect(euclideanAlgorithm(4, 2)).toBe(2); + expect(euclideanAlgorithm(12, 4)).toBe(4); + expect(euclideanAlgorithm(4, 12)).toBe(4); + expect(euclideanAlgorithm(5, 13)).toBe(1); + expect(euclideanAlgorithm(27, 13)).toBe(1); + expect(euclideanAlgorithm(24, 60)).toBe(12); + expect(euclideanAlgorithm(60, 24)).toBe(12); + expect(euclideanAlgorithm(252, 105)).toBe(21); + expect(euclideanAlgorithm(105, 252)).toBe(21); + expect(euclideanAlgorithm(1071, 462)).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 new file mode 100644 index 00000000..422b23fb --- /dev/null +++ b/src/algorithms/math/euclidean-algorithm/euclideanAlgorithm.js @@ -0,0 +1,24 @@ +/** + * @param {number} a + * @param {number} b + * @return {number|null} + */ +export default function euclideanAlgorithm(a, b) { + if (a === 0 && b === 0) { + return null; + } + + if (a === 0 && b !== 0) { + return b; + } + + if (a !== 0 && b === 0) { + return a; + } + + if (a > b) { + return euclideanAlgorithm(a - b, b); + } + + return euclideanAlgorithm(b - a, a); +}