{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Nested loops\n", "\n", "The following two functions compute sum and product of matrices, respectively.\n", "\n", "By counting the nested loops it is easy to see that `add()` is $O(n^2)$ while `prod()` is $O(n^3)$." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time for add: 0.00012074300000008975\n", "Time for prod: 0.00036587199999971176\n" ] } ], "source": [ "from random import randint\n", "import time\n", "\n", "def add(A, B):\n", " S = [[0] * len(A) for i in range(len(A))]\n", " for i in range(len(A)):\n", " for j in range(len(A)):\n", " S[i][j] = A[i][j] + B[i][j]\n", " return S\n", "\n", "def prod(A, B):\n", " S = [[0] * len(A) for i in range(len(A))]\n", " for i in range(len(A)):\n", " for j in range(len(A)):\n", " for k in range(len(A)):\n", " S[i][j] = S[i][j] + A[i][k] * B[k][j]\n", " return S\n", "\n", "N = 10\n", "A = [ [randint(0,100) for i in range(N)] for j in range(N) ]\n", "B = [ [randint(0,100) for i in range(N)] for j in range(N) ]\n", "\n", "t0 = time.process_time()\n", "add(A,B)\n", "t1 = time.process_time()\n", "prod(A,B)\n", "t2 = time.process_time()\n", "\n", "print(\"Time for add: \", t1-t0)\n", "print(\"Time for prod:\", t2-t1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Sorting a list, slow version\n", "\n", "The following code implements a slow version of the so-called *insertion sort* alogithm\n", "\n", "Complexity: $O(n^2)$." ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running time: 1.1012288430000012\n" ] } ], "source": [ "from random import randint\n", "import time\n", "\n", "def correct_position(e, S):\n", " for i in range(len(S)):\n", " if S[i] > e:\n", " return i\n", " return len(S)\n", "\n", "def sort_list(L):\n", " S = []\n", " for e in L:\n", " cp = correct_position(e, S)\n", " S.insert(cp, e)\n", " return S\n", "\n", "N = 10000\n", "L = [randint(0,10**9) for i in range(N)]\n", "\n", "t0 = time.process_time()\n", "sort_list(L)\n", "t1 = time.process_time()\n", "\n", "print(\"Running time:\", t1-t0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Binary search\n", "\n", "The following code implements a binary search.\n", "\n", "Complexity: $O(\\log_2(n))$" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The correct position of e = 658230309 in L is:\n", "... 658211821 658224379 e 658234625 658246765 ...\n", "\n", "Time for sorting: 0.021211020999999164\n", "Time for searching: 7.820199999741817e-05\n" ] } ], "source": [ "from random import randint\n", "import time\n", "\n", "def binary_search(e, S, start, end):\n", " if start == end:\n", " return start\n", " midpoint = (start+end) // 2\n", " if e < S[midpoint]:\n", " return binary_search(e, S, start, midpoint)\n", " else:\n", " return binary_search(e, S, midpoint+1, end)\n", " \n", "N = 100000\n", "L = [randint(0,10**9) for i in range(N)]\n", "e = randint(0,10**9)\n", "\n", "t0 = time.process_time()\n", "L.sort() # Using Python's sort()\n", "t1 = time.process_time()\n", "i = binary_search(e, L, 0, len(L))\n", "t2 = time.process_time()\n", "print(\"The correct position of e =\", e, \"in L is:\")\n", "print(\"...\", L[i-2], L[i-1], \"e\", L[i], L[i+1], \"...\")\n", "print(\"\")\n", "print(\"Time for sorting: \", t1-t0)\n", "print(\"Time for searching:\", t2-t1)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Sorting a list, fast version (with binary_search)\n", "\n", "The following code uses the function `binary_search()` above instead of `correct_position()` in our insertion sort algorithm.\n", "\n", "Complexity: $O(n\\log_2(n))$" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running time: 0.03710268399998995\n" ] } ], "source": [ "from random import randint\n", "import time\n", "\n", "def binary_search(e, S, start, end):\n", " if start == end:\n", " return start\n", " midpoint = (start+end) // 2\n", " if e < S[midpoint]:\n", " return binary_search(e, S, start, midpoint)\n", " else:\n", " return binary_search(e, S, midpoint+1, end)\n", " \n", "def sort_list(L):\n", " S = []\n", " for e in L:\n", " cp = binary_search(e, S, 0, len(S)) # Changed here\n", " S.insert(cp, e)\n", " return S\n", " \n", "N = 10000\n", "L = [randint(0,10**9) for i in range(N)]\n", "\n", "t0 = time.process_time()\n", "sort_list(L)\n", "t1 = time.process_time()\n", "\n", "print(\"Running time:\", t1-t0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Fast exponentiation\n", "\n", "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", "\n", "Complexity: $O(n)$ for the slow algorithm, $O(\\log_2(n))$ for the other two." ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.71828179834636\n", "2.7182817863957984\n", "2.7182817983473577\n", "Time for slow_power(): 3.234879998000004\n", "Time for fast_power(): 9.059099999575437e-05\n", "Time for Python's **: 0.00010159500000384014\n" ] } ], "source": [ "import time\n", "\n", "def slow_power(a, n):\n", " r = 1\n", " for i in range(n):\n", " r = r * a\n", " return r\n", "\n", "def fast_power(a, n):\n", " if n == 0:\n", " return 1\n", " if n%2 == 0:\n", " return fast_power(a*a, n//2)\n", " else:\n", " return a * fast_power(a, n-1)\n", "\n", "a = 1.00000001\n", "n = 100000000\n", "\n", "t0 = time.process_time()\n", "print(slow_power(a, n))\n", "t1 = time.process_time()\n", "print(fast_power(a, n))\n", "t2 = time.process_time()\n", "print(a**n)\n", "t3 = time.process_time()\n", "\n", "print(\"Time for slow_power():\", t1-t0)\n", "print(\"Time for fast_power():\", t2-t1)\n", "print(\"Time for Python's **: \", t3-t2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Fast gcd\n", "\n", "Complexity: $O(\\log_2(n))$" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "126\n", "Running time: 0.00017707599999994272\n" ] } ], "source": [ "import time\n", "\n", "def gcd(a, b):\n", " if b == 0:\n", " return a\n", " else:\n", " return gcd(b, a%b)\n", "\n", "t0 = time.process_time()\n", "print(gcd(155275387236018, 572335397352432))\n", "t1 = time.process_time()\n", "\n", "print(\"Running time:\", t1-t0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Fibonacci numbers\n", "\n", "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", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import time\n", "\n", "F_memorized = [-1] * (10**6)\n", "\n", "def F_slow(n):\n", " if n <= 1:\n", " return n\n", " else:\n", " return F_slow(n-1) + F_slow(n-2)\n", " \n", "def F_fast(n):\n", " if F_memorized[n] == -1:\n", " if n <= 1:\n", " F_memorized[n] = n\n", " else:\n", " F_memorized[n] = F_fast(n-1) + F_fast(n-2)\n", " \n", " return F_memorized[n]\n", "\n", "n = 40\n", "\n", "t0 = time.process_time()\n", "print(F_slow(n))\n", "t1 = time.process_time()\n", "print(F_fast(n))\n", "t2 = time.process_time()\n", "\n", "print(\"Time for F_slow:\", t1-t0)\n", "print(\"Time for F_fast:\", t2-t1)" ] } ], "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.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }