{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Bubble Sort" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Bubble Sort ist ein sehr berühmter Sortieralgorithmus. Das Prinzip ist recht einfach: Hohe Werte steigen schneller auf als weniger hohe Werte, analog der Luftblasen («bubbles») in einem Glas Mineralwasser, wo grössere Luftblasen ebenfalls schneller aufsteigen.\n", "\n", "In diesem Notebook sind die Aufgaben etwas offener formuliert, nachdem die wichtigsten Konzepte noch einmal geübt werden. Die Aufgaben sind anstelle von Schritten mit einer Liste von Hinweisen versehen.\n", "\n", "---\n", "**Ziele**\n", "\n", "Sie können\n", "* den Bubble-Sort-Algorithmus in eigenen Worten beschreiben.\n", "* den Bubble-Sort-Algorithmus implementieren (ohne oder mit Optimierungen).\n", "* Ihre Implementation und die allfällige Optimierung erklären.\n", "* verstehen, wie der Bubble-Sort-Algorithmus optimiert werden kann.\n", "---\n", "\n", "## Algorithmus\n", "\n", "Schauen Sie sich die folgende Animation von [visualgo.net](https://visualgo.net/en/sorting?slide=7) an. \n", "\n", "*Um den Videoclip sehen zu können, müssen Sie allenfalls die folgende Zelle ausführen.*" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Führen Sie diese Zelle aus, um den Videoclip sehen zu können.\n", "\n", "import IPython\n", "\n", "IPython.display.IFrame(src=\"https://www.youtube.com/embed/mcFOilpoGDA?rel=0&controls=0&showinfo=0\", width=560, height=315)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Aufgabe 1 – Bubble Sort beschreiben**\n", "\n", "Schreiben Sie den Bubble-Sort-Algorithmus in eigenen Worten auf. Eine gute Beschreibung in Worten, die auch jüngere Kolleginnen und Kollegen verstehen, die sich damit noch nicht auseinandergesetzt haben, hilft Ihnen später bei der Umsetzung." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Machen Sie aus dieser Zelle eine Markdownzelle und \n", "# beschreiben Sie den Bubble-Sort-Algorithmus in eigenen Worten." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vorbereitungen\n", "\n", "Um den Bubble-Sort-Algorithmus zu implementieren, werden Sie oft Listenelemente tauschen müssen und sie werden die Liste mehrmals durchgehen müssen. Dies werden Sie hier noch einmal wiederholen.\n", "\n", "### Platztausch in der Liste\n", "\n", "Der Platztausch wurde bereits bei Selection Sort thematisiert. \n", "\n", "
\n", " \n", "Falls Sie die Theorie dazu benötigen, können Sie diese hier noch einmal aufklappen.\n", " \n", "\n", "**Variablentausch** \n", "Stellen Sie sich vor, Sie haben zwei Variablen `a` und `b`:\n", " \n", "```Python\n", "a = 3\n", "b = 5\n", "```\n", "\n", "Wenn Sie nun der Variable `a` den Wert der Variable `b` zuweisen, haben beide Variablen denselben Wert und Sie haben den ursprünglichen Wert der Variable `a` verloren.\n", "\n", "Sie müssen also eine Lösung finden, wie Sie den Wert der Variable `a` nicht verlieren, wenn Sie den Wert der Variable `b` in die Variable `a` schreiben.\n", "\n", "Sie haben jederzeit die Möglichkeit, neue Variablen zu erstellen und können den Wert der Variable `a` problemlos in einer neuen Variable zwischenspeichern. Diese neue Variable nennt man **Hilfsvariable**. Da sie nur vorübergehend, also *temporär* genutzt wird, nennt man sie in der Regel `temp`. Sie können Sie aber auch `c` nennen oder `luisa` oder `adrian`.\n", "\n", "Der Variablentausch wird folgendermassen implementiert:\n", "\n", "```Python\n", "a = 3\n", "b = 5\n", "\n", "temp = a # Hilfsvariable\n", "# nun ist der Wert der Variable a nicht verloren (sondern in temp gespeichert) und Sie können a einen neuen Wert zuweisen:\n", "a = b\n", "b = a\n", "```\n", "\n", "**Platztausch in der Liste** \n", "Wenn Sie in einer Liste zwei Plätze tauschen wollen, gehen sie genauso vor wie wenn Sie zwei Variablen tauschen möchten – mit dem einzigen Unterschied, dass Sie nun keine Variablen haben, sondern Listenelemente, auf die Sie, über die Indizes zugreifen.\n", "
\n", "\n", "\n", "**Aufgabe 2 – Tausch zweier Listenelemente**\n", "\n", "Implementieren Sie einen \"Platztausch\", oft auch \"Swap\" genannt. \n", "\n", "Erstellen Sie dazu eine Funktion `swap(liste, index1, index2)`, die eine Liste und zwei Indizes entgegennimmt.\n", "Testen Sie Ihre Lösung mit einer kurzen Liste, bei der Sie das erste und letzte Element tauschen.\n", "\n", "Falls Sie Mühe haben, versuchen Sie zuerst zwei Variablen `a` und `b` zu tauschen und ersetzen Sie anschliessend Ihre Variablen durch Listenelemente." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Eine Liste für jedes Element durchlaufen\n", "\n", "Wie schon bei den vorangehenden Algorithmen muss jedes Element einzeln einsortiert werden. Dazu werden verschachtelte Schleifen verwendet.\n", "\n", "Das folgende Programm erstellt eine Liste und gibt die Elemente nebeneinander aus. Dazu ist eine Schleife nötig. \n", "```Python\n", "liste = [x for x in range(10)]\n", "\n", "for i in range(len(liste)):\n", " print(liste[i], end=\" \")\n", "```\n", "Ausgabe:\n", "\n", "`0 1 2 3 4 5 6 7 8 9`\n", "\n", "**Aufgabe 3 – Schleifen verschachteln**\n", "\n", "Anhand dieser Aufgabe machen Sie sich Gedanken dazu, wie Sie eine Schleife \n", "\n", "a) Ergänzen Sie das Programm, damit die folgende Ausgabe entsteht:\n", "\n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `0 1 2 3 4 5 6 7 8 9` " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ergänzen Sie diesen Code, um die gewünschte Ausgabe zu erhalten...\n", "\n", "liste = [x for x in range(10)]\n", "\n", "for i in range(len(liste)):\n", " print(liste[i], end=\" \")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "b) Diese Teilaufgabe bereitet Sie auf die Optimierung von Bubble Sort vor.\n", "\n", "Ändern Sie nun Ihr Programm so ab, dass Sie die folgende Ausgabe bekommen:\n", "\n", "> `0 1 2 3 4 5 6 7 8 9` \n", "> `1 2 3 4 5 6 7 8 9` \n", "> `2 3 4 5 6 7 8 9` \n", "> `3 4 5 6 7 8 9` \n", "> `4 5 6 7 8 9` \n", "> `5 6 7 8 9` \n", "> `6 7 8 9` \n", "> `7 8 9` \n", "> `8 9` \n", "> `9`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Implementation von Bubble Sort\n", "\n", "Nun sind Sie gerüstet und können den Bubble-Sort-Algorithmus implementieren, optimieren und Ihre Versionen vergleichen.\n", "\n", "**Wozu Optimierungen?**\n", "\n", "Eine Liste aus einer überschaubaren Anzahl von Elementen würde sich problemlos von Hand sortieren lassen und wenn nur wenige Elemente sortiert werden sollen, macht es noch keinen Unterschied, ob Vergleiche mehrfach ausgeführt oder eingespart werden, da Ihr Computer so schnell ist, dass Sie kaum einen Unterschied erkennen werden. In der Regel werden Algorithmen aber nicht auf überschaubare Datenmengen angewandt, sondern auf Unmengen von Daten. Bei grösseren Listen kann bereits ein Unterschied erkennbar sein und gerade das Sortieren ist eine Aufgabe, die immer wieder zur Anwendung kommt. Da ist es unabdingbar, effiziente Algorithmen zur Hand zu haben. \n", "\n", "Die Investition in einen effizienten Algorithmus lohnt sich also durchaus und ist überdies auch interessanter als das blosse Programmierhandwerk...\n", "\n", "Sie werden nun am Beispiel des Bubble-Sort-Algorithmus sehen, wie sich auch ein nicht gerade für seine Effizienz bekannter Algorithmus noch optimieren lässt.\n", "\n", "### Brute Force\n", "\n", "Beim Entwickeln lohnt es sich jeweils, sich zu Beginn eine ganz einfache Lösung zu überlegen und diese zu implementieren. Dabei spricht man von \"Brute Force\", \"roher Gewalt\". Das ist nicht so böse, wie es scheinen mag, aber vielleicht ist es eine Anspielung darauf, dass diese ersten Lösungen etwas unschön und unnötig kostspielig sind: Ohne viel zu überlegen wird einfach mal drauf los gearbeitet. Eine Brute Force Lösung zeigt auf, dass das Problem in seinen Grundzügen verstanden ist und sich lösen lässt. Sie bildet eine Basis, auf der aufgebaut werden kann.\n", "\n", "Im Falle von Bubble Sort besteht die Brute Force Lösung darin, für jedes Listenelement die ganze Liste durchzugehen.\n", "\n", "\n", "\n", "**Aufgabe 4 – Erste Implementation (Brute Force, ohne Optimierungen)**\n", "\n", "Es ist für den Vergleich der verschiedenen Optimierungsschritte wichtig, ganz einfach anzufangen. \n", "\n", "a) Implementieren Sie den Bubble-Sort-Algorithmus.\n", "\n", "Hinweise\n", "\n", "* Fangen Sie mit einer kurzen Liste an, z. B. `unsortierte_liste = [8, 3, 5, 2]`.\n", "* Implementieren Sie den ersten Durchgang und überprüfen Sie Ihre Ausgabe.\n", "* Wenn der erste Durchgang klappt, erweitern Sie Ihr Programm, um die ganze Liste zu sortieren.\n", "* Stellen Sie sicher, dass sich Ihr Programm auch auf grössere Listen anwenden lässt.\n", "\n", "b) Zählen Sie in Ihrem Programm die Anzahl Vergleiche und geben Sie diese aus." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Erste Optimierung\n", "\n", "Nun werden Sie die erste (und wichtigste) Optimierung in Angriff nehmen.\n", "\n", "**Aufgabe 5 – Erste Optimierung**\n", "\n", "a) Überlegen Sie sich, ob es Vergleiche gibt, die Sie einsparen können.\n", "\n", "
\n", " \n", "Hinweis für den Fall, dass Sie nicht weiterkommen\n", " \n", "\n", "Der Name «Bubble Sort» kommt daher, dass grössere Luftblasen im Wasser schneller aufsteigen als kleine.\n", "\n", "Eine Eigenschaft des Bubble-Sort-Algorithmus ist, dass bei jedem Durchgang das grösste Element am Ende des noch unsortierten Teils zu liegen kommt und damit seinen Platz in der geordneten Liste einnimmt. Nach dem ersten Durchgang ist das erste Element somit einsortiert. Es muss beim zweiten Durchgang nicht mehr angeschaut werden. Nach dem zweiten Durchgang das Element am zweitletzten Listenplatz einsortiert. \n", "\n", "
\n", "\n", "b) Optimieren Sie Ihre Umsetzung in einer neuen Funktion `bubblesort1()`. Zählen Sie wiederum die Anzahl Vergleiche, die ausgeführt werden." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Zweite Optimierung\n", "\n", "Gibt es noch weitere Vergleiche, die allenfalls eingespart werden können?\n", "\n", "Es ist tatsächlich noch eine weitere Optimierung möglich, denn aufgrund des häufigen Vertauschens zweier benachbarter Elemente kann es durchaus vorkommen, dass die Liste bereits vor dem letzten Durchgang sortiert ist. Vor allem wenn eine Liste bereits sortierte oder fast sortierte Bereiche aufweist.\n", "\n", "**Aufgabe 6 – zweite Optimierung** \n", "\n", "Überlegen Sie sich, wie Sie dies überprüfen könnten und setzten Sie diese Optimierung in einer neuen Funktion namens `bubblesort2()` um, welche die Anzahl Vergleiche zurückgibt, die ausgeführt werden." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vergleiche\n", "\n", "Wenn Algorithmen bezüglich Effizienz untersucht werden, wird verglichen, wie sie mit verschiedenen Eingabekonstellationen zurechtkommen. Dazu dienen in der Regel extreme und \"normale\" Ausgangslagen.\n", "\n", "Als Extreme werden für Sortieralgorithmen sortierte und umgekehrt sortierte Listen (beste und schlechteste Ausgangslage) verwendet, sowie eine zufällige Liste als normale Ausgangslage.\n", "\n", "**Aufgabe 7 – Vergleichen**\n", "\n", "Erstellen Sie drei Funktionen `sortierte_liste`, `umgekehrt_sortierte_liste`, `zufaellige_liste`, die jeweils die Anzahl Elemente entgegennehmen und die entsprechende Liste aus aufeinanderfolgenden Ganzzahlen ab Null enthalten.\n", "\n", "Vergleichen Sie anschliessend die erste Implementation und die Optimierungen.\n", "\n", "Erstellen Sie dazu grössere Listen (mit 10, 100, 1000 Elementen) in verschiedenen Anfangskonstellationen. \n", "\n", "Das Ziel ist eine Tabelle:\n", "\n", "Liste | sortiert | zufällig | umgekehrt sortiert\n", ":-------- | :-------- | :-------- | :--------\n", "Ohne Optimierung | ________________________ | ________________________ | ________________________\n", "unsortierter Teil wird kleiner | ________________________ | ________________________ | ________________________ \n", "Abbruch, wenn sortiert | ________________________ | ________________________ | ________________________ " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Hat es sich gelohnt?\n", "\n", "Schauen Sie sich den folgenden Clip an. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Führen Sie diese Zelle aus, um den Videoclip sehen zu können.\n", "\n", "import IPython\n", "\n", "IPython.display.IFrame(src=\"https://www.youtube.com/embed/koMpGeZpu4Q\", width=560, height=315)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wie Sie sehen, haben Sie mit diesen Optimierungen die Hälfte der Vergleiche herausgeholt und sparen in guten Fällen sogar noch etwas mehr ein. Bei beinahe sortierten Listen gewinnen Sie am meisten. In diesem Fall kann es Ihr Bubble Sort durchaus mit anderen Sortieralgorithmen aufnehmen, aber in den meisten Fällen ist er nicht der Algorithmus der Wahl, denn es gibt es noch einige effizientere Sortieralgorithmen. Positiv am Bubble-Sort-Algorithmus ist, dass praktisch kein zusätzlicher Speicherbedarf anfällt. Es gibt andere Sortieralgorithmen, die zwar sehr schnell sind, aber viel Speicher brauchen.\n", "\n", "Bei Interesse können Sie [hier](https://www.toptal.com/developers/sorting-algorithms) einen Vergleich finden." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Grafische Darstellung\n", "\n", "Es ist möglich, Ihre Liste in einem Säulendiagramm grafisch darzustellen. Um hier abzukürzen ist die entsprechende Funktion `show_diagram()` gegeben. Sie nimmt folgende Parameter entgegen:\n", "\n", "* `list`: Liste, die dargestellt werden soll\n", "* `title`: Diagrammtitel (optional; Defaultwert: \"Säulendiagramm\")\n", "* `sleep`: Wartezeit in Sekunden (optional, Defaultwert: 0.2)\n", "\n", "Für den Fall, dass Sie sie nachvollziehen möchten, ist sie mit Kommentaren versehen. Dies ist aber optional.\n", "\n", "Führen Sie die folgenden beiden Zellen aus, um die Funktion verwenden zu können.\n", "Die erste Zelle kümmert sich um die Imports. Falls die Bibliotheken nicht installiert sind, steht im Kommentar, wie Sie sie installieren können." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt # PyPlot: Bibliothek für Visualisierungen (Plots)\n", "import numpy as np # NumPy: Bibliothek für numerische Berechnungen\n", "from IPython import display # <--- Jupyter-Notebook-spezifische Bibliothek (ausserhalb von Jupyter: löschen)\n", "\n", "# In der Anacondadistribution ist matplotlib bereits vorinstalliert. \n", "# Falls Ihnen matplotlib oder numpy fehlen, können Sie diese wie folgt installieren, \n", "# kommentieren Sie die folgende Zeile ein (numpy wird mit matplotlib mitinstalliert):\n", "# pip install matplotlib\n", "\n", "def show_diagram(list, title = \"Säulendiagramm\", sleep = 0.2):\n", "\n", " # Den Output der aktuellen Zelle (des Jupyter Notebooks) löschen\n", " # parameter wait=True: warten, bis der neue Output bereitsteht\n", " display.clear_output(wait=True)\n", " \n", " # Balkenindex (x-Koordinaten der Balken)\n", " anzahl_elemente=len(list)\n", " bar_index = np.arange(anzahl_elemente)\n", " # Balkenbreite (1 bedeutet bis zum nächsten Balken)\n", " bar_width = 0.5\n", "\n", " # Titel\n", " plt.title(title)\n", "\n", " # Keine besondere Beschriftung der x- und y-Achsen:\n", " # plt.xticks und plt.yticks nicht definieren\n", "\n", " # Balkendiagramm\n", " plt.bar(bar_index, height=list, width=bar_width, color='blue')\n", "\n", " # Plot anzeigen \n", " plt.show()\n", " \n", " # kurz warten, weil sonst nichts sichtbar\n", " plt.pause(sleep)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Aufgabe 8 – Visualisierung**\n", "\n", "Nun möchten Sie ihre Liste jedes Mal visualisieren, wenn zwei Elemente getauscht wurden.\n", "\n", "Kopieren Sie dazu Ihre Implementation des Bubble-Sort-Algorithmus in die untenstehende Zelle und rufen Sie die Funktion `show_diagram()` so auf, dass jeweils die neue Liste dargestellt wird, sobald zwei Elemente getauscht wurden.\n", "\n", "Erstellen Sie noch einmal die drei Listen `aufsteigend`, `absteigend` und `zufaellig` und wenden Sie Ihre `bubblesort()`-Funktion auf diese drei Listen an." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code...\n", "# Funktion bubblesort() mit Aufruf der Funktion show_diagram()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code...\n", "# aufsteigende Liste erstellen" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code...\n", "# absteigende Liste erstellen" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Ihr Code...\n", "# zufällige Liste erstellen" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Quiz\n", "\n", "Alles klar?\n", "\n", "Sie sollten Bubble Sort nun in eigenen Worten beschreiben und Ihre Implementation erklären können.\n", "\n", "Testen Sie Ihr Verständnis anhand der Fragen, die erstellt werden, wenn Sie die folgenden Zellen gemäss Ihrer Umgebung ausführen.\n", "\n", "### NUR FALLS SIE AUF GOOGLE COLAB ARBEITEN\n", "\n", "Führen Sie bitte die **beiden** folgenden Zellen aus, um die Bibliothek für das Quiz zu laden." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NUR falls Sie AUF GOOGLE COLAB arbeiten:\n", "# Führen Sie diese Zelle aus, um das Quiz zu sehen.\n", "\n", "%%writefile quizclone.py\n", "import os\n", "from subprocess import getoutput\n", "getoutput(\"git clone -l -s https://github.com/donze-informatikunterricht/quiz.git cloned-repo\")\n", "os.chdir('cloned-repo')\n", "print('repo cloned successfully')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# NUR falls Sie AUF GOOGLE COLAB arbeiten:\n", "# Führen Sie diese Zelle aus, um das Quiz zu sehen.\n", "\n", "import quizclone" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Quiz erstellen (unabhängig von Ihrer Arbeitsumgebung)\n", "\n", "Nun können Sie das Quiz erstellen. Führen Sie dazu bitte die folgende Zelle aus.\n", "\n", "Fragen 3 und 4 des Quiz basieren auf der folgenden Liste:\n", "\n", "```python\n", "a = [2, 3, 7, 1, 6, 8, 4, 0, 5, 9]\n", "```\n", "\n", "Bitte führen Sie die folgende Zelle aus, um das Quiz zu erstellen." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.display import display\n", "import quiz\n", "\n", "display(quiz.Q1_bubble)\n", "display(quiz.Q2_bubble)\n", "display(quiz.Q3_bubble)\n", "display(quiz.Q4_bubble)\n", "display(quiz.Q5_bubble)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Abschluss: Sortiertanz\n", "\n", "Nun haben Sie sich mit dem Bubblsort-Algorithmus auseinandergesetzt. \n", "\n", "Sehr empfehlenswert für Such- und Sortieralgorithmen ist der [YouTube-Kanal AlgoRythmics](https://www.youtube.com/channel/UCIqiLefbVHsOAXDAxQJH7Xw), der Algorithmen in Form von Tänzen präsentiert.\n", "\n", "Führen Sie die folgende Zelle aus, um den Tanz zu Bubble Sort sehen zu können." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Führen Sie diese Zelle aus, um den Videoclip sehen zu können.\n", "\n", "import IPython\n", "\n", "IPython.display.IFrame(src=\"https://www.youtube.com/embed/lyZQPjUT5B4\", width=560, height=315)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.8" } }, "nbformat": 4, "nbformat_minor": 4 }