diff --git a/algorithms/math/gcd.js b/algorithms/math/gcd.js index b7b6b37..f921ea1 100644 --- a/algorithms/math/gcd.js +++ b/algorithms/math/gcd.js @@ -43,4 +43,64 @@ var gcdDivisionBased = function (a, b) { return a; }; +/** + * Binary GCD algorithm (Stein's Algorithm) + * + * @link https://en.wikipedia.org/wiki/Binary_GCD_algorithm + * This is basically a js version of the c implementation on Wikipedia + * + * @param Number + * @param Number + * + * @return Number + */ +var gcdBinaryIterative = function (a, b) { + + // GCD(0,b) == b; GCD(a,0) == a, GCD(0,0) == 0 + if (a === 0) { + return b; + } + + if (b === 0) { + return a; + } + + // Let shift = log(K), where K is the greatest power of 2 dividing both a and b + for (var shift = 0; ((a | b) & 1) == 0; ++shift) { + a >>= 1; + b >>= 1; + } + + // Remove all factors of 2 in a -- they are not common + // Note: a is not zero, so while will terminate + while ((a & 1) === 0) { + a >>= 1; + } + + var tmp; + + // From here on, a is always odd + do { + // Remove all factors of 2 in b -- they are not common + // Note: b is not zero, so while will terminate + while ((b & 1) === 0) { + b >>= 1; + } + + // Now a and b are both odd. Swap if necessary so a <= b, + // then set b = b - a (which is even). + if (a > b) { + tmp = b; + b = a; + a = tmp; + } + + b -= a; // Here b >= a + } while (b !== 0); + + // restore common factors of 2 + return a << shift; +}; + +gcdDivisionBased.binary = gcdBinaryIterative; module.exports = gcdDivisionBased; diff --git a/test/algorithms/math/gcd.js b/test/algorithms/math/gcd.js index 133c5bd..7c5d0c3 100644 --- a/test/algorithms/math/gcd.js +++ b/test/algorithms/math/gcd.js @@ -37,6 +37,20 @@ describe('GCD', function () { assert.equal(gcd(7, 5), 1); assert.equal(gcd(35, 49), 7); }); + + it('should calculate the correct GCD between two numbers using the binary method', function () { + var gcdb = gcd.binary; + assert.equal(gcdb(1, 0), 1); + assert.equal(gcdb(2, 2), 2); + assert.equal(gcdb(2, 4), 2); + assert.equal(gcdb(4, 2), 2); + assert.equal(gcdb(5, 2), 1); + assert.equal(gcdb(10, 100), 10); + assert.equal(gcdb(10000000, 2), 2); + assert.equal(gcdb(7, 49), 7); + assert.equal(gcdb(7, 5), 1); + assert.equal(gcdb(35, 49), 7); + }); });