\n",
+ " \n",
+ " | \n",
+ " \n",
+ " Please read - important note\n", + " The way I collaborate with you may be different to other courses you've taken. I prefer not to type code while you watch. Rather, I execute Jupyter Labs, like this, and give you an intuition for what's going on. My suggestion is that you carefully execute this yourself, after watching the lecture. Add print statements to understand what's going on, and then come up with your own variations. If you have a Github account, use this to showcase your variations. Not only is this essential practice, but it demonstrates your skills to others, including perhaps future clients or employers...\n", + " | \n",
+ "
\n",
+ " \n",
+ " | \n",
+ " \n",
+ " This code is a live resource - keep an eye out for my emails\n", + " I push updates to the code regularly. As people ask questions, I add more examples or improved commentary. As a result, you'll notice that the code below isn't identical to the videos. Everything from the videos is here; but I've also added better explanations and new models like DeepSeek. Consider this like an interactive book.\n", + " I try to send emails regularly with important updates related to the course. You can find this in the 'Announcements' section of Udemy in the left sidebar. You can also choose to receive my emails via your Notification Settings in Udemy. I'm respectful of your inbox and always try to add value with my emails!\n", + " \n", + " | \n",
+ "
\n",
+ " \n",
+ " | \n",
+ " \n",
+ " Business value of these exercises\n", + " A final thought. While I've designed these notebooks to be educational, I've also tried to make them enjoyable. We'll do fun things like have LLMs tell jokes and argue with each other. But fundamentally, my goal is to teach skills you can apply in business. I'll explain business implications as we go, and it's worth keeping this in mind: as you build experience with models and techniques, think of ways you could put this into action at work today. Please do contact me if you'd like to discuss more or if you have ideas to bounce off me.\n", + " | \n",
+ "
\n",
+ " \n",
+ " | \n",
+ " \n",
+ " Business applications\n", + " In this exercise, you experienced calling the Cloud API of a Frontier Model (a leading model at the frontier of AI) for the first time. We will be using APIs like OpenAI at many stages in the course, in addition to building our own LLMs.\n", + "\n", + "More specifically, we've applied this to Summarization - a classic Gen AI use case to make a summary. This can be applied to any business vertical - summarizing the news, summarizing financial performance, summarizing a resume in a cover letter - the applications are limitless. Consider how you could apply Summarization in your business, and try prototyping a solution.\n", + " | \n",
+ "
\n",
+ " \n",
+ " | \n",
+ " \n",
+ " Before you continue - now try yourself\n", + " Use the cell below to make your own simple commercial example. Stick with the summarization use case for now. Here's an idea: write something that will take the contents of an email, and will suggest an appropriate short subject line for the email. That's the kind of feature that might be built into a commercial email tool.\n", + " | \n",
+ "
\n",
+ " \n",
+ " | \n",
+ " \n",
+ " Business applications\n", + " In this exercise we extended the Day 1 code to make multiple LLM calls, and generate a document.\n", + "\n", + "This is perhaps the first example of Agentic AI design patterns, as we combined multiple calls to LLMs. This will feature more in Week 2, and then we will return to Agentic AI in a big way in Week 8 when we build a fully autonomous Agent solution.\n", + "\n", + "Generating content in this way is one of the very most common Use Cases. As with summarization, this can be applied to any business vertical. Write marketing content, generate a product tutorial from a spec, create personalized email content, and so much more. Explore how you can apply content generation to your business, and try making yourself a proof-of-concept prototype. See what other students have done in the community-contributions folder -- so many valuable projects -- it's wild!\n", + " | \n",
+ "
\n",
+ " \n",
+ " | \n",
+ " \n",
+ " Before you move to Week 2 (which is tons of fun)\n", + " Please see the week1 EXERCISE notebook for your challenge for the end of week 1. This will give you some essential practice working with Frontier APIs, and prepare you well for Week 2.\n", + " | \n",
+ "
\n",
+ " \n",
+ " | \n",
+ " \n",
+ " A reminder on 3 useful resources\n", + " 1. The resources for the course are available here.\n", + " 2. I'm on LinkedIn here and I love connecting with people taking the course! \n", + " 3. I'm trying out X/Twitter and I'm at @edwarddonner and hoping people will teach me how it's done.. \n", + " \n", + " | \n",
+ "
\n",
+ " \n",
+ " | \n",
+ " \n",
+ " Finally! I have a special request for you\n", + " \n", + " My editor tells me that it makes a MASSIVE difference when students rate this course on Udemy - it's one of the main ways that Udemy decides whether to show it to others. If you're able to take a minute to rate this, I'd be so very grateful! And regardless - always please reach out to me at ed@edwarddonner.com if I can help at any point.\n", + " \n", + " | \n",
+ "
LLM Tutor initialized successfully!\n",
+ "\n"
+ ],
+ "text/plain": [
+ "\u001b[1;32mLLM Tutor initialized successfully!\u001b[0m\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "\n",
+ "# Create a tutor instance\n",
+ "tutor = LLMTutor()\n",
+ "console.print(\"[bold green]LLM Tutor initialized successfully![/bold green]\")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "id": "25a36470-a68f-40f6-bea1-d2ebb173c015",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ Question: │\n", + "│ │\n", + "│ Given a list of dictionaries called 'books', write code to find and print all information │\n", + "│ about the book titled 'Mastery' by Robert Greene. │\n", + "│ │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", + "\n" + ], + "text/plain": [ + "\u001b[34m╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[1mQuestion:\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m Given a list of dictionaries called 'books', write code to find and print all information \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m about the book titled 'Mastery' by Robert Greene. \u001b[34m│\u001b[0m\n", + "\u001b[34m│\u001b[0m \u001b[34m│\u001b[0m\n", + "\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "# Define your question here\n", + "question = \"\"\"\n", + "Given a list of dictionaries called 'books', write code to find and print all information \n", + "about the book titled 'Mastery' by Robert Greene.\n", + "\"\"\"\n", + "\n", + "console.print(Panel(f\"[bold]Question:[/bold]\\n{question}\", border_style=\"blue\"))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "bceaeaf9-4d08-4380-b757-597b851dd8ca", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+ "│ Getting response from gpt-4o-mini... │\n",
+ "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+ "\n"
+ ],
+ "text/plain": [
+ "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+ "│ \u001b[1;34mGetting response from gpt-4o-mini...\u001b[0m │\n",
+ "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "To find and print all information about the book titled \"Mastery\" by Robert Greene from a list of dictionaries called `books`, we can write a function that iterates through the list, checks for the specific title and author, and then prints the information if a match is found. Here's a step-by-step explanation followed by the code.\n",
+ "\n",
+ "### Steps to Follow:\n",
+ "\n",
+ "1. **Structure of the Data**: \n",
+ " Each book in the `books` list is a dictionary. We need to understand how the book's information is structured. A typical dictionary might look like this:\n",
+ " \n",
+ " {\n",
+ " 'title': 'Mastery',\n",
+ " 'author': 'Robert Greene',\n",
+ " 'year': 2012,\n",
+ " 'genre': 'Non-fiction',\n",
+ " 'isbn': '978-0143124177'\n",
+ " }\n",
+ " \n",
+ "\n",
+ "2. **Iterate through the List**:\n",
+ " We will use a loop to go through each book in the `books` list. \n",
+ "\n",
+ "3. **Check for Conditions**:\n",
+ " For each book (dictionary), we need to check if the 'title' is 'Mastery' and the 'author' is 'Robert Greene'. \n",
+ "\n",
+ "4. **Print the Details**: \n",
+ " If we find a match, we will print all the details of that book.\n",
+ "\n",
+ "### Example Code\n",
+ "\n",
+ "Here’s a Python code snippet that accomplishes this:\n",
+ "\n",
+ "\n",
+ "# Sample list of dictionaries representing books\n",
+ "books = [\n",
+ " {'title': 'Mastery', 'author': 'Robert Greene', 'year': 2012, 'genre': 'Non-fiction', 'isbn': '978-0143124177'},\n",
+ " {'title': 'The 48 Laws of Power', 'author': 'Robert Greene', 'year': 1998, 'genre': 'Non-fiction', 'isbn': '978-0140280197'},\n",
+ " {'title': 'The Art of War', 'author': 'Sun Tzu', 'year': '5th century BC', 'genre': 'Philosophy', 'isbn': '978-1590302255'}\n",
+ "]\n",
+ "\n",
+ "# Function to find and print information about the book titled 'Mastery' by Robert Greene\n",
+ "def find_book(books):\n",
+ " for book in books:\n",
+ " # Check if the title and author match\n",
+ " if book.get('title') == 'Mastery' and book.get('author') == 'Robert Greene':\n",
+ " # Print the entire dictionary if a match is found\n",
+ " print(\"Found book:\")\n",
+ " for key, value in book.items():\n",
+ " print(f\"{key}: {value}\")\n",
+ " return # Exit the function after finding the book\n",
+ " print(\"Book not found.\") # Optional: Print if the book is not in the list\n",
+ "\n",
+ "# Call the function\n",
+ "find_book(books)\n",
+ "\n",
+ "\n",
+ "### Explanation of the Code:\n",
+ "\n",
+ "1. **Data Structure**: The `books` variable is initialized as a list containing dictionary elements, where each dictionary represents a book.\n",
+ "\n",
+ "2. **Function Definition**: The function `find_book(books)` takes the list of books as an argument.\n",
+ "\n",
+ "3. **Iteration**: The `for` loop iterates over each book in the `books` list.\n",
+ "\n",
+ "4. **Finding the Match**: It checks if the title and author of the current book (retrieved using the `get` method to avoid `KeyError`) match 'Mastery' and 'Robert Greene'.\n",
+ "\n",
+ "5. **Printing Details**: If a match is found, it prints out the key-value pairs from the dictionary in a formatted manner.\n",
+ "\n",
+ "6. **Exit after Finding**: The `return` statement ensures that the function exits as soon as the book is found.\n",
+ "\n",
+ "7. **Not Found Condition**: If no book matches the criteria, it prints \"Book not found.\"\n",
+ "\n",
+ "### Conclusion\n",
+ "This method is efficient for small to moderately sized lists of dictionaries. If you have a very large dataset, consider using more efficient search algorithms or data structures like dictionaries for faster lookups, but the above approach should work well for typical use cases."
+ ],
+ "text/plain": [
+ "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+ "│ Getting response from llama3.2... │\n",
+ "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+ "\n"
+ ],
+ "text/plain": [
+ "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+ "│ \u001b[1;32mGetting response from llama3.2...\u001b[0m │\n",
+ "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "## llama3.2 Response\n",
+ "Here's an example of how you can achieve this using Python:\n",
+ "\n",
+ "**Problem Statement**\n",
+ "\n",
+ "Given a list of dictionaries called `books`, where each dictionary represents a book with its title, author, publication year, etc., write code to find and print all information about the book titled `'Mastery'` by Robert Greene.\n",
+ "\n",
+ "**Example Input Data**\n",
+ "```python\n",
+ "books = [\n",
+ " {'title': 'Mastery', 'author': 'Robert Greene', 'publication_year': 2012, 'genre': 'Self-Help'},\n",
+ " {'title': 'The 48 Laws of Power', 'author': 'Robert Greene', 'publication_year': 2007, 'genre': 'Non-Fiction'},\n",
+ " {'title': 'To Kill a Mockingbird', 'author': 'Harper Lee', 'publication_year': 1960, 'genre': 'Classic Fiction'},\n",
+ " {'title': 'Mastery', 'author': 'Robert Greene', 'publication_year': 2018, 'genre': 'Self-Help'} # duplicate title\n",
+ "]\n",
+ "```\n",
+ "**Solution**\n",
+ "\n",
+ "Here's the Python code that finds and prints all information about the book titled `'Mastery'` by Robert Greene:\n",
+ "```python\n",
+ "# Define a function to find books with a specific title and author\n",
+ "def find_book(books, title, author):\n",
+ " \"\"\"\n",
+ " Find all books in the list that match the given title and author.\n",
+ "\n",
+ " Args:\n",
+ " books (list): List of dictionaries representing books.\n",
+ " title (str): Title of the book to search for.\n",
+ " author (str): Author of the book to search for.\n",
+ "\n",
+ " Returns:\n",
+ " list: List of dictionaries representing the found books.\n",
+ " \"\"\"\n",
+ " return [book for book in books if book['title'] == title and book['author'] == author]\n",
+ "\n",
+ "# Define a function to print book information\n",
+ "def print_book_info(book):\n",
+ " \"\"\"\n",
+ " Print all information about a single book.\n",
+ "\n",
+ " Args:\n",
+ " book (dict): Dictionary representing the book.\n",
+ " \"\"\"\n",
+ " print(f\"Title: {book['title']}\")\n",
+ " print(f\"Author: {book['author']}\")\n",
+ " print(f\"Publication Year: {book['publication_year']}\")\n",
+ " print(f\"Genre: {book['genre']}\\n\")\n",
+ "\n",
+ "# Find and print information about the book titled 'Mastery' by Robert Greene\n",
+ "target_title = \"Mastery\"\n",
+ "target_author = \"Robert Greene\"\n",
+ "\n",
+ "found_books = find_book(books, target_title, target_author)\n",
+ "\n",
+ "if found_books:\n",
+ " for i, book in enumerate(found_books):\n",
+ " print(f\"Book {i+1}:\")\n",
+ " print_book_info(book)\n",
+ "else:\n",
+ " print(f\"No books found with title '{target_title}' by author '{target_author}'.\")\n",
+ "```\n",
+ "**Explanation**\n",
+ "\n",
+ "The solution consists of two functions:\n",
+ "\n",
+ "1. `find_book`: This function takes a list of dictionaries representing books, as well as the title and author to search for. It uses a list comprehension to find all books that match the given criteria and returns them.\n",
+ "2. `print_book_info`: This function takes a single dictionary representing a book and prints its information.\n",
+ "\n",
+ "In the example code, we define the `books` list with some sample data. We then call the `find_book` function to find all books with the title `'Mastery'` by Robert Greene. If found books are returned, we iterate over them and print their information using the `print_book_info` function.\n",
+ "\n",
+ "Note that if there are duplicate titles in the input data, only one book will be returned by the `find_book` function, as dictionaries cannot have duplicate keys."
+ ],
+ "text/plain": [
+ "\n",
+ "Performance Statistics:\n",
+ "\n"
+ ],
+ "text/plain": [
+ "\n",
+ "\u001b[1mPerformance Statistics:\u001b[0m\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "mean min max count\n", + "Model \n", + "gpt 14.672200 14.672200 14.672200 1\n", + "llama 79.891858 79.891858 79.891858 1\n", + "\n" + ], + "text/plain": [ + " mean min max count\n", + "Model \n", + "gpt \u001b[1;36m14.672200\u001b[0m \u001b[1;36m14.672200\u001b[0m \u001b[1;36m14.672200\u001b[0m \u001b[1;36m1\u001b[0m\n", + "llama \u001b[1;36m79.891858\u001b[0m \u001b[1;36m79.891858\u001b[0m \u001b[1;36m79.891858\u001b[0m \u001b[1;36m1\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAI/CAYAAABqNbq7AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAY3dJREFUeJzt3XtYFHX///HXqrhyVjywUKiIqJma5wNWagbmqbzN7rvsIFndlpoRFUre1WoGZqlklnYwxcos89DhLoPKKFPvUDO9yUxvj5lIeQJFEXV+f/hjvq6ggTIuK8/Hde11OZ/PZ2bes+sy+9qZnbEZhmEIAAAAAACUuyruLgAAAAAAgMsVoRsAAAAAAIsQugEAAAAAsAihGwAAAAAAixC6AQAAAACwCKEbAAAAAACLELoBAAAAALAIoRsAAAAAAIsQugEAAAAAsAihGxdlzpw5stls5qNatWoKCQnR7bffrs2bN7u7PI8TGxvr8nye6xEbG6tvvvlGNptN33zzjbvLNnXv3t2ssUqVKvL391fjxo1122236cMPP9SpU6eKzdOwYUPFxsaWaT0rVqyQ0+nUwYMHyzTf2esqeg4//PDDMi3nfPLz8+V0Okt8XYreL9u3by+39QFARTRt2jTZbDa1aNHC3aVUOGfuK202m2rUqKHmzZtrwoQJOn78uLvL8zil+dxU9HkpNjZWDRs2dHfJqISqubsAXB5mz56tZs2a6dixY/r+++/13HPPadmyZfrll19Uq1Ytd5fnMZ566ik9+OCD5vTatWs1YsQIJSUlqUePHmZ73bp1VbduXa1cuVLNmzd3R6nn1KhRI7377ruSpCNHjmjbtm1asmSJbrvtNl133XX65JNPFBgYaI5fvHixAgICyrSOFStWaNy4cYqNjVXNmjVLPd+FrKus8vPzNW7cOEmnP1idqW/fvlq5cqVCQkIsrQEA3O2tt96SJGVlZek///mPOnXq5OaKKpYz95V//PGH3nzzTT311FPauXOnXn/9dTdX51lWrlzpMv3ss89q2bJl+vrrr13amzdvrrCwMD3yyCOXsjxAEqEb5aRFixZq3769pNNB4+TJk3rmmWe0ZMkS3XvvvW6uznNEREQoIiLCnD527JgkKTIyUp07dy42vqQ2d/P29i5W1/3336/Zs2dr6NCh+uc//6n333/f7GvTpo3lNR09elTe3t6XZF3nU/RlCQBczlavXq2ffvpJffv21b///W/NmjXrkoduwzB07NgxeXt7X9L1ltbZ+8revXurefPmSk1N1bRp01SjRg03VudZzv7MUbduXVWpUqXEz0hWf/EOnAunl8MSRQF87969Lu2rV6/WzTffrKCgINWoUUNt2rTRBx984DImPz9fjz/+uMLDw1WjRg0FBQWpffv2eu+998wxsbGx8vPzU1ZWlnr27ClfX1/VrVtXI0eOVH5+vsvyjh07psTERIWHh6t69eq64oorNGLEiGKnJjds2FD9+vXT0qVL1bZtW3l7e6tZs2bmt/Vlqa+023oxSjq9vOh5+eWXX9SrVy/5+voqJCREEydOlCStWrVK1157rXx9fdWkSROlpqYWW252draGDRumK6+8UtWrV1d4eLjGjRunEydOXFS99957r/r06aMFCxZox44dZvvZp3yfOnVKEyZMUNOmTeXt7a2aNWuqVatWeumllyRJTqdTTzzxhCQpPDzc5bSxouX169dPixYtUps2bVSjRg3zyPO5TmU/duyY4uPj5XA45O3trW7duunHH390GdO9e/diR64luZyqtn37djNUjxs3zuXnANK5Ty9/6623dM0115j/n/72t79p48aNxdbj5+enLVu2qE+fPvLz81NYWJgee+wxFRQUnPN5B4BLbdasWZKkiRMnKioqSvPnzzf3zYWFhapXr57uvvvuYvMdPHhQ3t7eio+PN9tyc3PNfW7RPjwuLk5Hjhxxmddms2nkyJGaOXOmrrrqKtntdnMfN27cOHXq1ElBQUEKCAhQ27ZtNWvWLBmG4bKMgoICPfbYY3I4HPLx8dH111+vNWvWlLjvKO99ZbVq1dS6dWsdP37c5fOJYRh69dVX1bp1a3l7e6tWrVoaNGiQtm7d6jL/jz/+qH79+qlevXqy2+0KDQ1V37599dtvvxV7jl577TU1adJEdrtdzZs31/z584vV89///le33HKLatWqpRo1aqh169bFPjMUfQ557733NHbsWIWGhiogIEA33nijNm3aVOb6SrutF6Ok08uLnpfZs2ebnz3at2+vVatWyTAMvfDCCwoPD5efn59uuOEGbdmypdhyv/zyS/Xs2VMBAQHy8fFR165d9dVXX5Vb3fB8HOmGJbZt2yZJatKkidm2bNky3XTTTerUqZNmzpypwMBAzZ8/X//4xz+Un59v7tDi4+P19ttva8KECWrTpo2OHDmi//73v9q3b5/LOgoLC9WnTx8NGzZMY8aM0YoVKzRhwgTt2LFDn3zyiaTTf8AHDBigr776SomJibruuuu0fv16PfPMM1q5cqVWrlwpu91uLvOnn37SY489pjFjxig4OFhvvvmm7rvvPjVu3FjXX399qesr7bZaobCwUAMHDtSDDz6oJ554QvPmzVNiYqJyc3O1cOFCjR49WldeeaVefvllxcbGqkWLFmrXrp2k0x8iOnbsqCpVqujpp59WRESEVq5cqQkTJmj79u2aPXv2RdV2880367PPPtN3332nBg0alDhm0qRJcjqd+te//qXrr79ehYWF+uWXX8wPIffff7/279+vl19+WYsWLTJP1T7zNPu1a9dq48aN+te//qXw8HD5+vqet64nn3xSbdu21ZtvvqlDhw7J6XSqe/fu+vHHH9WoUaNSb19ISIiWLl2qm266Sffdd5/uv/9+STrv0e3k5GQ9+eSTuuOOO5ScnKx9+/bJ6XSqS5cuyszMVGRkpDm2sLBQN998s+677z499thj+vbbb/Xss88qMDBQTz/9dKnrBACrHD16VO+99546dOigFi1aaOjQobr//vu1YMECDRkyRF5eXrrrrrs0c+ZMvfLKKy5HHt977z0dO3bMPEMuPz9f3bp102+//aYnn3xSrVq1UlZWlp5++mlt2LBBX375pWw2mzn/kiVL9N133+npp5+Ww+FQvXr1JJ3+QnTYsGGqX7++pNNfQD/88MPavXu3y9/Oe++9V++//74SEhJ0ww036Oeff9bf/vY35ebmumyjVfvKbdu2qWbNmi77jGHDhmnOnDkaNWqUnn/+ee3fv1/jx49XVFSUfvrpJwUHB+vIkSOKjo5WeHi4XnnlFQUHBys7O1vLli1TXl6eyzo+/vhjLVu2TOPHj5evr69effVV3XHHHapWrZoGDRokSdq0aZOioqJUr149TZs2TbVr19Y777yj2NhY7d27VwkJCS7LfPLJJ9W1a1e9+eabys3N1ejRo9W/f39t3LhRVatWLXV9pdlWq3z66af68ccfNXHiRNlsNo0ePVp9+/bVkCFDtHXrVk2fPl2HDh1SfHy8br31Vq1bt878v/fOO+/onnvu0S233KLU1FR5eXnptddeU69evfTFF1+oZ8+eltUND2IAF2H27NmGJGPV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f/XVV9q7d68ZBE+ePKn3339fERERuvLKKyWd/uy0ePFi/f777y5fIMydO1c+Pj4XdbvSc9V3IdtaEXTt2lU1a9bUzz//rJEjR7q7HFRghG5YolatWkpMTFRCQoLmzZunu+66S6+99pp69+6tXr16KTY2VldccYX279+vjRs3au3atVqwYIEkqVOnTurXr59atWqlWrVqaePGjXr77bfVpUsXl8BdvXp1TZ48WYcPH1aHDh3Mq5f37t1b1157rSQpOjpavXr10ujRo5Wbm6uuXbuaVy9v06ZNibcs+Sulqa+021rRjB8/Xunp6YqKitKoUaPUtGlTHTt2TNu3b9dnn32mmTNnmjvlczl69KhWrVpl/nvr1q1asmSJPv30U3Xr1k0zZ8487/z9+/c37/tet25d7dixQykpKWrQoIF5Je+WLVtKkl566SXzarhNmzaVv7//BW13Tk6O/va3v+mBBx7QoUOH9Mwzz6hGjRpKTEw0x9x999167bXXdNddd+mBBx7Qvn37NGnSpGL3/PT391eDBg300UcfqWfPngoKClKdOnWK3aJEOv2lwlNPPaUnn3xS99xzj+644w7t27dP48aNU40aNfTMM89c0PYAwKX2+eef6/fff9fzzz9f4u0VW7RooenTp2vWrFnq16+fpNOnmL///vsaOXKkrrzySt14440u88TFxWnhwoW6/vrr9eijj6pVq1Y6deqUdu7cqbS0ND322GN/ef/vvn37asqUKRo8eLD++c9/at++fXrxxReLHTm9+uqrdccdd2jy5MmqWrWqbrjhBmVlZWny5MkKDAxUlSr/94vM8thXluSee+7RlClT9OKLL2rEiBHq2rWr/vnPf+ree+/V6tWrdf3118vX11d79uzR8uXL1bJlSz300EP69NNP9eqrr2rAgAFq1KiRDMPQokWLdPDgQUVHR7uso06dOrrhhhv01FNPmVcv/+WXX1xOa3/mmWfM360//fTTCgoK0rvvvqt///vfmjRpkgIDA8u0XaWpr7TbWtH4+fnp5Zdf1pAhQ7R//34NGjRI9erV0x9//KGffvpJf/zxh2bMmOHuMlERuOf6bbhcFF2N+ewrixrG6St/169f34iMjDROnDhhGIZh/PTTT8bf//53o169eoaXl5fhcDiMG264weVqnWPGjDHat29v1KpVy7Db7UajRo2MRx991Pjz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1pednS1JCg4OdmkPDg42+0pSUFCg3NxclwcAAJUVVy8HAMADPfXUU7r11lv1v//9TzfccIMk6auvvtJ7772nBQsWlGoZu3bt0iOPPKK0tDTVqFHjnONsNpvLtGEYxdrOlJycrHHjxpWqBgAALncc6QYAwAPdfPPNWrJkibZs2aLhw4frscce02+//aYvv/xSAwYMKNUy1qxZo5ycHLVr107VqlVTtWrVlJGRoWnTpqlatWrmEe6zj2rn5OQUO/p9psTERB06dMh87Nq164K3EwAAT8eRbgAAPFTfvn1LvJhaafXs2VMbNmxwabv33nvVrFkzjR49Wo0aNZLD4VB6erratGkjSTp+/LgyMjL0/PPPn3O5drtddrv9gusCAOByQugGAMBDHTx4UB9++KG2bt2qxx9/XEFBQVq7dq2Cg4N1xRVX/OX8/v7+atGihUubr6+vateubbbHxcUpKSlJkZGRioyMVFJSknx8fDR48GBLtgkAgMsNoRsAAA+0fv163XjjjQoMDNT27dt1//33KygoSIsXL9aOHTs0d+7ccllPQkKCjh49quHDh+vAgQPq1KmT0tLS5O/vXy7LBwDgckfoBgDAA8XHxys2NlaTJk1yCcC9e/e+qKPQ33zzjcu0zWaT0+mU0+m84GUCAFCZcSE1AAA8UGZmpoYNG1as/Yorrjjv7bwAAMClRegGAMAD1ahRo8T7X2/atEl169Z1Q0UAAKAkhG4AADzQLbfcovHjx6uwsFDS6dPAd+7cqTFjxujWW291c3UAAKAIoRsAAA/04osv6o8//lC9evV09OhRdevWTREREfLz89Nzzz3n7vIAAMD/x4XUAADwQAEBAVq+fLm+/vprrV27VqdOnVK7du3Us2dPd5cGAADOwJFuAAA8yH/+8x99/vnn5vQNN9ygunXr6tVXX9Udd9yhf/7znyooKHBjhQAA4EyEbgAAPIjT6dT69evN6Q0bNuiBBx5QdHS0xowZo08++UTJyclurBAAAJyJ0A0AgAdZt26dyynk8+fPV8eOHfXGG28oPj5e06ZN0wcffODGCgEAwJkI3QAAeJADBw4oODjYnM7IyNBNN91kTnfo0EG7du1yR2kAAKAEhG4AADxIcHCwtm3bJkk6fvy41q5dqy5dupj9eXl58vLycld5AADgLIRuAAA8yE033aQxY8bou+++U2Jionx8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", + "text/plain": [ + "
History saved to my_tutor_session.json\n",
+ "\n"
+ ],
+ "text/plain": [
+ "\u001b[1;32mHistory saved to my_tutor_session.json\u001b[0m\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "\n",
+ "# Save history to a file\n",
+ "tutor.save_history(\"my_tutor_session.json\")\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "4aa6afbf-1cc1-4ed1-a65f-14ee02ce278f",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n", + "│ New Question: │\n", + "│ Explain how to implement a binary search algorithm in Python. │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", + "\n" + ], + "text/plain": [ + "\u001b[32m╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\u001b[0m\n", + "\u001b[32m│\u001b[0m \u001b[1mNew Question:\u001b[0m \u001b[32m│\u001b[0m\n", + "\u001b[32m│\u001b[0m Explain how to implement a binary search algorithm in Python. \u001b[32m│\u001b[0m\n", + "\u001b[32m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+ "│ Getting response from gpt-4o-mini... │\n",
+ "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+ "\n"
+ ],
+ "text/plain": [
+ "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+ "│ \u001b[1;34mGetting response from gpt-4o-mini...\u001b[0m │\n",
+ "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "Binary search is an efficient algorithm for finding a target value within a sorted list. It works by repeatedly dividing the search interval in half. If the target value is less than the element in the middle of the interval, the search continues on the lower half; otherwise, it continues on the upper half. This process is continued until the target value is found or the search interval is empty.\n",
+ "\n",
+ "Here's a detailed explanation and implementation of the binary search algorithm in Python:\n",
+ "\n",
+ "### Step-by-Step Implementation\n",
+ "\n",
+ "1. **Prerequisites**:\n",
+ " - Ensure the input list is sorted. Binary search can only be performed on a sorted list.\n",
+ " \n",
+ "2. **Set Initial Variables**:\n",
+ " - Define two pointers, `low` and `high`, which represent the starting and ending indices of the search range in the list.\n",
+ "\n",
+ "3. **Calculate the Middle Index**:\n",
+ " - Use the formula `mid = (low + high) // 2` to find the middle index.\n",
+ "\n",
+ "4. **Comparison**:\n",
+ " - Compare the middle element with the target:\n",
+ " - If the middle element is equal to the target, return the index of the middle element.\n",
+ " - If the target is less than the middle element, narrow the search to the left half by setting `high = mid - 1`.\n",
+ " - If the target is greater than the middle element, narrow the search to the right half by setting `low = mid + 1`.\n",
+ "\n",
+ "5. **Loop Until the Target is Found or the Interval is Empty**:\n",
+ " - Repeat the above steps until the `low` pointer exceeds the `high` pointer. If the target is not found, return a value indicating that the target is not present (commonly -1).\n",
+ "\n",
+ "### Implementation in Python\n",
+ "\n",
+ "Here’s a complete Python implementation of the binary search algorithm:\n",
+ "\n",
+ "\n",
+ "def binary_search(arr, target):\n",
+ " low = 0\n",
+ " high = len(arr) - 1\n",
+ "\n",
+ " while low <= high:\n",
+ " # Find the middle index\n",
+ " mid = (low + high) // 2\n",
+ " \n",
+ " # Check if the target is present at mid\n",
+ " if arr[mid] == target:\n",
+ " return mid # Target found, return the index\n",
+ " \n",
+ " # If the target is smaller than the mid element,\n",
+ " # it can only be present in the left subarray\n",
+ " elif arr[mid] > target:\n",
+ " high = mid - 1\n",
+ " \n",
+ " # If the target is larger than the mid element,\n",
+ " # it can only be present in the right subarray\n",
+ " else:\n",
+ " low = mid + 1\n",
+ "\n",
+ " # Target was not found\n",
+ " return -1\n",
+ "\n",
+ "\n",
+ "### Example Usage\n",
+ "\n",
+ "\n",
+ "# Example sorted list\n",
+ "arr = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]\n",
+ "target = 7\n",
+ "\n",
+ "# Perform binary search\n",
+ "result = binary_search(arr, target)\n",
+ "\n",
+ "if result != -1:\n",
+ " print(f'Target {target} found at index {result}.')\n",
+ "else:\n",
+ " print(f'Target {target} not found in the list.')\n",
+ "\n",
+ "\n",
+ "### Key Points\n",
+ "\n",
+ "1. **Time Complexity**: The time complexity of binary search is O(log n), where n is the number of elements in the array. This is significantly more efficient than a linear search, which has a time complexity of O(n).\n",
+ " \n",
+ "2. **Space Complexity**: The space complexity of the binary search algorithm is O(1) for the iterative version, as it requires a fixed amount of space for variables.\n",
+ "\n",
+ "3. **Iterative vs Recursive**: The above implementation is iterative, which is generally preferred for binary search due to its efficiency and avoidance of recursion limits. However, a recursive implementation can also be done:\n",
+ "\n",
+ "### Recursive Implementation\n",
+ "\n",
+ "\n",
+ "def binary_search_recursive(arr, target, low, high):\n",
+ " if low <= high:\n",
+ " mid = (low + high) // 2\n",
+ " \n",
+ " if arr[mid] == target:\n",
+ " return mid\n",
+ " elif arr[mid] > target:\n",
+ " return binary_search_recursive(arr, target, low, mid - 1)\n",
+ " else:\n",
+ " return binary_search_recursive(arr, target, mid + 1, high)\n",
+ " \n",
+ " return -1\n",
+ "\n",
+ "\n",
+ "### Conclusion\n",
+ "\n",
+ "Binary search is a fundamental searching technique that exploits the properties of sorted arrays. Its efficiency makes it a preferred method for searching when working with large datasets. Understanding its underlying algorithm and being able to implement it in Python is a valuable skill in software engineering and data science."
+ ],
+ "text/plain": [
+ "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+ "│ Getting response from llama3.2... │\n",
+ "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+ "\n"
+ ],
+ "text/plain": [
+ "╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\n",
+ "│ \u001b[1;32mGetting response from llama3.2...\u001b[0m │\n",
+ "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "## llama3.2 Response\n",
+ "**Implementing Binary Search Algorithm in Python**\n",
+ "=====================================================\n",
+ "\n",
+ "Binary search is an efficient algorithm for finding an item from a sorted list of items. It works by repeatedly dividing in half the portion of the list that could contain the item, until you've narrowed down the possible locations to just one.\n",
+ "\n",
+ "Here's a step-by-step guide on how to implement binary search in Python:\n",
+ "\n",
+ "**Step 1: Define the Search Function**\n",
+ "-------------------------------------\n",
+ "\n",
+ "```python\n",
+ "def binary_search(arr, target):\n",
+ " \"\"\"\n",
+ " Searches for an element in a sorted array using binary search algorithm.\n",
+ " \n",
+ " Parameters:\n",
+ " arr (list): The sorted list of elements.\n",
+ " target: The element to be searched.\n",
+ " \n",
+ " Returns:\n",
+ " int: The index of the target element if found; otherwise, -1.\n",
+ " \"\"\"\n",
+ "```\n",
+ "\n",
+ "**Step 2: Initialize Variables**\n",
+ "---------------------------------\n",
+ "\n",
+ "```python\n",
+ " low = 0 # Index of the first element in the list\n",
+ " high = len(arr) - 1 # Index of the last element in the list\n",
+ "```\n",
+ "\n",
+ "**Step 3: Loop Until Found or Not Found**\n",
+ "-----------------------------------------\n",
+ "\n",
+ "```python\n",
+ " while low <= high:\n",
+ " mid = (low + high) // 2 # Calculate the middle index\n",
+ " \n",
+ " if arr[mid] == target:\n",
+ " return mid # Target found, return its index\n",
+ " \n",
+ " elif arr[mid] < target:\n",
+ " low = mid + 1 # Search in the right half\n",
+ " \n",
+ " else:\n",
+ " high = mid - 1 # Search in the left half\n",
+ "```\n",
+ "\n",
+ "**Step 4: Handle Edge Cases**\n",
+ "---------------------------\n",
+ "\n",
+ "```python\n",
+ " if low > high:\n",
+ " return -1 # Target not found, return -1\n",
+ "```\n",
+ "\n",
+ "**Putting it all Together**\n",
+ "-----------------------------\n",
+ "\n",
+ "Here's the complete binary search implementation in Python:\n",
+ "\n",
+ "```python\n",
+ "def binary_search(arr, target):\n",
+ " \"\"\"\n",
+ " Searches for an element in a sorted array using binary search algorithm.\n",
+ " \n",
+ " Parameters:\n",
+ " arr (list): The sorted list of elements.\n",
+ " target: The element to be searched.\n",
+ " \n",
+ " Returns:\n",
+ " int: The index of the target element if found; otherwise, -1.\n",
+ " \"\"\"\n",
+ " low = 0\n",
+ " high = len(arr) - 1\n",
+ "\n",
+ " while low <= high:\n",
+ " mid = (low + high) // 2\n",
+ " \n",
+ " if arr[mid] == target:\n",
+ " return mid\n",
+ " elif arr[mid] < target:\n",
+ " low = mid + 1\n",
+ " else:\n",
+ " high = mid - 1\n",
+ " \n",
+ " return -1\n",
+ "\n",
+ "# Example usage\n",
+ "arr = [2, 4, 6, 8, 10]\n",
+ "target = 6\n",
+ "index = binary_search(arr, target)\n",
+ "if index != -1:\n",
+ " print(f\"Target {target} found at index {index}\")\n",
+ "else:\n",
+ " print(\"Target not found\")\n",
+ "```\n",
+ "\n",
+ "**Time Complexity**\n",
+ "------------------\n",
+ "\n",
+ "The time complexity of binary search is O(log n), where n is the length of the input array. This makes it much faster than linear search (O(n)) for large datasets.\n",
+ "\n",
+ "I hope this explanation helps! Let me know if you have any further questions or need additional clarification."
+ ],
+ "text/plain": [
+ "