Skip to content

Instantly share code, notes, and snippets.

@t-flora
Last active January 27, 2020 10:18
Show Gist options
  • Save t-flora/89589ba066b2d20411b59d9fc127e5eb to your computer and use it in GitHub Desktop.
Save t-flora/89589ba066b2d20411b59d9fc127e5eb to your computer and use it in GitHub Desktop.
CS110 3.1 – Incremental Maximum Subarray Method
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Before you turn this problem in, make sure everything runs as expected. First, **restart the kernel** (in the menubar, select Kernel$\\rightarrow$Restart) and then **run all cells** (in the menubar, select Cell$\\rightarrow$Run All).\n",
"\n",
"Make sure you fill in any place that says `YOUR CODE HERE` or \"YOUR ANSWER HERE\", as well as your name and collaborators below:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"NAME = \"Tiago Flora\"\n",
"COLLABORATORS = \"Chloe Gabrielle Go\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"nbgrader": {
"checksum": "6eb33d4bbb91bdad042e00eb02fae1ad",
"grade": false,
"grade_id": "cell-f941f4ddd5e342f7",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"# CS110 Pre-class Work 3.1\n",
"\n",
"## Question 1. \n",
"Paste in your Python implementation of the maximum subarray from the previous class in the cell below and use that to find out the value of the maximum subarray of this array: `A = [-2, -3, 4, -1, -2, 1]`"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"deletable": false,
"nbgrader": {
"checksum": "6c4991e90ad8cdfb8cf42529b7e5edd6",
"grade": true,
"grade_id": "cell-e69c8bbcd40ca242",
"locked": false,
"points": 0,
"schema_version": 1,
"solution": true
}
},
"outputs": [
{
"data": {
"text/plain": [
"(2, 2, 4)"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import math\n",
"\n",
"def bruteforce_max_subarray(A):\n",
" \"\"\"\n",
" Implements brute-force maximum subarray finding.\n",
" \n",
" Inputs:\n",
" - A: a NON-EMPTY list of floats\n",
" \n",
" Outputs: A tuple of\n",
" - the start index of the max subarray\n",
" - the end index of the max subarray\n",
" - the value of the maximum subarray\n",
" \"\"\"\n",
" maximum = -float(math.inf)\n",
" right_max = 0\n",
" left_max = 0\n",
" for i in range(len(A)):\n",
" # Every iteration of the loop will restart the sum of the subarray\n",
" subsum = 0\n",
" for j in range(i, len(A)):\n",
" subsum += A[j]\n",
" \"\"\"\n",
" Instead of changing the maximum subarray inside the if statement,\n",
" we can define it as max(subsum, maximum).\n",
" However, if we do this, we will get an AssertionError for the first test,\n",
" for the algorithm will choose to update the starting index of\n",
" the maximum subarray of the first assert test, which can be either 1 or 3.\n",
" \"\"\"\n",
"# maximum = max(subsum, maximum)\n",
" if maximum < subsum:\n",
" maximum = subsum\n",
" right_max = j\n",
" left_max = i\n",
" return((left_max, right_max, maximum))\n",
"\n",
"A = [-2,-3,4,-1,-2,1]\n",
"\n",
"bruteforce_max_subarray(A)"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"nbgrader": {
"checksum": "676f4558ca97298a50665d2b57563a54",
"grade": false,
"grade_id": "cell-6b2d39096ef80c67",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Question 2. \n",
"Now, your friend Joe comes and appends a single extra number at the end of the array, which becomes: `B = [-2, -3, 4, -1, -2, 1, 8]`. Do you have to re-run the entire maximum subarray again? Explain your answer. \n",
"The subsequent questions will help you figure out an efficient algorithmic strategy to address the last question, but make sure to write your explanation above first, before answering the remaining questions.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"nbgrader": {
"checksum": "b848c0f0fa07252cc99055a801b12e47",
"grade": true,
"grade_id": "cell-d4b7cd845c816263",
"locked": false,
"points": 0,
"schema_version": 1,
"solution": true
}
},
"source": [
"You do not. As you add a value at the end of the array, we could just check its value (positive or negative), and evaluate whether the maximum subarray, then, could be larger than it currently is. If the number is negative, it certainly cannot; but if it is positive, you can check from the starting position of the previous maximum subarray and sum values up to the end. If the resulting subarray yields a larger sum than the previous one, we can return it instead of the previous one."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"nbgrader": {
"checksum": "055be509a3fd9e61e64a6572949aa994",
"grade": false,
"grade_id": "cell-7280eecbaa455be1",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Question 3. \n",
"\n",
"**Determine if the following statement is True or False and explain your answer.**\n",
"If the maximum subarray of the array A is different than the maximum subarray of the array B (questions 1 and 2), the new maximum-subarray doesn’t need to include 8 (i.e., the newly appended element). \n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"nbgrader": {
"checksum": "2b29f3c119737d73a7578e00d43972a4",
"grade": true,
"grade_id": "cell-9377964a8756f13b",
"locked": false,
"points": 0,
"schema_version": 1,
"solution": true
}
},
"source": [
"For this specific case, the statement is False. The maximum subarray of A and B are only different because B includes 8, and thus B's maximum subarray needs to include it. In a general case, the maximum subarray including 8 or whatever last element appended to the original array is contingent on the inclusion generating a larger sum than previously."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"nbgrader": {
"checksum": "461bf61cb22c2304f3988c6f34a901e8",
"grade": false,
"grade_id": "cell-e7cc711ccdade69f",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Question 4.\n",
"Complete the Python function `incremental_max_subarray(x, mx)` in the cell below.\n",
"\n",
"This [video](https://www.youtube.com/watch?v=AAgErqQmwmA&list=PLF_a-qBXTGFektoI6JUOTRL36JlvD04BR&index=4&t=0s) might be helpful to understand the `incremental_max_subarray` problem."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"deletable": false,
"nbgrader": {
"checksum": "ac3f0799ce4ff7159403a97b99cbb5bd",
"grade": false,
"grade_id": "cell-0230e459f3d701f8",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"def incremental_max_subarray(x, mx):\n",
" \"\"\" \n",
" Inputs:\n",
" - x: a NON-EMPTY list of numbers (e.g., the array B in the first two questions above). \n",
" * If x has 1 element: returns the value of the element regardless of the value of mx\n",
" - mx: the maximum subarray of x excluding its last element (i.e., compute the \n",
" maximum subarray of the input array x considering only its first len(x) - 1 elements)\n",
" \n",
" Output: The maximum subarray of the array x.\n",
" \n",
" For example, using the array B in question 2, the result of incremental_max_subarray(B, 4) \n",
" is 10 (10 = 8 + 1 - 2 -1 + 4).\n",
" \"\"\"\n",
" if len(x) == 1: # We add the if statement for 1-element arrays\n",
" return(x[0])\n",
" else: # For n-element arrays where n>1\n",
" new_subsum = 0 # Initiate subarray sum counter\n",
" for j in range(len(x)-1, -1, -1): # Define the range of the for loop\n",
" new_subsum += x[j] # We add every object of the array to the sum counter\n",
" if new_subsum > mx: # If our sum surpasses the original maximum, we update it\n",
" mx = new_subsum\n",
" return(mx)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"deletable": false,
"editable": false,
"nbgrader": {
"checksum": "70a3880be02d6f703dfa95229957e71f",
"grade": true,
"grade_id": "cell-9abf19e56620ffa3",
"locked": true,
"points": 1,
"schema_version": 1,
"solution": false
},
"scrolled": true
},
"outputs": [],
"source": [
"B = [-2, -3, 4, -1, -2, 1, 8]\n",
"assert(incremental_max_subarray(B, 4) == 10)\n",
"assert(incremental_max_subarray(B[:1], 0) == B[0])"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"nbgrader": {
"checksum": "278d2e7036e916ce0fafffdd0c237584",
"grade": true,
"grade_id": "cell-bacebe71191cbd0f",
"locked": false,
"points": 0,
"schema_version": 1,
"solution": true
}
},
"source": [
"As we can see, the function returns the correct values when an array A has more than one element, and then disregards the second argument mx if len(A) == 1."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"deletable": false,
"nbgrader": {
"checksum": "d9cdd0a60c40e487e87d79d21915e36b",
"grade": false,
"grade_id": "cell-fd96d4ccccd99fe6",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"def max_subarray(A):\n",
" \"\"\"\n",
" Using `incremental_max_subarray` iteratively on A to produce the value of the maximum\n",
" subarray of A.\n",
" \n",
" Inputs:\n",
" - A: a NON-EMPTY list of floats\n",
" \n",
" Outputs: float, the sum of the maximum subarray of A\n",
" \"\"\"\n",
" x_i = A[:1] # Initialize an iteratively updated array\n",
" mx_i = A[0] # Initialize an iteratively updated maximum sum\n",
" for i in range(len(A)+1):\n",
" mx_i = incremental_max_subarray(x_i, mx_i) # At every iteration, we update the maximum subarray we have found\n",
" x_i = A[:i+1] # Increase the subarray of A by one element every iteration\n",
" return(mx_i)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"10"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"max_subarray(B)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"30"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"C = [-2,5,1,7,1,6,10,-11,3,6,-8]\n",
"max_subarray(C)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"deletable": false,
"editable": false,
"nbgrader": {
"checksum": "74149a9559625383203ec1320bff5558",
"grade": true,
"grade_id": "cell-669ad779766aa2c3",
"locked": true,
"points": 1,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"# Please ignore this cell. This cell is for us to implement the tests \n",
"# to see if your code works properly. "
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"nbgrader": {
"checksum": "03054d2ff22ec9310060ab534208ec0d",
"grade": false,
"grade_id": "cell-ae966fc92d098939",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"### Part 2.\n",
"Is this more efficient than the divide-and-conquer approach? Explain."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"nbgrader": {
"checksum": "69eb86a7f05367f6396017910664f67d",
"grade": true,
"grade_id": "cell-cd8f0b130a7136db",
"locked": false,
"points": 0,
"schema_version": 1,
"solution": true
}
},
"source": [
"We know from Cormen et al that the divide-and-conquer approach to the maximum subarray problem is $\\Theta(n\\log n)$. Given that our method includes two for loops, one implicit in the call for incremental_max_subarray() and another explicit in max_subarray(), we can expect max_subarray() to be $O(n^2)$. Thus, this algorithm would not be more efficient in terms of step count than the divide-and-conquer approach, as it will take more steps to run, asymptotically."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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iFwE/Aac7id/DxiO64I0A7gAQFU3ich9wHd+untDF9mBK3B3gCXhLqhX6qK6McbUadv/gDfOh4S9oO8YSKxIj9K7V513Z51VyqjBJQv8XVlXlfI5LwAvRCifi+un2xhjTCT5uS6BZG+H896q8gQCtefCugFiY2Pp3bs33bt3p1evXjz22GMUFLjWu4844ojdzl/aNHfffTePPPJIlcZalppeXk2bOnUq/fv3p1evXqSkpPDBBx8UjsvMzGTgwIHk55fd2+pee1X+n7k8y8jJyWHAgAHk5eVFHDb1xId3wG9z4KT/wL4HV8siLInUIklJSSxcuJAlS5Ywc+ZM3nvvPe655x4Avvpq991Tl2caUzpVLUzapXnttdd45JFHePvtt/n++++ZOHEiY8aMYfVqdyf6Cy+8wCmnnEJsbFV0Gx9ZeZaRkJDA4MGDef311yMOm3rghynwzTPQ/wo45LRqW4wlkVpqn332YezYsTz55JOoauGe680338zTTz9dON3dd9/No48+ChTfu33ggQfo1q0bQ4YM4aeffiosf/XVVznssMPo3bs3l112Wal7syNHjqRfv350796dsWPHApCamspBBx3EJZdcQvfu3Rk6dCiZmZllLq+kl19+mZ49e9KrVy/OO++8wvLHHnuMHj160KNHDx5//PEKLe+ss84qPPJJTU2lR4+is5yPPPIId999d6nrHlrGlVdeSd++fQuTQSQ7d+7klltuYfLkybRq1QqArl27MmjQID766CMAJkyYwIgRIwA4+eSTuf322zn66KNp1aoVs2ZF7pk20roD3HfffRx44IEce+yxxdYxfBllLWfkyJFMmDChcLqSw6YOW7cEpv8NOvwJht5XrYuqd6347s497yxh6ZptVfqZB7fZm7tO7F7h+bp06UJBQQHr168vLBs1ahTXXnstV155JQCTJ0/m/fffLzbfvHnzmDRpEgsWLCAvL4++ffvSr18/li1bxuuvv86XX35JfHw8V155JRMmTGD06NG7LPuFF16gWbNmZGZmcuihh3LqqacCsHz5ciZOnMi4ceM444wzmDp1KgcddFDE5ZW0ZMkSHnjgAb788ktatGjBpk2bCuN98cUX+eabb1BV+vfvz8CBA2natOkeLS9caes+YMAAfvrpJ1588cXC5Dx8+HCee+452rRpU+wzJk2aRN++fWnfvn2x8sTERLZu3UpOTg4rV64k1NXA4sWLOfLII5k9ezZvvvkmEyZMYMiQIbt8V5HWPT8/n6lTp+6yjiWXUdZyevTowXfffVc4XclhU0dlboFJ50Di3nD6SxBbvS0RWBKp5Ur2PNmnTx/Wr1/PmjVr2LBhA02bNt3lKfzZs2dz8sknk5ycDMBJJ50EwEcffcS8efM49NBDAXdufZ999iGSJ554gmnTpgGwevVqli9fTqtWrejcuTO9e/cGoF+/fqSmprJx48aIyyvp448/5rTTTqNFixYANGvWDIAvvviCk08+mYYNGwJwyimnMHv2bE466aQ9Wl640tZ9wIABdOzYkcMPP7xw2vfeey/iZyxevJhevXrtUv79998zZswYNm7cSJMmTQDIyMhg69atXHfddQDk5eUVjgtX2roXFBQwYsQIkpKSADjxxBMBii1jd8uJjY0lISGB7du306hRo12GTR1UUABvXgpb0+D8d6FRq2pfpCWREipzxFBdVq5cSWxs7C4V/WmnncaUKVP4448/GDVqVMR5Iz1ApqqMGTOGBx98sFj5U089xbhx4wBXgf7888/MmjWLOXPmkJyczKBBgwqfaE5MTCycLzY2tvD0UmkPrIV/9imnnFJqXKWp6PLi4uKKXdcIxV3auqemphZW4LvTuHFjsrOzi5XNmTOHbdu2MXDgQLZt21a4vCVLltCvX7/C6xaLFi0qdpotpLR1L608KSmp2NPlu1tOdnY2DRo0KHXY1DGfPQzLP4DjH4UO/WtkkXZNpJbasGEDl19+OX/96193qTBHjRrFpEmTmDJlCqedtusFswEDBjBt2jQyMzPZvn0777zzDgCDBw9mypQphafHNm3axK+//spVV13FwoULWbhwIW3atGHr1q00bdqU5ORkfvzxR77++usyYy1teUCxzz711FOZPHky6enphcsPzf/WW2+RkZHBzp07mTZtGkcffXSllrfvvvuyfv160tPTyc7OZsaMGWWue0WccMIJTJ48mQ0bNgDw888/c/HFF/Piiy8SGxtL06ZNyc/PJysri8WLFxceQYGr3Hv27BlxXSKt+1FHHcU777xDVlYWO3bs4N133wUotgygzOWkp6fTsmXLwoYVSw6bOubH9+Czh6DX2ZByUY0t1o5EapHMzEx69+5Nbm4ucXFxnHfeeVx//fW7TNe9e3e2b99O27Ztad269S7j+/bty5lnnknv3r3p2LFjYYV88MEHc//99zN06FAKCgqIj4/nqaeeomPHjsXmHzZsGM8++yw9e/akW7duxU71RFLa8iLF/Y9//IOBAwcSGxtLnz59eOmll+jbty/nn38+hx12GAAXX3wxffr0ITU1tcLLi4+P584776R///507tyZAw88sMx1D10gD1faNZGUlBTuuOMOBg8ejIjQuHFjnn322WLLHzp0KF988QU//PAD/fsX7QkuXrw44pFIaesO7jRdr1696NixIykpKTRu3LjYMoYMGVLmcj755BOGDx9eOK7ksKlDNi6HaZdBmz5wwr+gJpuyUdV69erXr5+WtHTp0l3KTPS466679J///GfQYaiq6vz58/Xcc8+tks/avn27qqru3LlT+/Xrp/PmzavQMk4++WT98ccfSx0OZ/8DUSxzq+p/UlQf7qK6ZXW1LAKYq6XUqXYkYkwV6tOnD8cccwz5+fl7/KzIpZdeytKlS8nKymLMmDH07du33MvIyclh5MiRdOvWLeKwqSMKCuCtKyD9Fxj9NjRut/t5qphoNTTIVZulpKTo3Llzi5UtW7aMgw46KKCIjAme/Q9EqU8fgk8fhGEPweFXVNtiRGSeqqZEGmcX1o0xJhr9+K5LIL3Ohv6XBxaGJRFjjIk26390z4O06VvzF9JLsCRijDHRJHMzTDob4pPhzFchPtjnfuzCujHGRIuCfJhyEWz5Dca8A43bBh2RJRFjjIkaM++EXz6CE/8NHf8UdDSAnc4yxpjo8P0kmPMkHHoJ9Ds/6GgKWRIxxpjaLm0eTL8aOh0Nwx7c/fQ1yJKIMcbUZtvWuAvpjVrB6eOrvWn3irIkYuqst956i0suuYQRI0bw4YcfBh2OMRWXm+kSSM4OOGsSNGwedES7sCRSi+xJv9tB2rJlS7HeFstS3nWsin7MR44cybhx43jppZcKu4W1vsZN1FB1vROuWQinjKu2PtL3lCWROkTL0Ud4dahIEimPqu7H/P777+eqq64CrK9xE0W+eAx+eAP+fDscWHtbX7YkUsuU1a847NpHeaQ+wkvrRz01NZUDDzyQiy++mB49enDOOecwa9YsjjzySLp27cq3334LlN0XeaS4brnlFn755Rd69+7NjTfeCETuo708KtqPOZTex7iqcvPNN3PccccVNl4Yis36Gje12rJ34KN74ZDT4egb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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"x = np.arange(1, 100, 1)\n",
"y = x*np.log2(x)\n",
"\n",
"y1 = x**2\n",
"\n",
"plt.plot(x,y, label = \"Divide-and-conquer: $O(n \\log(n))$\")\n",
"plt.plot(x,y1, label = \"Incremental: $O(n^2)$\")\n",
"plt.xlabel('n (in steps of 100)')\n",
"plt.ylabel('Steps')\n",
"plt.title('Number of steps against n for different \\n maximum subarray algorithms')\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"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.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment