{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "# Search\n",
        "\n",
        "Consider the following situations :\n",
        "\n",
        "1.   A social media network portal verifying login details\n",
        "2.   Grainger library textbook look up\n",
        "3.   Sifting through a pile of clothes looking for a specific piece of clothing\n",
        "4.   Looking for your favorite music video on youtube\n",
        "\n",
        "\n",
        "Common denominator : **Search**\n",
        "\n"
      ],
      "metadata": {
        "id": "EqS8xAhNsM5N"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Search for 277 in the following lists :"
      ],
      "metadata": {
        "id": "i-t5k1LsyuvN"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import random\n",
        "\n",
        "x = 10\n",
        "li = random.sample(range(1,1000),x)\n",
        "print(li)\n",
        "\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "collapsed": true,
        "id": "67tWoxZjAW0U",
        "outputId": "69206d1f-c887-4aee-e252-cebd3817c778"
      },
      "execution_count": 345,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[386, 296, 604, 491, 234, 46, 116, 502, 499, 187]\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import random\n",
        "\n",
        "x = 30\n",
        "li = random.sample(range(1,1000),x)\n",
        "print(li)\n",
        "\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "collapsed": true,
        "id": "SMoudiQBGgY5",
        "outputId": "1bc0fea7-9438-48a0-c62e-b229a36a8465"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[608, 272, 823, 354, 139, 325, 10, 590, 560, 22, 355, 262, 909, 554, 384, 153, 871, 476, 812, 134, 723, 931, 951, 854, 327, 421, 688, 346, 835, 55]\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import random\n",
        "\n",
        "x = 100\n",
        "li = random.sample(range(1,1000),x)\n",
        "print(li)\n",
        "\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "KC-5GEjVGjF5",
        "outputId": "6953b13d-c996-4417-ef8a-66954a7cf60e"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[347, 804, 580, 26, 663, 304, 876, 653, 849, 85, 621, 859, 375, 368, 20, 242, 563, 498, 95, 226, 288, 609, 165, 420, 983, 308, 277, 538, 541, 838, 811, 629, 392, 311, 932, 131, 978, 363, 155, 922, 973, 666, 591, 707, 578, 544, 570, 105, 82, 189, 306, 168, 2, 758, 503, 186, 8, 598, 360, 865, 217, 272, 781, 731, 595, 456, 409, 179, 880, 782, 457, 852, 550, 572, 303, 4, 97, 430, 756, 649, 435, 835, 300, 505, 483, 528, 438, 184, 990, 626, 576, 761, 122, 382, 267, 372, 330, 320, 123, 840]\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Basic search algorithm\n",
        "\n",
        "Main idea - Iteratively check if each element of the list equals the target. Return yes if there is a match at any point and no if you reach end of the list without a match."
      ],
      "metadata": {
        "id": "n0USN2C8Gq5U"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import random\n",
        "\n",
        "# Creating an input list of random numbers\n",
        "# generating a random list of size 30 in the range 1 to 1000 (1000 excluded)\n",
        "x = 30\n",
        "li = random.sample(range(1,1000),x)\n",
        "print(li)\n",
        "\n",
        "#basic search algorithm\n",
        "def basic_search(l,t):\n",
        "    for i in range(len(l)):\n",
        "      if(l[i] == t):\n",
        "        return (\"Element \" + str(t) + \" is present in the list\")\n",
        "    return (\"Element \" + str(t) + \" is not present in the list\")\n",
        "\n",
        "basic_search(li,277)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        },
        "collapsed": true,
        "id": "jcwr_WXHG_Vo",
        "outputId": "b3732740-db1c-4304-f0f8-ae427e6597fc"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[79, 597, 839, 976, 407, 877, 726, 105, 846, 490, 288, 162, 578, 647, 818, 724, 338, 242, 175, 408, 482, 549, 178, 443, 129, 344, 860, 641, 584, 281]\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'Element 277 is not present in the list'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 159
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Analysis\n",
        "\n",
        "Okay, so we gave some algorithm that apparently performs search on the random list generated above. Does it work correctly for all possible inputs?\n",
        "1.   Correctness - Does this algorithm work correctly on all inputs? How do we make such a claim?\n",
        "\n",
        "2.   Efficiency - What is the running time of this algorithm?\n",
        "\n",
        "3.   Space usage - How much additional space/memory does this algorithm need?\n",
        "\n"
      ],
      "metadata": {
        "id": "SleW432fJ6Os"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Time complexity analysis\n",
        "\n",
        "Since the running time depends on the size of the input, we indicate run time as a function of the *size* of the input.\n",
        "\n",
        "$n$ : Size of input list *li*\n",
        "\n",
        "In this case, the input has two arguments, a list of $n$ integers and a target number $t$.\n",
        "\n",
        "What is the running time of *basic_search*?\n",
        "\n",
        "\n",
        "\n",
        "*   Computations performed :  \n",
        "\n",
        "1.   iterating over elements of the input list *li* until $t$ is found\n",
        "2.   in each step, checking if current list element equals target $t$\n",
        "3.   at an appropriate moment, the return statement indicating presence/absence\n",
        "\n",
        "\n",
        "*   Exact run time :\n",
        "\n",
        "  Depends on whether $t$ is in the list.\n",
        "\n",
        "1. if $t$ is absent : exactly $n$ comparisons - total $(2n+1)$ operations.\n",
        "\n",
        "2. if $t$ is present : depends on the first location $k$ where $t$ occurs in the list - total $(2(k+1)+1) = (2k + 3)$ operations\n",
        "\n",
        "In any case, since $k \\leq (n-1)$, the total number of operations is at most $(2n+1)$.\n",
        "\n",
        "# Asymptotic run time\n",
        "\n",
        "Gives a clean asymptotic upper bound on the run time of the algorithm. Since the total number of operations is at most $(2n+1)$, we have:\n",
        "\n",
        "> The asymptotic run time of *basic_search* is $O(n)$.\n",
        "\n",
        "\n",
        "\n",
        "\n"
      ],
      "metadata": {
        "id": "O0r2xHTUR6cG"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Correctness\n",
        "\n",
        "We need to prove that the *basic_search* we wrote above works correctly on all possible inputs. How can we do that?\n",
        "\n",
        "There are two cases - either the list contains $t$ or it does not. We need to show that :\n",
        "\n",
        "\n",
        "\n",
        "*   if $t$ is in the list, *basic_search*  returns \"element present in list\".\n",
        "*   if $t$ is not in the list, *basic_search*  returns \"element not present in list\"."
      ],
      "metadata": {
        "id": "PXojNfmPMQyx"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "\n",
        "> Claim : If the first occurence of $t$ in the list is at position $k$, then the for loop runs exactly $(k+1)$ times and returns \"element present in list\".\n",
        "\n",
        "*Proof* - If the first occurence of $t$ is at position $k$, then the if condition fails for all $0 \\leq i \\leq (k-1)$. At the next iteration, with $i=k$, the if condition is successful as $li[k] = t$. So, the return statement is executed.  \n",
        "\n",
        "\n",
        "\n"
      ],
      "metadata": {
        "id": "VIEJ4p2ckXwM"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Is the proof of correctness complete?"
      ],
      "metadata": {
        "id": "j15va93GlY_-"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "\n",
        "\n",
        "> Claim 2 : If $t$ does not occur in the list at all, the for loop runs for exactly $n$ steps and returns \"element not in list\".\n",
        "\n",
        "*Proof* - If $t$ is not in the list, then the for loop in *basic_search* runs exactly $n$ times and since none of the elements of $li$ are equal to $t$, the loop ends and then returns \"element not in list\".\n",
        "\n"
      ],
      "metadata": {
        "id": "XDNzH3VJld0y"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "\n",
        "**Case - Doctor & Patient**\n",
        "\n",
        "Doctor X wants to look up the medical history of patient Y. How should X go about this task? Would it suffice to know whether X's medical records exist?\n",
        "\n",
        "Often, it is helpful to not just know if the target element is in the list, but to get access to the element.\n",
        "\n",
        "\n",
        "\n",
        "> Modify the search algorithm to help access an element if it is found. What change would you make?\n",
        "\n",
        "\n"
      ],
      "metadata": {
        "id": "OssVSIwbxRpY"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import random\n",
        "\n",
        "# Creating an input list of random numbers\n",
        "# generating a random list of size 30 in the range 1 to 1000 (1000 excluded)\n",
        "x = 30\n",
        "li = random.sample(range(1,1000),x)\n",
        "print(li)\n",
        "\n",
        "#EDIT this basic search algorithm to return access to the element\n",
        "def basic_search(l,t):\n",
        "    for i in range(len(l)):\n",
        "      if(l[i] == t):\n",
        "        return (\"Element \" + str(t) + \" is present in the list\")\n",
        "    return (\"Element \" + str(t) + \" is not present in the list\")\n",
        "\n",
        "basic_search(li,277)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        },
        "id": "NuzZfFAkyPPs",
        "outputId": "2e181939-26b1-401f-b18f-ddb6587af248"
      },
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[639, 417, 252, 169, 456, 218, 518, 769, 659, 396, 840, 655, 564, 615, 711, 161, 491, 430, 895, 303, 221, 547, 686, 581, 408, 386, 819, 796, 665, 93]\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'Element 277 is not present in the list'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 160
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "import random\n",
        "\n",
        "x = 30\n",
        "li = random.sample(range(1,1000),x)\n",
        "print(li)\n",
        "print(\"\")\n",
        "\n",
        "li2 = random.sample(range(1,1000),x)\n",
        "li2.sort()\n",
        "print(li2)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "KasTpvWlwoMc",
        "outputId": "d2b7717f-8563-45b1-f21c-454e8368966a"
      },
      "execution_count": 343,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[502, 831, 406, 711, 591, 370, 583, 58, 391, 710, 254, 582, 39, 426, 64, 514, 432, 441, 37, 328, 306, 573, 731, 392, 59, 418, 776, 336, 327, 245]\n",
            "\n",
            "[57, 73, 89, 106, 147, 171, 185, 263, 280, 299, 329, 367, 368, 438, 467, 488, 513, 542, 597, 605, 607, 611, 751, 765, 826, 904, 934, 959, 970, 986]\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "\n",
        "\n",
        "> Are both lists *li* and *li2* equally difficult to search through? Why?\n",
        "\n"
      ],
      "metadata": {
        "id": "PPWAK9UKlOiF"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Class Poll\n",
        "\n",
        "> Is *linear_search* the most efficient search algorithm?\n",
        "\n"
      ],
      "metadata": {
        "id": "yIg8vn4RwK_O"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Structured vs Unstructured Data\n",
        "\n",
        "\n",
        "\n",
        "*   If the data is structured (in this case sorted), then there are more efficient ways to search for an element.\n",
        "\n",
        "*   Main Idea - Pick some location and check for target. If the check fails, you can exclude one chunk of the list entirely. Now, search for the element in the remaining chunk.\n",
        "\n",
        "\n",
        "\n",
        "> **Which location should we pick to search within the list?**"
      ],
      "metadata": {
        "id": "NgPcxKcy0n8o"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "Candidate position : 5\n",
        "\n",
        "\n",
        "*   Case 1 -\n",
        "```\n",
        "if(li[5] == t): Search successful - nothing more to do!\n",
        "```\n",
        "\n",
        "*  Case 2 -\n",
        "```\n",
        "if(li[5] > t): we need to check li[0:5]  - total 5 elements left to check\n",
        "```\n",
        "\n",
        "*  Case 3 -\n",
        "```\n",
        "if(li[5] < t): we need to check li[5:] - total (n-6) elements left to check\n",
        "```\n",
        "\n",
        "So, in the *worst case* we may have only eliminated 5 elements from the search space!"
      ],
      "metadata": {
        "id": "316KfSuU3oCE"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Slice-and-Dice!\n",
        "\n",
        "Main idea : To search through structured data, we can first check the middle position (median) of the sorted list. Depending on the result of the check:\n",
        "\n",
        " focus search on either left half or right half of the list!\n",
        "\n",
        " *   Case 1 -\n",
        "```\n",
        "if(li[(n/2)] == t): Search successful - nothing more to do!\n",
        "```\n",
        "\n",
        "*  Case 2 -\n",
        "```\n",
        "if(li[n/2] > t): we need to check li[0:(n/2)]  - total {n/2} elements left to check\n",
        "```\n",
        "\n",
        "*  Case 3 -\n",
        "```\n",
        "if(li[n/2] < t): we need to check li[(n/2)+1:] - total {(n/2)-1} elements left to check\n",
        "```\n",
        "\n",
        "Let's write some code!\n"
      ],
      "metadata": {
        "id": "my55o4g-6Hdd"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import random\n",
        "\n",
        "#Generating an input list and sorting it\n",
        "x = 32\n",
        "li = random.sample(range(1,100),x)\n",
        "li.sort()\n",
        "print(li)\n",
        "\n",
        "#function slice_dice that slices search space in half at each step until target is found\n",
        "def slice_dice(l,t):\n",
        "  k = len(l)\n",
        "  print(l[k//2])\n",
        "  if(k == 1):\n",
        "    if(l[k//2] == t):\n",
        "      return \"Element is present in the list\"\n",
        "    else:\n",
        "      return \"Element is not in the list\"\n",
        "  if(l[k//2] == t):\n",
        "     return \"Element is present in the list\"\n",
        "\n",
        "  elif(l[k//2] > t):\n",
        "    return (slice_dice(l[0:k//2],t))\n",
        "\n",
        "  else:\n",
        "    return (slice_dice(l[(k//2)+1:],t))\n",
        "\n",
        "print(slice_dice(li,77))"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "EuQuZDC55_j3",
        "outputId": "26b3f4fe-7e2d-4669-8ed7-1ca02d53f7f4"
      },
      "execution_count": 335,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[3, 5, 6, 10, 11, 14, 15, 22, 24, 25, 26, 31, 35, 38, 40, 43, 45, 46, 47, 50, 56, 63, 64, 69, 70, 74, 76, 80, 86, 95, 96, 97]\n",
            "45\n",
            "70\n",
            "86\n",
            "76\n",
            "80\n",
            "Element is not in the list\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Slice and Dice analysis\n",
        "\n",
        "\n",
        "1.   Run time analysis - How much time does *slice_dice* take on lists of size $n$?\n",
        "\n",
        "2.   Correctness - Does *slice_dice* return the correct answer on all input lists (and targets)?\n",
        "\n"
      ],
      "metadata": {
        "id": "6zArei2HjmLg"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Run time analysis\n",
        "\n",
        "*   Is slice_dice as efficient as linear_search?\n",
        "\n",
        "\n",
        "*   Computations performed -\n",
        "\n",
        "Each call of *slice_dice* entails :\n",
        "\n",
        "1.   Computing the length of the list - $O(1)$ time in python\n",
        "2.   Accessing the middle element - $O(1)$ time\n",
        "3.   Comparing middle element with target - $O(1) * 3 = O(1)$ time\n",
        "\n",
        "Within each call of *slice_dice*, total time = $O(1) + O(1) + O(1)$ = $O(1)$ time.\n",
        "\n",
        "\n",
        "> **How many total calls does *slice_dice* make?**\n"
      ],
      "metadata": {
        "id": "2kZoMYlTnIZh"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Recurrences\n",
        "\n",
        "Let $f(n)$ denote the total number of calls of *slice_dice* on any input of size $n$.\n",
        "\n",
        "We want to understand what $f(n)$ is?\n",
        "\n",
        "What do we know?\n",
        "\n",
        "On inputs of size $n$, *slice_dice* checks if median is equal to target and -\n",
        "\n",
        "*   if (median == target) : *slice_dice* terminates\n",
        "*   else: *slice_dice* calls *slice_dice* on input of length $n/2$.\n",
        "\n",
        "So, $$f(n) \\leq 1 + f(n/2)$$\n",
        "\n",
        "Congratulations, we have written our first recurrence relation!"
      ],
      "metadata": {
        "id": "oxC46vcHssmR"
      }
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "vNHeYb9QnlEX"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}