diff --git a/bit_manipulation/get_next/__init__.py b/bit_manipulation/get_next/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bit_manipulation/get_next/get_next_challenge.ipynb b/bit_manipulation/get_next/get_next_challenge.ipynb new file mode 100644 index 0000000..265dafd --- /dev/null +++ b/bit_manipulation/get_next/get_next_challenge.ipynb @@ -0,0 +1,186 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Challenge Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Given a positive integer, get the next largest number and the next smallest number with the same number of 1's as the given number.\n", + "\n", + "* [Constraints](#Constraints)\n", + "* [Test Cases](#Test-Cases)\n", + "* [Algorithm](#Algorithm)\n", + "* [Code](#Code)\n", + "* [Unit Test](#Unit-Test)\n", + "* [Solution Notebook](#Solution-Notebook)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints\n", + "\n", + "* Is the output a positive int?\n", + " * Yes\n", + "* Can we assume the inputs are valid?\n", + " * No\n", + "* Can we assume this fits memory?\n", + " * Yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "* None -> Exception\n", + "* 0 -> Exception\n", + "* negative int -> Exception\n", + "* General case:\n", + "
\n", + " * Input: 0000 0000 1101 0111\n", + " * Next largest: 0000 0000 1101 1011\n", + " * Next smallest: 0000 0000 1100 1111\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "Refer to the [Solution Notebook](). If you are stuck and need a hint, the solution notebook's algorithm discussion might be a good place to start." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Bits(object):\n", + "\n", + " def get_next_largest(self, num):\n", + " # TODO: Implement me\n", + " pass\n", + "\n", + " def get_next_smallest(self, num):\n", + " # TODO: Implement me\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit Test" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**The following unit test is expected to fail until you solve the challenge.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# %load test_get_next_largest.py\n", + "from nose.tools import assert_equal, assert_raises\n", + "\n", + "\n", + "class TestBits(object):\n", + "\n", + " def test_get_next_largest(self):\n", + " bits = Bits()\n", + " assert_raises(Exception, bits.get_next_largest, None)\n", + " assert_raises(Exception, bits.get_next_largest, 0)\n", + " assert_raises(Exception, bits.get_next_largest, -1)\n", + " num = int('011010111', base=2)\n", + " expected = int('011011011', base=2)\n", + " assert_equal(bits.get_next_largest(num), expected)\n", + " print('Success: test_get_next_largest')\n", + "\n", + " def test_get_next_smallest(self):\n", + " bits = Bits()\n", + " assert_raises(Exception, bits.get_next_smallest, None)\n", + " assert_raises(Exception, bits.get_next_smallest, 0)\n", + " assert_raises(Exception, bits.get_next_smallest, -1)\n", + " num = int('011010111', base=2)\n", + " expected = int('011001111', base=2)\n", + " assert_equal(bits.get_next_smallest(num), expected)\n", + " print('Success: test_get_next_smallest')\n", + "\n", + "def main():\n", + " test = TestBits()\n", + " test.test_get_next_largest()\n", + " test.test_get_next_smallest()\n", + "\n", + "\n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solution Notebook\n", + "\n", + "Review the [Solution Notebook]() for a discussion on algorithms and code solutions." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/bit_manipulation/get_next/get_next_solution.ipynb b/bit_manipulation/get_next/get_next_solution.ipynb new file mode 100644 index 0000000..fa87ce3 --- /dev/null +++ b/bit_manipulation/get_next/get_next_solution.ipynb @@ -0,0 +1,272 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Solution Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem: Given a positive integer, get the next largest number and the next smallest number with the same number of 1's as the given number.\n", + "\n", + "* [Constraints](#Constraints)\n", + "* [Test Cases](#Test-Cases)\n", + "* [Algorithm](#Algorithm)\n", + "* [Code](#Code)\n", + "* [Unit Test](#Unit-Test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints\n", + "\n", + "* Is the output a positive int?\n", + " * Yes\n", + "* Can we assume the inputs are valid?\n", + " * No\n", + "* Can we assume this fits memory?\n", + " * Yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "* None -> Exception\n", + "* 0 -> Exception\n", + "* negative int -> Exception\n", + "* General case:\n", + "
\n", + " * Input: 0000 0000 1101 0111\n", + " * Next largest: 0000 0000 1101 1011\n", + " * Next smallest: 0000 0000 1100 1111\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "### get_next_largest\n", + "\n", + "* Find the right-most non trailing zero, call this index\n", + " * We'll use a mask of 1 and do a logical right shift on a copy of num to examine each bit starting from the right\n", + " * Count the number of zeroes to the right of index\n", + " * While num & 1 == 0 and num_copy != 0:\n", + " * Increment number of zeroes\n", + " * Logical shift right num_copy\n", + " * Count the number of ones to the right of index\n", + " * While num & 1 == 1 and num_copy != 0:\n", + " * Increment number of ones\n", + " * Logical shift right num_copy\n", + " * The index will be the sum of number of ones and number of zeroes\n", + " * Set the index bit\n", + " * Clear all bits to the right of index\n", + " * Set bits starting from 0\n", + " * Only set (number of ones - 1) bits because we set index to 1\n", + "\n", + "We should make a note that Python does not have a logical right shift operator built in. We can either use a positive number of implement one for a 32 bit number:\n", + "\n", + " num % 0x100000000 >> n\n", + "\n", + "### get_next_smallest\n", + "\n", + "* The algorithm for finding the next smallest number is very similar to finding the next largest number\n", + " * Instead of finding the right-most non-trailing zero, we'll find the right most non-trailing one and clear it\n", + " * Clear all bits to the right of index\n", + " * Set bits starting at 0 to the number of ones to the right of index, plus one\n", + "\n", + "Complexity:\n", + "* Time: O(b), where b is the number of bits in num\n", + "* Space: O(b), where b is the number of bits in num" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Bits(object):\n", + "\n", + " def get_next_largest(self, num):\n", + " if num is None:\n", + " raise TypeError('num cannot be None')\n", + " if num <= 0:\n", + " raise ValueError('num cannot be 0 or negative')\n", + " num_ones = 0\n", + " num_zeroes = 0\n", + " num_copy = num\n", + " # We'll look for index, which is the right-most non-trailing zero\n", + " # Count number of zeroes to the right of index\n", + " while num_copy != 0 and num_copy & 1 == 0:\n", + " num_zeroes += 1\n", + " num_copy >>= 1\n", + " # Count number of ones to the right of index\n", + " while num_copy != 0 and num_copy & 1 == 1:\n", + " num_ones += 1\n", + " num_copy >>= 1\n", + " # Determine index and set the bit\n", + " index = num_zeroes + num_ones\n", + " num |= 1 << index\n", + " # Clear all bits to the right of index\n", + " num &= ~((1 << index) - 1)\n", + " # Set bits starting from 0\n", + " num |= ((1 << num_ones - 1) - 1)\n", + " return num\n", + "\n", + " def get_next_smallest(self, num):\n", + " if num is None:\n", + " raise TypeError('num cannot be None')\n", + " if num <= 0:\n", + " raise ValueError('num cannot be 0 or negative')\n", + " num_ones = 0\n", + " num_zeroes = 0\n", + " num_copy = num\n", + " # We'll look for index, which is the right-most non-trailing one\n", + " # Count number of zeroes to the right of index\n", + " while num_copy != 0 and num_copy & 1 == 1:\n", + " num_ones += 1\n", + " num_copy >>= 1\n", + " # Count number of zeroes to the right of index\n", + " while num_copy != 0 and num_copy & 1 == 0:\n", + " num_zeroes += 1\n", + " num_copy >>= 1\n", + " # Determine index and clear the bit\n", + " index = num_zeroes + num_ones\n", + " num &= ~(1 << index)\n", + " # Clear all bits to the right of index\n", + " num &= ~((1 << index) - 1)\n", + " # Set bits starting from 0\n", + " num |= (1 << num_ones + 1) - 1\n", + " return num" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit Test" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting test_get_next_largest.py\n" + ] + } + ], + "source": [ + "%%writefile test_get_next_largest.py\n", + "from nose.tools import assert_equal, assert_raises\n", + "\n", + "\n", + "class TestBits(object):\n", + "\n", + " def test_get_next_largest(self):\n", + " bits = Bits()\n", + " assert_raises(Exception, bits.get_next_largest, None)\n", + " assert_raises(Exception, bits.get_next_largest, 0)\n", + " assert_raises(Exception, bits.get_next_largest, -1)\n", + " num = int('011010111', base=2)\n", + " expected = int('011011011', base=2)\n", + " assert_equal(bits.get_next_largest(num), expected)\n", + " print('Success: test_get_next_largest')\n", + "\n", + " def test_get_next_smallest(self):\n", + " bits = Bits()\n", + " assert_raises(Exception, bits.get_next_smallest, None)\n", + " assert_raises(Exception, bits.get_next_smallest, 0)\n", + " assert_raises(Exception, bits.get_next_smallest, -1)\n", + " num = int('011010111', base=2)\n", + " expected = int('011001111', base=2)\n", + " assert_equal(bits.get_next_smallest(num), expected)\n", + " print('Success: test_get_next_smallest')\n", + "\n", + "def main():\n", + " test = TestBits()\n", + " test.test_get_next_largest()\n", + " test.test_get_next_smallest()\n", + "\n", + "\n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success: test_get_next_largest\n", + "Success: test_get_next_smallest\n" + ] + } + ], + "source": [ + "%run -i test_get_next_largest.py" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/bit_manipulation/get_next/test_get_next_largest.py b/bit_manipulation/get_next/test_get_next_largest.py new file mode 100644 index 0000000..0fd11e2 --- /dev/null +++ b/bit_manipulation/get_next/test_get_next_largest.py @@ -0,0 +1,33 @@ +from nose.tools import assert_equal, assert_raises + + +class TestBits(object): + + def test_get_next_largest(self): + bits = Bits() + assert_raises(Exception, bits.get_next_largest, None) + assert_raises(Exception, bits.get_next_largest, 0) + assert_raises(Exception, bits.get_next_largest, -1) + num = int('011010111', base=2) + expected = int('011011011', base=2) + assert_equal(bits.get_next_largest(num), expected) + print('Success: test_get_next_largest') + + def test_get_next_smallest(self): + bits = Bits() + assert_raises(Exception, bits.get_next_smallest, None) + assert_raises(Exception, bits.get_next_smallest, 0) + assert_raises(Exception, bits.get_next_smallest, -1) + num = int('011010111', base=2) + expected = int('011001111', base=2) + assert_equal(bits.get_next_smallest(num), expected) + print('Success: test_get_next_smallest') + +def main(): + test = TestBits() + test.test_get_next_largest() + test.test_get_next_smallest() + + +if __name__ == '__main__': + main() \ No newline at end of file