From 3d49bb1d43f6cd5c81ba99df93304a64588037e7 Mon Sep 17 00:00:00 2001 From: Donne Martin Date: Thu, 29 Dec 2016 07:21:56 -0500 Subject: [PATCH] Update quicksort challenge (#134) Update constraints, test cases, code, algorithm, and tests. --- .../quick_sort/quick_sort_challenge.ipynb | 40 +++++---- .../quick_sort/quick_sort_solution.ipynb | 88 +++++++++++-------- .../quick_sort/test_quick_sort.py | 24 +++-- 3 files changed, 82 insertions(+), 70 deletions(-) diff --git a/sorting_searching/quick_sort/quick_sort_challenge.ipynb b/sorting_searching/quick_sort/quick_sort_challenge.ipynb index 3f98893..50debd9 100644 --- a/sorting_searching/quick_sort/quick_sort_challenge.ipynb +++ b/sorting_searching/quick_sort/quick_sort_challenge.ipynb @@ -34,9 +34,13 @@ "source": [ "## Constraints\n", "\n", - "* Is a naiive solution sufficient (ie not in-place)?\n", + "* Is a naive solution sufficient (ie not in-place)?\n", " * Yes\n", "* Are duplicates allowed?\n", + " * Yes\n", + "* Can we assume the input is valid?\n", + " * No\n", + "* Can we assume this fits memory?\n", " * Yes" ] }, @@ -46,7 +50,7 @@ "source": [ "## Test Cases\n", "\n", - "* None -> None\n", + "* None -> Exception\n", "* Empty input -> []\n", "* One element -> [element]\n", "* Two or more elements" @@ -76,9 +80,11 @@ }, "outputs": [], "source": [ - "def quick_sort(data):\n", - " # TODO: Implement me\n", - " pass" + "class QuickSort(object):\n", + "\n", + " def sort(self, data):\n", + " # TODO: Implement me\n", + " pass" ] }, { @@ -101,37 +107,33 @@ "outputs": [], "source": [ "# %load test_quick_sort.py\n", - "from nose.tools import assert_equal\n", + "from nose.tools import assert_equal, assert_raises\n", "\n", "\n", "class TestQuickSort(object):\n", - " def test_quick_sort(self, func):\n", + "\n", + " def test_quick_sort(self):\n", + " quick_sort = QuickSort()\n", + "\n", " print('None input')\n", - " data = None\n", - " sorted_data = func(data)\n", - " assert_equal(sorted_data, None)\n", + " assert_raises(TypeError, quick_sort.sort, None)\n", "\n", " print('Empty input')\n", - " data = []\n", - " sorted_data = func(data)\n", - " assert_equal(sorted_data, [])\n", + " assert_equal(quick_sort.sort([]), [])\n", "\n", " print('One element')\n", - " data = [5]\n", - " sorted_data = func(data)\n", - " assert_equal(sorted_data, [5])\n", + " assert_equal(quick_sort.sort([5]), [5])\n", "\n", " print('Two or more elements')\n", " data = [5, 1, 7, 2, 6, -3, 5, 7, -1]\n", - " sorted_data = func(data)\n", - " assert_equal(sorted_data, sorted(data))\n", + " assert_equal(quick_sort.sort(data), sorted(data))\n", "\n", " print('Success: test_quick_sort\\n')\n", "\n", "\n", "def main():\n", " test = TestQuickSort()\n", - " test.test_quick_sort(quick_sort)\n", + " test.test_quick_sort()\n", "\n", "\n", "if __name__ == '__main__':\n", diff --git a/sorting_searching/quick_sort/quick_sort_solution.ipynb b/sorting_searching/quick_sort/quick_sort_solution.ipynb index f90b953..5682e58 100644 --- a/sorting_searching/quick_sort/quick_sort_solution.ipynb +++ b/sorting_searching/quick_sort/quick_sort_solution.ipynb @@ -34,9 +34,13 @@ "source": [ "## Constraints\n", "\n", - "* Is a naiive solution sufficient (ie not in-place)?\n", + "* Is a naive solution sufficient (ie not in-place)?\n", " * Yes\n", "* Are duplicates allowed?\n", + " * Yes\n", + "* Can we assume the input is valid?\n", + " * No\n", + "* Can we assume this fits memory?\n", " * Yes" ] }, @@ -46,7 +50,7 @@ "source": [ "## Test Cases\n", "\n", - "* None -> None\n", + "* None -> Exception\n", "* Empty input -> []\n", "* One element -> [element]\n", "* Two or more elements" @@ -74,7 +78,16 @@ "* Time: O(n log(n)) average, best, O(n^2) worst\n", "* Space: O(n+m), n = number of elements, m = recursion depth\n", "\n", - "Most implementations are not stable." + "Misc:\n", + "\n", + "* More sophisticated implementations are in-place, although they still take up recursion depth space\n", + "* Most implementations are not stable\n", + "\n", + "See [Quicksort on wikipedia](https://en.wikipedia.org/wiki/Quicksort):\n", + "\n", + "Typically, quicksort is significantly faster in practice than other Θ(nlogn) algorithms, because its inner loop can be efficiently implemented on most architectures [presumably because it has good cache locality], and in most real-world data, it is possible to make design choices which minimize the probability of requiring quadratic time.\n", + "\n", + "See: [Quicksort vs merge sort](http://stackoverflow.com/questions/70402/why-is-quicksort-better-than-mergesort)" ] }, { @@ -95,28 +108,33 @@ "from __future__ import division\n", "\n", "\n", - "def quick_sort(data):\n", - " if data is None or len(data) < 2:\n", - " return data\n", - " equal = []\n", - " left = []\n", - " right = []\n", - " pivot_index = len(data) // 2\n", - " pivot_value = data[pivot_index]\n", + "class QuickSort(object):\n", "\n", - " # Build the left and right partitions\n", - " for i in range(len(data)):\n", - " if data[i] == pivot_value:\n", - " equal.append(data[i])\n", - " elif data[i] < pivot_value:\n", - " left.append(data[i])\n", - " else:\n", - " right.append(data[i])\n", + " def sort(self, data):\n", + " if data is None:\n", + " raise TypeError('data cannot be None')\n", + " return self._sort(data)\n", "\n", - " # Recursively apply quick_sort\n", - " left = quick_sort(left)\n", - " right = quick_sort(right)\n", - " return left + equal + right" + " def _sort(self, data):\n", + " if len(data) < 2:\n", + " return data\n", + " equal = []\n", + " left = []\n", + " right = []\n", + " pivot_index = len(data) // 2\n", + " pivot_value = data[pivot_index]\n", + " # Build the left and right partitions\n", + " for item in data:\n", + " if item == pivot_value:\n", + " equal.append(item)\n", + " elif item < pivot_value:\n", + " left.append(item)\n", + " else:\n", + " right.append(item)\n", + " # Recursively apply quick_sort\n", + " left_ = self._sort(left)\n", + " right_ = self._sort(right)\n", + " return left_ + equal + right_" ] }, { @@ -144,37 +162,33 @@ ], "source": [ "%%writefile test_quick_sort.py\n", - "from nose.tools import assert_equal\n", + "from nose.tools import assert_equal, assert_raises\n", "\n", "\n", "class TestQuickSort(object):\n", - " def test_quick_sort(self, func):\n", + "\n", + " def test_quick_sort(self):\n", + " quick_sort = QuickSort()\n", + "\n", " print('None input')\n", - " data = None\n", - " sorted_data = func(data)\n", - " assert_equal(sorted_data, None)\n", + " assert_raises(TypeError, quick_sort.sort, None)\n", "\n", " print('Empty input')\n", - " data = []\n", - " sorted_data = func(data)\n", - " assert_equal(sorted_data, [])\n", + " assert_equal(quick_sort.sort([]), [])\n", "\n", " print('One element')\n", - " data = [5]\n", - " sorted_data = func(data)\n", - " assert_equal(sorted_data, [5])\n", + " assert_equal(quick_sort.sort([5]), [5])\n", "\n", " print('Two or more elements')\n", " data = [5, 1, 7, 2, 6, -3, 5, 7, -1]\n", - " sorted_data = func(data)\n", - " assert_equal(sorted_data, sorted(data))\n", + " assert_equal(quick_sort.sort(data), sorted(data))\n", "\n", " print('Success: test_quick_sort\\n')\n", "\n", "\n", "def main():\n", " test = TestQuickSort()\n", - " test.test_quick_sort(quick_sort)\n", + " test.test_quick_sort()\n", "\n", "\n", "if __name__ == '__main__':\n", diff --git a/sorting_searching/quick_sort/test_quick_sort.py b/sorting_searching/quick_sort/test_quick_sort.py index f58965d..0915b37 100644 --- a/sorting_searching/quick_sort/test_quick_sort.py +++ b/sorting_searching/quick_sort/test_quick_sort.py @@ -1,34 +1,30 @@ -from nose.tools import assert_equal +from nose.tools import assert_equal, assert_raises class TestQuickSort(object): - def test_quick_sort(self, func): + + def test_quick_sort(self): + quick_sort = QuickSort() + print('None input') - data = None - sorted_data = func(data) - assert_equal(sorted_data, None) + assert_raises(TypeError, quick_sort.sort, None) print('Empty input') - data = [] - sorted_data = func(data) - assert_equal(sorted_data, []) + assert_equal(quick_sort.sort([]), []) print('One element') - data = [5] - sorted_data = func(data) - assert_equal(sorted_data, [5]) + assert_equal(quick_sort.sort([5]), [5]) print('Two or more elements') data = [5, 1, 7, 2, 6, -3, 5, 7, -1] - sorted_data = func(data) - assert_equal(sorted_data, sorted(data)) + assert_equal(quick_sort.sort(data), sorted(data)) print('Success: test_quick_sort\n') def main(): test = TestQuickSort() - test.test_quick_sort(quick_sort) + test.test_quick_sort() if __name__ == '__main__':