mathsoftware

A course about LaTeX and SageMath
git clone https://git.tronto.net/mathsoftware
Download | Log | Files | Refs | README | LICENSE

X1-ComputationalComplexity-notebook.ipynb (10501B)


      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 }