diff --git a/README.md b/README.md index 33eefdcb..3feb9bda 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ a set of rules that precisely define a sequence of operations. * [Least Common Multiple](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/least-common-multiple) (LCM) * [Integer Partition](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/integer-partition) * [Sieve of Eratosthenes](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/sieve-of-eratosthenes) - finding all prime numbers up to any given limit + * [Is Power of Two](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/is-power-of-two) - check if the number is power of two (naive and bitwise algorithms) * **Sets** * [Cartesian Product](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/cartesian-product) - product of multiple sets * [Power Set](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/power-set) - all subsets of a set diff --git a/src/algorithms/math/is-power-of-two/README.md b/src/algorithms/math/is-power-of-two/README.md new file mode 100644 index 00000000..5b66b7c6 --- /dev/null +++ b/src/algorithms/math/is-power-of-two/README.md @@ -0,0 +1,52 @@ +# Is a power of two + +Given a positive integer, write a function to find if it is +a power of two or not. + +**Naive solution** + +In naive solution we just keep dividing the number by two +unless the number becomes `1` and every time we do so we +check that remainder after division is always `0`. Otherwise +the number can't be a power of two. + +**Bitwise solution** + +Powers of two in binary form always have just one bit. +The only exception is with a signed integer (e.g. an 8-bit +signed integer with a value of -128 looks like: `10000000`) + +``` +1: 0001 +2: 0010 +4: 0100 +8: 1000 +``` + +So after checking that the number is greater than zero, +we can use a bitwise hack to test that one and only one +bit is set. + +``` +number & (number - 1) +``` + +For example for number `8` that operations will look like: + +``` + 1000 +- 0001 + ---- + 0111 + + 1000 +& 0111 + ---- + 0000 +``` + +## References + +- [GeeksForGeeks](https://www.geeksforgeeks.org/program-to-find-whether-a-no-is-power-of-two/) +- [Bitwise Solution on Stanford](http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2) +- [Binary number subtraction on YouTube](https://www.youtube.com/watch?v=S9LJknZTyos&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=66) diff --git a/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js new file mode 100644 index 00000000..69058943 --- /dev/null +++ b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwo.test.js @@ -0,0 +1,30 @@ +import isPowerOfTwo from '../isPowerOfTwo'; + +describe('isPowerOfTwo', () => { + it('should throw an exception when trying to apply function to negative number', () => { + const isNegativePowerOfTwo = () => { + isPowerOfTwo(-1); + }; + + expect(isNegativePowerOfTwo).toThrowError(); + }); + + it('should check if the number is made by multiplying twos', () => { + expect(isPowerOfTwo(0)).toBeFalsy(); + expect(isPowerOfTwo(1)).toBeFalsy(); + expect(isPowerOfTwo(2)).toBeTruthy(); + expect(isPowerOfTwo(3)).toBeFalsy(); + expect(isPowerOfTwo(4)).toBeTruthy(); + expect(isPowerOfTwo(5)).toBeFalsy(); + expect(isPowerOfTwo(6)).toBeFalsy(); + expect(isPowerOfTwo(7)).toBeFalsy(); + expect(isPowerOfTwo(8)).toBeTruthy(); + expect(isPowerOfTwo(10)).toBeFalsy(); + expect(isPowerOfTwo(12)).toBeFalsy(); + expect(isPowerOfTwo(16)).toBeTruthy(); + expect(isPowerOfTwo(31)).toBeFalsy(); + expect(isPowerOfTwo(64)).toBeTruthy(); + expect(isPowerOfTwo(1024)).toBeTruthy(); + expect(isPowerOfTwo(1023)).toBeFalsy(); + }); +}); diff --git a/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwoBitwise.test.js b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwoBitwise.test.js new file mode 100644 index 00000000..9f6ef06e --- /dev/null +++ b/src/algorithms/math/is-power-of-two/__test__/isPowerOfTwoBitwise.test.js @@ -0,0 +1,30 @@ +import isPowerOfTwoBitwise from '../isPowerOfTwoBitwise'; + +describe('isPowerOfTwoBitwise', () => { + it('should throw an exception when trying to apply function to negative number', () => { + const isNegativePowerOfTwo = () => { + isPowerOfTwoBitwise(-1); + }; + + expect(isNegativePowerOfTwo).toThrowError(); + }); + + it('should check if the number is made by multiplying twos', () => { + expect(isPowerOfTwoBitwise(0)).toBeFalsy(); + expect(isPowerOfTwoBitwise(1)).toBeFalsy(); + expect(isPowerOfTwoBitwise(2)).toBeTruthy(); + expect(isPowerOfTwoBitwise(3)).toBeFalsy(); + expect(isPowerOfTwoBitwise(4)).toBeTruthy(); + expect(isPowerOfTwoBitwise(5)).toBeFalsy(); + expect(isPowerOfTwoBitwise(6)).toBeFalsy(); + expect(isPowerOfTwoBitwise(7)).toBeFalsy(); + expect(isPowerOfTwoBitwise(8)).toBeTruthy(); + expect(isPowerOfTwoBitwise(10)).toBeFalsy(); + expect(isPowerOfTwoBitwise(12)).toBeFalsy(); + expect(isPowerOfTwoBitwise(16)).toBeTruthy(); + expect(isPowerOfTwoBitwise(31)).toBeFalsy(); + expect(isPowerOfTwoBitwise(64)).toBeTruthy(); + expect(isPowerOfTwoBitwise(1024)).toBeTruthy(); + expect(isPowerOfTwoBitwise(1023)).toBeFalsy(); + }); +}); diff --git a/src/algorithms/math/is-power-of-two/isPowerOfTwo.js b/src/algorithms/math/is-power-of-two/isPowerOfTwo.js new file mode 100644 index 00000000..0663b4ee --- /dev/null +++ b/src/algorithms/math/is-power-of-two/isPowerOfTwo.js @@ -0,0 +1,30 @@ +/** + * @param {number} number + * @return {boolean} + */ +export default function isPowerOfTwo(number) { + // Don't work with negative numbers. + if (number < 0) { + throw new Error('Please provide positive number'); + } + + // 0 and 1 are not powers of two. + if (number <= 1) { + return false; + } + + // Let's find out if we can divide the number by two + // many times without remainder. + let dividedNumber = number; + while (dividedNumber !== 1) { + if (dividedNumber % 2 !== 0) { + // For every case when remainder isn't zero we can say that this number + // couldn't be a result of power of two. + return false; + } + + dividedNumber /= 2; + } + + return true; +} diff --git a/src/algorithms/math/is-power-of-two/isPowerOfTwoBitwise.js b/src/algorithms/math/is-power-of-two/isPowerOfTwoBitwise.js new file mode 100644 index 00000000..1433b825 --- /dev/null +++ b/src/algorithms/math/is-power-of-two/isPowerOfTwoBitwise.js @@ -0,0 +1,31 @@ +/** + * @param {number} number + * @return {boolean} + */ +export default function isPowerOfTwoBitwise(number) { + // Don't work with negative numbers. + if (number < 0) { + throw new Error('Please provide positive number'); + } + + // 0 and 1 are not powers of two. + if (number <= 1) { + return false; + } + + /* + * Powers of two in binary look like this: + * 1: 0001 + * 2: 0010 + * 4: 0100 + * 8: 1000 + * + * Note that there is always exactly 1 bit set. The only exception is with a signed integer. + * e.g. An 8-bit signed integer with a value of -128 looks like: + * 10000000 + * + * So after checking that the number is greater than zero, we can use a clever little bit + * hack to test that one and only one bit is set. + */ + return (number & (number - 1)) === 0; +}