From c06594871d348edad5b69ca67fe237741f1de512 Mon Sep 17 00:00:00 2001 From: Donne Martin Date: Thu, 30 Mar 2017 05:49:29 -0400 Subject: [PATCH] Add flip bit challenge --- bit_manipulation/flip_bit/__init__.py | 0 .../flip_bit/flip_bit_challenge.ipynb | 182 ++++++++++++ .../flip_bit/flip_bit_solution.ipynb | 275 ++++++++++++++++++ bit_manipulation/flip_bit/test_flip_bit.py | 29 ++ 4 files changed, 486 insertions(+) create mode 100644 bit_manipulation/flip_bit/__init__.py create mode 100644 bit_manipulation/flip_bit/flip_bit_challenge.ipynb create mode 100644 bit_manipulation/flip_bit/flip_bit_solution.ipynb create mode 100644 bit_manipulation/flip_bit/test_flip_bit.py diff --git a/bit_manipulation/flip_bit/__init__.py b/bit_manipulation/flip_bit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bit_manipulation/flip_bit/flip_bit_challenge.ipynb b/bit_manipulation/flip_bit/flip_bit_challenge.ipynb new file mode 100644 index 0000000..c755106 --- /dev/null +++ b/bit_manipulation/flip_bit/flip_bit_challenge.ipynb @@ -0,0 +1,182 @@ +{ + "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: Flip one bit from 0 to 1 to maximize the longest sequence of 1s.\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 input an int, base 2?\n", + " * Yes\n", + "* Can we assume the input is a 32 bit number?\n", + " * Yes\n", + "* Do we have to validate the length of the input?\n", + " * No\n", + "* Is the output an int?\n", + " * Yes\n", + "* Can we assume the inputs are valid?\n", + " * No\n", + "* Can we assume we are using a positive number since Python doesn't have an >>> operator?\n", + " * Yes\n", + "* Can we assume this fits memory?\n", + " * Yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "* None -> Exception\n", + "* All 1's -> Count of 1s\n", + "* All 0's -> 1\n", + "* General case\n", + " * 0000 1111 1101 1101 1111 0011 1111 0000 -> 10 (ten)" + ] + }, + { + "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 flip_bit(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_flip_bit.py\n", + "from nose.tools import assert_equal, assert_raises\n", + "\n", + "\n", + "class TestBits(object):\n", + "\n", + " def test_flip_bit(self):\n", + " bits = Bits()\n", + " assert_raises(TypeError, bits.flip_bit, None)\n", + " assert_equal(bits.flip_bit(0), 1)\n", + " assert_equal(bits.flip_bit(-1), bits.MAX_BITS)\n", + " num = int('00001111110111011110001111110000', base=2)\n", + " expected = 10\n", + " assert_equal(bits.flip_bit(num), expected)\n", + " num = int('00000100111011101111100011111011', base=2)\n", + " expected = 9\n", + " assert_equal(bits.flip_bit(num), expected)\n", + " num = int('00010011101110111110001111101111', base=2)\n", + " expected = 10\n", + " assert_equal(bits.flip_bit(num), expected)\n", + " print('Success: test_print_binary')\n", + "\n", + "\n", + "def main():\n", + " test = TestBits()\n", + " test.test_flip_bit()\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/flip_bit/flip_bit_solution.ipynb b/bit_manipulation/flip_bit/flip_bit_solution.ipynb new file mode 100644 index 0000000..6b7889f --- /dev/null +++ b/bit_manipulation/flip_bit/flip_bit_solution.ipynb @@ -0,0 +1,275 @@ +{ + "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: Flip one bit from 0 to 1 to maximize the longest sequence of 1s.\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 input an int, base 2?\n", + " * Yes\n", + "* Can we assume the input is a 32 bit number?\n", + " * Yes\n", + "* Do we have to validate the length of the input?\n", + " * No\n", + "* Is the output an int?\n", + " * Yes\n", + "* Can we assume the inputs are valid?\n", + " * No\n", + "* Can we assume we are using a positive number since Python doesn't have an >>> operator?\n", + " * Yes\n", + "* Can we assume this fits memory?\n", + " * Yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test Cases\n", + "\n", + "* None -> Exception\n", + "* All 1's -> Count of 1s\n", + "* All 0's -> 1\n", + "* General case\n", + " * Trailing zeroes\n", + " * 0000 1111 1101 1101 1111 0011 1111 0000 -> 10 (ten)\n", + " * Trailing ones\n", + " * 0000 1001 1101 1101 1111 0001 1111 0111 -> 9" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Algorithm\n", + "\n", + "* seen = []\n", + "* Build a list of sequence counts\n", + " * Look for 0's\n", + " * This will be 0 length if the input has trailing ones\n", + " * Add sequence length to seen\n", + " * Look for 1's\n", + " * Add sequence length to seen\n", + "* Find the largest sequence of ones looking at seen\n", + " * Loop through seen\n", + " * On each iteration of the loop, flip what we are looking for from 0 to 1 and vice versa\n", + " * If seen[i] represents 1's, continue, we only want to process 0's\n", + " * If this is our first iteration:\n", + " * max_result = seen[i+1] + 1 if seen[i] > 0\n", + " * continue\n", + " * If we are looking at leading zeroes (i == len(seen)-1):\n", + " * result = seen[i-1] + 1\n", + " * If we are looking at one zero:\n", + " * result = seen[i+1] + seen[i-1] + 1\n", + " * If we are looking at multiple zeroes:\n", + " * result = max(seen[i+1], seen[i-1]) + 1\n", + " * Update max_result based on result\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 or implement one for a 32 bit number:\n", + "\n", + " num % 0x100000000 >> n\n", + " \n", + "Complexity:\n", + "* Time: O(b)\n", + "* Space: O(b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Bits(object):\n", + "\n", + " MAX_BITS = 32\n", + " \n", + " def _build_seen_list(self, num):\n", + " seen = []\n", + " looking_for = 0\n", + " count = 0\n", + " for _ in range(self.MAX_BITS):\n", + " if num & 1 != looking_for:\n", + " seen.append(count)\n", + " looking_for = not looking_for\n", + " count = 0\n", + " count += 1\n", + " num >>= 1\n", + " seen.append(count)\n", + " return seen\n", + " \n", + " def flip_bit(self, num):\n", + " if num is None:\n", + " raise TypeError('num cannot be None')\n", + " if num == -1:\n", + " return self.MAX_BITS\n", + " if num == 0:\n", + " return 1\n", + " seen = self._build_seen_list(num)\n", + " max_result = 0\n", + " looking_for = 0\n", + " for index, count in enumerate(seen):\n", + " result = 0\n", + " # Only look for zeroes\n", + " if looking_for == 1:\n", + " looking_for = not looking_for\n", + " continue\n", + " # First iteration, take trailing zeroes\n", + " # or trailing ones into account\n", + " if index == 0:\n", + " if count != 0:\n", + " # Trailing zeroes\n", + " try:\n", + " result = seen[index + 1] + 1\n", + " except IndexError:\n", + " result = 1\n", + " # Last iteration\n", + " elif index == len(seen) - 1:\n", + " result = 1 + seen[index - 1]\n", + " else:\n", + " # One zero\n", + " if count == 1:\n", + " result = seen[index + 1] + seen[index - 1] + 1\n", + " # Multiple zeroes\n", + " else:\n", + " result = max(seen[index + 1], seen[index - 1]) + 1\n", + " if result > max_result:\n", + " max_result = result\n", + " looking_for = not looking_for\n", + " return max_result" + ] + }, + { + "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_flip_bit.py\n" + ] + } + ], + "source": [ + "%%writefile test_flip_bit.py\n", + "from nose.tools import assert_equal, assert_raises\n", + "\n", + "\n", + "class TestBits(object):\n", + "\n", + " def test_flip_bit(self):\n", + " bits = Bits()\n", + " assert_raises(TypeError, bits.flip_bit, None)\n", + " assert_equal(bits.flip_bit(0), 1)\n", + " assert_equal(bits.flip_bit(-1), bits.MAX_BITS)\n", + " num = int('00001111110111011110001111110000', base=2)\n", + " expected = 10\n", + " assert_equal(bits.flip_bit(num), expected)\n", + " num = int('00000100111011101111100011111011', base=2)\n", + " expected = 9\n", + " assert_equal(bits.flip_bit(num), expected)\n", + " num = int('00010011101110111110001111101111', base=2)\n", + " expected = 10\n", + " assert_equal(bits.flip_bit(num), expected)\n", + " print('Success: test_print_binary')\n", + "\n", + "\n", + "def main():\n", + " test = TestBits()\n", + " test.test_flip_bit()\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_print_binary\n" + ] + } + ], + "source": [ + "%run -i test_flip_bit.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/flip_bit/test_flip_bit.py b/bit_manipulation/flip_bit/test_flip_bit.py new file mode 100644 index 0000000..6d72d58 --- /dev/null +++ b/bit_manipulation/flip_bit/test_flip_bit.py @@ -0,0 +1,29 @@ +from nose.tools import assert_equal, assert_raises + + +class TestBits(object): + + def test_flip_bit(self): + bits = Bits() + assert_raises(TypeError, bits.flip_bit, None) + assert_equal(bits.flip_bit(0), 1) + assert_equal(bits.flip_bit(-1), bits.MAX_BITS) + num = int('00001111110111011110001111110000', base=2) + expected = 10 + assert_equal(bits.flip_bit(num), expected) + num = int('00000100111011101111100011111011', base=2) + expected = 9 + assert_equal(bits.flip_bit(num), expected) + num = int('00010011101110111110001111101111', base=2) + expected = 10 + assert_equal(bits.flip_bit(num), expected) + print('Success: test_print_binary') + + +def main(): + test = TestBits() + test.test_flip_bit() + + +if __name__ == '__main__': + main() \ No newline at end of file