diff options
Diffstat (limited to '')
| -rw-r--r-- | src/Lecture7/notebook/X1-ComputationalComplexity-notebook.ipynb | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/src/Lecture7/notebook/X1-ComputationalComplexity-notebook.ipynb b/src/Lecture7/notebook/X1-ComputationalComplexity-notebook.ipynb new file mode 100644 index 0000000..18be171 --- /dev/null +++ b/src/Lecture7/notebook/X1-ComputationalComplexity-notebook.ipynb | |||
| @@ -0,0 +1,412 @@ | |||
| 1 | { | ||
| 2 | "cells": [ | ||
| 3 | { | ||
| 4 | "cell_type": "markdown", | ||
| 5 | "metadata": {}, | ||
| 6 | "source": [ | ||
| 7 | "# Nested loops\n", | ||
| 8 | "\n", | ||
| 9 | "The following two functions compute sum and product of matrices, respectively.\n", | ||
| 10 | "\n", | ||
| 11 | "By counting the nested loops it is easy to see that `add()` is $O(n^2)$ while `prod()` is $O(n^3)$." | ||
| 12 | ] | ||
| 13 | }, | ||
| 14 | { | ||
| 15 | "cell_type": "code", | ||
| 16 | "execution_count": 3, | ||
| 17 | "metadata": {}, | ||
| 18 | "outputs": [ | ||
| 19 | { | ||
| 20 | "name": "stdout", | ||
| 21 | "output_type": "stream", | ||
| 22 | "text": [ | ||
| 23 | "Time for add: 0.01961983600000039\n", | ||
| 24 | "Time for prod: 11.278560734\n" | ||
| 25 | ] | ||
| 26 | } | ||
| 27 | ], | ||
| 28 | "source": [ | ||
| 29 | "from random import randint\n", | ||
| 30 | "import time\n", | ||
| 31 | "\n", | ||
| 32 | "def add(A, B):\n", | ||
| 33 | " S = [[0] * len(A) for i in range(len(A))]\n", | ||
| 34 | " for i in range(len(A)):\n", | ||
| 35 | " for j in range(len(A)):\n", | ||
| 36 | " S[i][j] = A[i][j] + B[i][j]\n", | ||
| 37 | " return S\n", | ||
| 38 | "\n", | ||
| 39 | "def prod(A, B):\n", | ||
| 40 | " S = [[0] * len(A) for i in range(len(A))]\n", | ||
| 41 | " for i in range(len(A)):\n", | ||
| 42 | " for j in range(len(A)):\n", | ||
| 43 | " for k in range(len(A)):\n", | ||
| 44 | " S[i][j] = S[i][j] + A[i][k] * B[k][j]\n", | ||
| 45 | " return S\n", | ||
| 46 | "\n", | ||
| 47 | "N = 400\n", | ||
| 48 | "A = [ [randint(0,100) for i in range(N)] for j in range(N) ]\n", | ||
| 49 | "B = [ [randint(0,100) for i in range(N)] for j in range(N) ]\n", | ||
| 50 | "\n", | ||
| 51 | "t0 = time.process_time()\n", | ||
| 52 | "add(A,B)\n", | ||
| 53 | "t1 = time.process_time()\n", | ||
| 54 | "prod(A,B)\n", | ||
| 55 | "t2 = time.process_time()\n", | ||
| 56 | "\n", | ||
| 57 | "print(\"Time for add: \", t1-t0)\n", | ||
| 58 | "print(\"Time for prod:\", t2-t1)" | ||
| 59 | ] | ||
| 60 | }, | ||
| 61 | { | ||
| 62 | "cell_type": "markdown", | ||
| 63 | "metadata": {}, | ||
| 64 | "source": [ | ||
| 65 | "# Sorting a list, slow version\n", | ||
| 66 | "\n", | ||
| 67 | "The following code implements a slow version of the so-called *insertion sort* alogithm\n", | ||
| 68 | "\n", | ||
| 69 | "Complexity: $O(n^2)$." | ||
| 70 | ] | ||
| 71 | }, | ||
| 72 | { | ||
| 73 | "cell_type": "code", | ||
| 74 | "execution_count": 7, | ||
| 75 | "metadata": {}, | ||
| 76 | "outputs": [ | ||
| 77 | { | ||
| 78 | "name": "stdout", | ||
| 79 | "output_type": "stream", | ||
| 80 | "text": [ | ||
| 81 | "Running time: 1.2679741750000009\n" | ||
| 82 | ] | ||
| 83 | } | ||
| 84 | ], | ||
| 85 | "source": [ | ||
| 86 | "from random import randint\n", | ||
| 87 | "import time\n", | ||
| 88 | "\n", | ||
| 89 | "def correct_position(e, S):\n", | ||
| 90 | " for i in range(len(S)):\n", | ||
| 91 | " if S[i] > e:\n", | ||
| 92 | " return i\n", | ||
| 93 | " return len(S)\n", | ||
| 94 | "\n", | ||
| 95 | "def sort_list(L):\n", | ||
| 96 | " S = []\n", | ||
| 97 | " for e in L:\n", | ||
| 98 | " cp = correct_position(e, S)\n", | ||
| 99 | " S.insert(cp, e)\n", | ||
| 100 | " return S\n", | ||
| 101 | "\n", | ||
| 102 | "N = 10000\n", | ||
| 103 | "L = [randint(0,10**9) for i in range(N)]\n", | ||
| 104 | "\n", | ||
| 105 | "t0 = time.process_time()\n", | ||
| 106 | "sort_list(L)\n", | ||
| 107 | "#L.sort()\n", | ||
| 108 | "t1 = time.process_time()\n", | ||
| 109 | "\n", | ||
| 110 | "print(\"Running time:\", t1-t0)" | ||
| 111 | ] | ||
| 112 | }, | ||
| 113 | { | ||
| 114 | "cell_type": "markdown", | ||
| 115 | "metadata": {}, | ||
| 116 | "source": [ | ||
| 117 | "# Binary search\n", | ||
| 118 | "\n", | ||
| 119 | "The following code implements a binary search.\n", | ||
| 120 | "\n", | ||
| 121 | "Complexity: $O(\\log_2(n))$" | ||
| 122 | ] | ||
| 123 | }, | ||
| 124 | { | ||
| 125 | "cell_type": "code", | ||
| 126 | "execution_count": 8, | ||
| 127 | "metadata": {}, | ||
| 128 | "outputs": [ | ||
| 129 | { | ||
| 130 | "name": "stdout", | ||
| 131 | "output_type": "stream", | ||
| 132 | "text": [ | ||
| 133 | "The correct position of e = 36132116 in L is:\n", | ||
| 134 | "... 36130178 36131096 e 36132160 36132386 ...\n", | ||
| 135 | "\n", | ||
| 136 | "Time for sorting: 0.4964379069999971\n", | ||
| 137 | "Time for searching: 0.00012017000000241751\n" | ||
| 138 | ] | ||
| 139 | } | ||
| 140 | ], | ||
| 141 | "source": [ | ||
| 142 | "from random import randint\n", | ||
| 143 | "import time\n", | ||
| 144 | "\n", | ||
| 145 | "def binary_search(e, S, start, end):\n", | ||
| 146 | " if start == end:\n", | ||
| 147 | " return start\n", | ||
| 148 | " midpoint = (start+end) // 2\n", | ||
| 149 | " if e < S[midpoint]:\n", | ||
| 150 | " return binary_search(e, S, start, midpoint)\n", | ||
| 151 | " else:\n", | ||
| 152 | " return binary_search(e, S, midpoint+1, end)\n", | ||
| 153 | " \n", | ||
| 154 | "N = 1000000\n", | ||
| 155 | "L = [randint(0,10**9) for i in range(N)]\n", | ||
| 156 | "e = randint(0,10**9)\n", | ||
| 157 | "\n", | ||
| 158 | "t0 = time.process_time()\n", | ||
| 159 | "L.sort() # Using Python's sort()\n", | ||
| 160 | "t1 = time.process_time()\n", | ||
| 161 | "i = binary_search(e, L, 0, len(L))\n", | ||
| 162 | "t2 = time.process_time()\n", | ||
| 163 | "print(\"The correct position of e =\", e, \"in L is:\")\n", | ||
| 164 | "print(\"...\", L[i-2], L[i-1], \"e\", L[i], L[i+1], \"...\")\n", | ||
| 165 | "print(\"\")\n", | ||
| 166 | "print(\"Time for sorting: \", t1-t0)\n", | ||
| 167 | "print(\"Time for searching:\", t2-t1)\n" | ||
| 168 | ] | ||
| 169 | }, | ||
| 170 | { | ||
| 171 | "cell_type": "markdown", | ||
| 172 | "metadata": {}, | ||
| 173 | "source": [ | ||
| 174 | "# Sorting a list, fast version (with binary_search)\n", | ||
| 175 | "\n", | ||
| 176 | "The following code uses the function `binary_search()` above instead of `correct_position()` in our insertion sort algorithm.\n", | ||
| 177 | "\n", | ||
| 178 | "Complexity: $O(n\\log_2(n))$" | ||
| 179 | ] | ||
| 180 | }, | ||
| 181 | { | ||
| 182 | "cell_type": "code", | ||
| 183 | "execution_count": 11, | ||
| 184 | "metadata": {}, | ||
| 185 | "outputs": [ | ||
| 186 | { | ||
| 187 | "name": "stdout", | ||
| 188 | "output_type": "stream", | ||
| 189 | "text": [ | ||
| 190 | "Running time: 1.5683504970000008\n" | ||
| 191 | ] | ||
| 192 | } | ||
| 193 | ], | ||
| 194 | "source": [ | ||
| 195 | "from random import randint\n", | ||
| 196 | "import time\n", | ||
| 197 | "\n", | ||
| 198 | "def binary_search(e, S, start, end):\n", | ||
| 199 | " if start == end:\n", | ||
| 200 | " return start\n", | ||
| 201 | " midpoint = (start+end) // 2\n", | ||
| 202 | " if e < S[midpoint]:\n", | ||
| 203 | " return binary_search(e, S, start, midpoint)\n", | ||
| 204 | " else:\n", | ||
| 205 | " return binary_search(e, S, midpoint+1, end)\n", | ||
| 206 | " \n", | ||
| 207 | "def sort_list(L):\n", | ||
| 208 | " S = []\n", | ||
| 209 | " for e in L:\n", | ||
| 210 | " cp = binary_search(e, S, 0, len(S)) # Changed here\n", | ||
| 211 | " S.insert(cp, e)\n", | ||
| 212 | " return S\n", | ||
| 213 | " \n", | ||
| 214 | "N = 100000\n", | ||
| 215 | "L = [randint(0,10**9) for i in range(N)]\n", | ||
| 216 | "\n", | ||
| 217 | "t0 = time.process_time()\n", | ||
| 218 | "sort_list(L)\n", | ||
| 219 | "t1 = time.process_time()\n", | ||
| 220 | "\n", | ||
| 221 | "print(\"Running time:\", t1-t0)" | ||
| 222 | ] | ||
| 223 | }, | ||
| 224 | { | ||
| 225 | "cell_type": "markdown", | ||
| 226 | "metadata": {}, | ||
| 227 | "source": [ | ||
| 228 | "# Fast exponentiation\n", | ||
| 229 | "\n", | ||
| 230 | "The following cell contains two functions for computing $a^n$ ($n$ non-negative integer): a slow one that runs in $O(n)$ and a fast one that runs in $O(\\log_2(n))$. We compare these two also with Python's built-in operator `**`.\n", | ||
| 231 | "\n", | ||
| 232 | "Complexity: $O(n)$ for the slow algorithm, $O(\\log_2(n))$ for the other two." | ||
| 233 | ] | ||
| 234 | }, | ||
| 235 | { | ||
| 236 | "cell_type": "code", | ||
| 237 | "execution_count": 14, | ||
| 238 | "metadata": {}, | ||
| 239 | "outputs": [ | ||
| 240 | { | ||
| 241 | "name": "stdout", | ||
| 242 | "output_type": "stream", | ||
| 243 | "text": [ | ||
| 244 | "2.71828179834636\n", | ||
| 245 | "2.7182817863957984\n", | ||
| 246 | "2.7182817983473577\n", | ||
| 247 | "Time for slow_power(): 3.5567659670000005\n", | ||
| 248 | "Time for fast_power(): 0.00014241699999928414\n", | ||
| 249 | "Time for Python's **: 9.477100000054861e-05\n" | ||
| 250 | ] | ||
| 251 | } | ||
| 252 | ], | ||
| 253 | "source": [ | ||
| 254 | "import time\n", | ||
| 255 | "\n", | ||
| 256 | "def slow_power(a, n):\n", | ||
| 257 | " r = 1\n", | ||
| 258 | " for i in range(n):\n", | ||
| 259 | " r = r * a\n", | ||
| 260 | " return r\n", | ||
| 261 | "\n", | ||
| 262 | "def fast_power(a, n):\n", | ||
| 263 | " if n == 0:\n", | ||
| 264 | " return 1\n", | ||
| 265 | " if n%2 == 0:\n", | ||
| 266 | " return fast_power(a*a, n//2)\n", | ||
| 267 | " else:\n", | ||
| 268 | " return a * fast_power(a, n-1)\n", | ||
| 269 | "\n", | ||
| 270 | "a = 1.00000001\n", | ||
| 271 | "n = 100000000\n", | ||
| 272 | "\n", | ||
| 273 | "t0 = time.process_time()\n", | ||
| 274 | "print(slow_power(a, n))\n", | ||
| 275 | "t1 = time.process_time()\n", | ||
| 276 | "print(fast_power(a, n))\n", | ||
| 277 | "t2 = time.process_time()\n", | ||
| 278 | "print(a**n)\n", | ||
| 279 | "t3 = time.process_time()\n", | ||
| 280 | "\n", | ||
| 281 | "print(\"Time for slow_power():\", t1-t0)\n", | ||
| 282 | "print(\"Time for fast_power():\", t2-t1)\n", | ||
| 283 | "print(\"Time for Python's **: \", t3-t2)" | ||
| 284 | ] | ||
| 285 | }, | ||
| 286 | { | ||
| 287 | "cell_type": "markdown", | ||
| 288 | "metadata": {}, | ||
| 289 | "source": [ | ||
| 290 | "# Fast gcd\n", | ||
| 291 | "\n", | ||
| 292 | "Complexity: $O(\\log_2(n))$" | ||
| 293 | ] | ||
| 294 | }, | ||
| 295 | { | ||
| 296 | "cell_type": "code", | ||
| 297 | "execution_count": 15, | ||
| 298 | "metadata": {}, | ||
| 299 | "outputs": [ | ||
| 300 | { | ||
| 301 | "name": "stdout", | ||
| 302 | "output_type": "stream", | ||
| 303 | "text": [ | ||
| 304 | "126\n", | ||
| 305 | "Running time: 0.0002987290000007192\n" | ||
| 306 | ] | ||
| 307 | } | ||
| 308 | ], | ||
| 309 | "source": [ | ||
| 310 | "import time\n", | ||
| 311 | "\n", | ||
| 312 | "def gcd(a, b):\n", | ||
| 313 | " if b == 0:\n", | ||
| 314 | " return a\n", | ||
| 315 | " else:\n", | ||
| 316 | " return gcd(b, a%b)\n", | ||
| 317 | "\n", | ||
| 318 | "t0 = time.process_time()\n", | ||
| 319 | "print(gcd(155275387236018, 572335397352432))\n", | ||
| 320 | "t1 = time.process_time()\n", | ||
| 321 | "\n", | ||
| 322 | "print(\"Running time:\", t1-t0)" | ||
| 323 | ] | ||
| 324 | }, | ||
| 325 | { | ||
| 326 | "cell_type": "markdown", | ||
| 327 | "metadata": {}, | ||
| 328 | "source": [ | ||
| 329 | "# Fibonacci numbers\n", | ||
| 330 | "\n", | ||
| 331 | "In the following cell there are two functions that compute the $n$-th Fibonacci number. They are almost the same, but the second one memorizes the results in a list to avoid computing them multiple times, and it is much much faster.\n", | ||
| 332 | "\n", | ||
| 333 | "Complexity: $O\\left(\\left(\\frac{1+\\sqrt 5}{2}\\right)^n\\right)\\sim O(1.6^n)$ for the slow version, $O(n)$ for the fast version." | ||
| 334 | ] | ||
| 335 | }, | ||
| 336 | { | ||
| 337 | "cell_type": "code", | ||
| 338 | "execution_count": 24, | ||
| 339 | "metadata": {}, | ||
| 340 | "outputs": [ | ||
| 341 | { | ||
| 342 | "name": "stdout", | ||
| 343 | "output_type": "stream", | ||
| 344 | "text": [ | ||
| 345 | "222232244629420445529739893461909967206666939096499764990979600\n", | ||
| 346 | "Time for F_slow: 3.0404000000316955e-05\n", | ||
| 347 | "Time for F_fast: 0.0003859389999973928\n" | ||
| 348 | ] | ||
| 349 | } | ||
| 350 | ], | ||
| 351 | "source": [ | ||
| 352 | "import time\n", | ||
| 353 | "\n", | ||
| 354 | "F_memorized = [-1] * (10**6)\n", | ||
| 355 | "\n", | ||
| 356 | "def F_slow(n):\n", | ||
| 357 | " if n <= 1:\n", | ||
| 358 | " return n\n", | ||
| 359 | " else:\n", | ||
| 360 | " return F_slow(n-1) + F_slow(n-2)\n", | ||
| 361 | " \n", | ||
| 362 | "def F_fast(n):\n", | ||
| 363 | " if F_memorized[n] == -1:\n", | ||
| 364 | " if n <= 1:\n", | ||
| 365 | " F_memorized[n] = n\n", | ||
| 366 | " else:\n", | ||
| 367 | " F_memorized[n] = F_fast(n-1) + F_fast(n-2)\n", | ||
| 368 | " \n", | ||
| 369 | " return F_memorized[n]\n", | ||
| 370 | "\n", | ||
| 371 | "n = 300\n", | ||
| 372 | "\n", | ||
| 373 | "t0 = time.process_time()\n", | ||
| 374 | "#print(F_slow(n))\n", | ||
| 375 | "t1 = time.process_time()\n", | ||
| 376 | "print(F_fast(n))\n", | ||
| 377 | "t2 = time.process_time()\n", | ||
| 378 | "\n", | ||
| 379 | "print(\"Time for F_slow:\", t1-t0)\n", | ||
| 380 | "print(\"Time for F_fast:\", t2-t1)" | ||
| 381 | ] | ||
| 382 | }, | ||
| 383 | { | ||
| 384 | "cell_type": "code", | ||
| 385 | "execution_count": null, | ||
| 386 | "metadata": {}, | ||
| 387 | "outputs": [], | ||
| 388 | "source": [] | ||
| 389 | } | ||
| 390 | ], | ||
| 391 | "metadata": { | ||
| 392 | "kernelspec": { | ||
| 393 | "display_name": "Python 3", | ||
| 394 | "language": "python", | ||
| 395 | "name": "python3" | ||
| 396 | }, | ||
| 397 | "language_info": { | ||
| 398 | "codemirror_mode": { | ||
| 399 | "name": "ipython", | ||
| 400 | "version": 3 | ||
| 401 | }, | ||
| 402 | "file_extension": ".py", | ||
| 403 | "mimetype": "text/x-python", | ||
| 404 | "name": "python", | ||
| 405 | "nbconvert_exporter": "python", | ||
| 406 | "pygments_lexer": "ipython3", | ||
| 407 | "version": "3.8.5" | ||
| 408 | } | ||
| 409 | }, | ||
| 410 | "nbformat": 4, | ||
| 411 | "nbformat_minor": 4 | ||
| 412 | } | ||
