{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8685ea3a",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import timeit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "id": "cd404fc3",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_style(figsize=(12, 6), labelsize=20, titlesize=24, ticklabelsize=14, **kwargs):\n",
    "   basic_style = {\n",
    "       'figure.figsize': figsize,\n",
    "       'axes.labelsize': labelsize,\n",
    "       'axes.titlesize': titlesize,\n",
    "       'xtick.labelsize': ticklabelsize,\n",
    "       'ytick.labelsize': ticklabelsize,\n",
    "       'axes.spines.top': False,\n",
    "       'axes.spines.right': False,\n",
    "       'axes.spines.left': True,\n",
    "       'axes.grid': True,\n",
    "       'axes.grid.axis': 'both',\n",
    "   }\n",
    "   basic_style.update(kwargs)\n",
    "   return plt.rc_context(rc=basic_style)\n",
    "\n",
    "\n",
    "def plot_time_increase(multipliers, timing, ax=None, color='red', ls='-'):    \n",
    "    if ax is None:\n",
    "        ax = plt.gca()\n",
    "    ax.plot(multipliers, timing, lw=3, ls=ls, color=color)\n",
    "    ax.set_xticks(list(range(1, multipliers[-1] + 2, 2)))\n",
    "    ax.set_xlabel('N')\n",
    "    ax.set_ylabel('Time (s)')\n",
    "    ax.set_xlim(0, multipliers[-1] + 1.1)\n",
    "    ax.set_ylim(0, 0.06)\n",
    "\n",
    "\n",
    "def measure_time_increase(func, words1, words2, max_mult=20, z=1):\n",
    "    timing = []\n",
    "    multipliers = list(range(1, max_mult + 1))\n",
    "    for mult in multipliers:\n",
    "        mult_words1 = words1 * mult\n",
    "        mult_words2 = words2 * mult\n",
    "        t = timeit.repeat(\n",
    "            'func(words1, words2)', \n",
    "            globals={'func': func, 'words1': mult_words1, 'words2': mult_words2}, \n",
    "            repeat=3,\n",
    "            number=3000\n",
    "        )\n",
    "        timing.append(min(t) * z)\n",
    "    return multipliers, timing"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "048881d0",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Example: Find common words"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2464a282",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Problem: given two lists of words, extract all the words that are in common"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "id": "b8d7a1eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "results = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 168,
   "id": "01d8c3d7",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 44840 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def two_loops(words1, words2):\n",
    "    common = []\n",
    "    for w in words1:\n",
    "        if w in words2:\n",
    "            common.append(w)\n",
    "    return common\n",
    "\n",
    "\n",
    "words1 = ['apple', 'orange', 'banana', 'melon', 'peach']\n",
    "words2 = ['orange', 'kiwi', 'avocado', 'apple', 'banana']\n",
    "multipliers, timing = measure_time_increase(two_loops, words1, words2)\n",
    "\n",
    "results['Two loops'] = (multipliers, timing)\n",
    "with plot_style(figsize=(6, 6), ticklabelsize=16, labelsize=22):\n",
    "    plot_time_increase(*results['Two loops'], ax=None, color='tab:blue', ls='-')\n",
    "    plt.legend(['Two loops', 'Sorted lists', 'Sets'], \n",
    "               title=None, fontsize=18, loc='center left', \n",
    "               bbox_to_anchor=(1, 0.5), frameon=False)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "944f65b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "common = []\n",
    "for w in words1:        # O(N)\n",
    "    if w in words2:       # O(N)\n",
    "        common.append(w)  # O(1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 169,
   "id": "68088136",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 57604 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def sorted_lists(words1, words2):\n",
    "    words1 = sorted(words1)\n",
    "    words2 = sorted(words2)\n",
    "\n",
    "    common = []\n",
    "    idx2 = 0\n",
    "    for w in words1:\n",
    "        while idx2 < len(words2) and words2[idx2] < w:\n",
    "            idx2 += 1\n",
    "\n",
    "        if idx2 >= len(words2):\n",
    "            break\n",
    "\n",
    "        if words2[idx2] == w:\n",
    "            common.append(w)\n",
    "            \n",
    "    return common\n",
    "\n",
    "words1 = ['apple', 'orange', 'banana', 'melon', 'peach']\n",
    "words2 = ['orange', 'kiwi', 'avocado', 'apple', 'banana']\n",
    "multipliers, timing = measure_time_increase(sorted_lists, words1, words2)\n",
    "\n",
    "results['Sorted lists'] = (multipliers, timing)\n",
    "with plot_style(figsize=(6, 6), ticklabelsize=16, labelsize=22):\n",
    "    plot_time_increase(*results['Two loops'], ax=None, color='tab:blue', ls='-')\n",
    "    plot_time_increase(*results['Sorted lists'], ax=None, color='tab:orange', ls='-')\n",
    "    plt.legend(['Two loops', 'Sorted lists', 'Sets'], \n",
    "               title=None, fontsize=18, loc='center left', \n",
    "               bbox_to_anchor=(1, 0.5), frameon=False)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13625669",
   "metadata": {},
   "outputs": [],
   "source": [
    "words1 = sorted(words1)     # O(N * log(N))\n",
    "words2 = sorted(words2)     # O(N * log(N))\n",
    "\n",
    "common = []\n",
    "idx2 = 0\n",
    "for w in words1:                                    # O(N)\n",
    "    while idx2 < len(words2) and words2[idx2] < w:  # O(N) in total\n",
    "        idx2 += 1\n",
    "\n",
    "    if idx2 >= len(words2): # O(1)\n",
    "        break\n",
    "\n",
    "    if words2[idx2] == w:   # O(1)\n",
    "        common.append(w)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "id": "d7a6a1be",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 61740 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def sets(words1, words2):\n",
    "    words2 = set(words2)\n",
    "\n",
    "    common = []\n",
    "    for w in words1:\n",
    "        if w in words2:\n",
    "            common.append(w)\n",
    "\n",
    "    return common\n",
    "\n",
    "\n",
    "words1 = ['apple', 'orange', 'banana', 'melon', 'peach']\n",
    "words2 = ['orange', 'kiwi', 'avocado', 'apple', 'banana']\n",
    "multipliers, timing = measure_time_increase(sets, words1, words2)\n",
    "\n",
    "results['Sets'] = (multipliers, timing)\n",
    "with plot_style(figsize=(6, 6), ticklabelsize=16, labelsize=22):\n",
    "    plot_time_increase(*results['Two loops'], ax=None, color='tab:blue', ls='-')\n",
    "    plot_time_increase(*results['Sorted lists'], ax=None, color='tab:orange', ls='-')\n",
    "    plot_time_increase(*results['Sets'], ax=None, color='tab:green', ls='-')\n",
    "    plt.legend(['Two loops', 'Sorted lists', 'Sets'], \n",
    "               title=None, fontsize=18, loc='center left', \n",
    "               bbox_to_anchor=(0.05, 0.85), frameon=True, facecolor='white', framealpha=0.8, edgecolor='white')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca3700a5",
   "metadata": {},
   "outputs": [],
   "source": [
    "words2 = set(words2)      # O(N)\n",
    "\n",
    "common = []\n",
    "for w in words1:          # O(N)\n",
    "    if w in words2:          # O(1)\n",
    "        common.append(w)     # O(1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93c8e164",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 172,
   "id": "2c760f1c",
   "metadata": {},
   "outputs": [],
   "source": [
    "words1 = ['apple', 'orange', 'banana', 'melon', 'peach']\n",
    "words2 = ['orange', 'kiwi', 'avocado', 'apple', 'banana']\n",
    "multipliers, timing = measure_time_increase(two_loops, words1, words2, z=0.6)\n",
    "\n",
    "results['Two loops (fast)'] = (multipliers, timing)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 197,
   "id": "57cff0ad",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 69724 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with plot_style(figsize=(6, 6), ticklabelsize=16, labelsize=22):\n",
    "    plot_time_increase(*results['Two loops'], ax=None, color='tab:blue', ls='-')\n",
    "    plot_time_increase(*results['Sorted lists'], ax=None, color='tab:orange', ls='-')\n",
    "    plot_time_increase(*results['Sets'], ax=None, color='tab:green', ls='-')\n",
    "    plot_time_increase(*results['Two loops (fast)'], ax=None, color='tab:blue', ls='--')\n",
    "    plt.legend(['Two loops', 'Sorted lists', 'Sets'], \n",
    "               title=None, fontsize=18, loc='center left', \n",
    "               bbox_to_anchor=(0.05, 0.85), frameon=True, facecolor='white', framealpha=0.8, edgecolor='white')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 182,
   "id": "0496ee17",
   "metadata": {},
   "outputs": [],
   "source": [
    "words1 = ['apple', 'orange', 'banana', 'melon', 'peach']\n",
    "words2 = ['orange', 'kiwi', 'avocado', 'apple', 'banana']\n",
    "results_longer = {}\n",
    "max_mult = 40\n",
    "results_longer['Two loops'] = measure_time_increase(two_loops, words1, words2, max_mult=max_mult)\n",
    "results_longer['Two loops (fast)'] = measure_time_increase(two_loops, words1, words2, z=0.6, max_mult=max_mult)\n",
    "results_longer['Sorted lists'] = measure_time_increase(sorted_lists, words1, words2, max_mult=max_mult)\n",
    "results_longer['Sets'] = measure_time_increase(sets, words1, words2, max_mult=max_mult)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 201,
   "id": "de3857e9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 77784 characters]",
      "text/plain": [
       "<Figure size 600x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with plot_style(figsize=(6, 6), ticklabelsize=16, labelsize=22):\n",
    "    plot_time_increase(*results_longer['Two loops'], ax=None, color='tab:blue', ls='-')\n",
    "    plot_time_increase(*results_longer['Sorted lists'], ax=None, color='tab:orange', ls='-')\n",
    "    plot_time_increase(*results_longer['Sets'], ax=None, color='tab:green', ls='-')\n",
    "    plot_time_increase(*results_longer['Two loops (fast)'], ax=None, color='tab:blue', ls='--')\n",
    "    plt.legend(['Two loops', 'Sorted lists', 'Sets'], \n",
    "               title=None, fontsize=18, loc='center left', \n",
    "               bbox_to_anchor=(0.05, 0.85), frameon=True, facecolor='white', framealpha=0.8, edgecolor='white')\n",
    "    plt.ylim(0, 0.2)\n",
    "    plt.xticks(list(range(1, max_mult + 2, 4)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 203,
   "id": "b512aa6d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 96468 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with plot_style(figsize=(12, 6), ticklabelsize=16, labelsize=22):\n",
    "    plot_time_increase(*results_longer['Two loops'], ax=None, color='tab:blue', ls='-')\n",
    "    plot_time_increase(*results_longer['Sorted lists'], ax=None, color='tab:orange', ls='-')\n",
    "    plot_time_increase(*results_longer['Sets'], ax=None, color='tab:green', ls='-')\n",
    "    plot_time_increase(*results_longer['Two loops (fast)'], ax=None, color='tab:blue', ls='--')\n",
    "    plt.legend(['Two loops', 'Sorted lists', 'Sets'], \n",
    "               title=None, fontsize=18, loc='center left', \n",
    "               bbox_to_anchor=(0.05, 0.85), frameon=True, facecolor='white', framealpha=0.8, edgecolor='white')\n",
    "    plt.ylim(0, 0.2)\n",
    "    plt.xticks(list(range(1, max_mult + 2, 2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0263826e",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
