diff --git a/04_iteration/01_exercises_solved.ipynb b/04_iteration/01_exercises_solved.ipynb new file mode 100644 index 0000000..f65abe3 --- /dev/null +++ b/04_iteration/01_exercises_solved.ipynb @@ -0,0 +1,831 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 4: Recursion & Looping (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) of Chapter 4.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Towers of Hanoi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A popular example of a problem that is solved by recursion art the **[Towers of Hanoi ](https://en.wikipedia.org/wiki/Tower_of_Hanoi)**.\n", + "\n", + "In its basic version, a tower consisting of, for example, four disks with increasing radii, is placed on the left-most of **three** adjacent spots. In the following, we refer to the number of disks as $n$, so here $n = 4$.\n", + "\n", + "The task is to move the entire tower to the right-most spot whereby **two rules** must be obeyed:\n", + "\n", + "1. Disks can only be moved individually, and\n", + "2. a disk with a larger radius must *never* be placed on a disk with a smaller one.\n", + "\n", + "Although the **[Towers of Hanoi ](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** are a **classic** example, introduced by the mathematician [Édouard Lucas ](https://en.wikipedia.org/wiki/%C3%89douard_Lucas) already in 1883, it is still **actively** researched as this scholarly [article](https://www.worldscientific.com/doi/abs/10.1142/S1793830919300017?journalCode=dmaa&) published in January 2019 shows.\n", + "\n", + "Despite being so easy to formulate, the game is quite hard to solve.\n", + "\n", + "Below is an interactive illustration of the solution with the minimal number of moves for $n = 4$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Watch the following video by [MIT](https://www.mit.edu/)'s professor [Richard Larson](https://idss.mit.edu/staff/richard-larson/) for a comprehensive introduction.\n", + "\n", + "The [MIT Blossoms Initiative](https://blossoms.mit.edu/) is primarily aimed at high school students and does not have any prerequisites.\n", + "\n", + "The video consists of three segments, the last of which is *not* necessary to have watched to solve the tasks below. So, watch the video until 37:55." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"UuIneNBbscc\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Video Review Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Explain for the $n = 3$ case why it can be solved as a **recursion**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: How does the number of minimal moves needed to solve a problem with three spots and $n$ disks grow as a function of $n$? How does this relate to the answer to **Q1**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: The **[Towers of Hanoi ](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem is of **exponential growth**. What does that mean? What does that imply for large $n$?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: The video introduces the recursive relationship $Sol(4, 1, 3) = Sol(3, 1, 2) ~ \\bigoplus ~ Sol(1, 1, 3) ~ \\bigoplus ~ Sol(3, 2, 3)$. The $\\bigoplus$ is to be interpreted as some sort of \"plus\" operation. How does this \"plus\" operation work? How does this way of expressing the problem relate to the answer to **Q1**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Naive Translation to Python" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As most likely the first couple of tries will result in *semantic* errors, it is advisable to have some sort of **visualization tool** for the program's output: For example, an online version of the game can be found [here](https://www.mathsisfun.com/games/towerofhanoi.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's first **generalize** the mathematical relationship from above and then introduce the variable names used in our `sol()` implementation below.\n", + "\n", + "Unsurprisingly, the recursive relationship in the video may be generalized into:\n", + "\n", + "$Sol(n, o, d) = Sol(n-1, o, i) ~ \\bigoplus ~ Sol(1, o, d) ~ \\bigoplus ~ Sol(n-1, i, d)$\n", + "\n", + "$Sol(\\cdot)$ takes three \"arguments\" $n$, $o$, and $d$ and is defined with *three* references to itself that take modified versions of $n$, $o$, and $d$ in different orders. The middle reference, Sol(1, o, d), constitutes the \"end\" of the recursive definition: It is the problem of solving Towers of Hanoi for a \"tower\" of only one disk.\n", + "\n", + "While the first \"argument\" of $Sol(\\cdot)$ is a number that we refer to as `n_disks` below, the second and third \"arguments\" are merely **labels** for the spots, and we refer to the **roles** they take in a given problem as `origin` and `destination` below. Instead of labeling individual spots with the numbers `1`, `2`, and `3` as in the video, we may also call them `\"left\"`, `\"center\"`, and `\"right\"`. Both ways are equally correct! So, only the first \"argument\" of $Sol(\\cdot)$ is really a number!\n", + "\n", + "As an example, the notation $Sol(4, 1, 3)$ from above can then be \"translated\" into Python as either the function call `sol(4, 1, 3)` or `sol(4, \"left\", \"right\")`. This describes the problem of moving a tower consisting of `n_disks=4` disks from either the `origin=1` spot to the `destination=3` spot or from the `origin=\"left\"` spot to the `destination=\"right\"` spot.\n", + "\n", + "To adhere to the rules, an `intermediate` spot $i$ is needed. In `sol()` below, this is a temporary variable within a function call and *not* a parameter of the function itself." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In summary, to move a tower consisting of `n_disks` (= $n$) disks from an `origin` (= $o$) to a `destination` (= $d$), three steps must be executed:\n", + "\n", + "1. Move the tower's topmost `n_disks - 1` (= $n - 1$) disks from the `origin` (= $o$) to an `intermediate` (= $i$) spot (= **Sub-Problem 1**),\n", + "2. move the remaining and largest disk from the `origin` (= $o$) to the `destination` (= $d$), and\n", + "3. move the `n_disks - 1` (= $n - 1$) disks from the `intermediate` (= $i$) spot to the `destination` (= $d$) spot (= **Sub-Problem 2**).\n", + "\n", + "The two sub-problems themselves are solved via the same *recursive* logic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Write your answers to **Q5** to **Q7** into the skeleton of `sol()` below.\n", + "\n", + "`sol()` takes three arguments `n_disks`, `origin`, and `destination` that mirror $n$, $o$, and $d$ above.\n", + "\n", + "For now, assume that all arguments to `sol()` are `int` objects! We generalize this into real labels further below in the `hanoi()` function.\n", + "\n", + "Once completed, `sol()` should *print* out all the moves in the correct order. For example, *print* `\"1 -> 3\"` to mean \"Move the top-most `n_disks - 1` disks from spot `1` to spot `3`.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def sol(n_disks, origin, destination):\n", + " \"\"\"A naive implementation of Towers of Hanoi.\n", + "\n", + " This function prints out the moves to solve a Towers of Hanoi problem.\n", + "\n", + " Args:\n", + " n_disks (int): number of disks in the tower\n", + " origin (int): spot of the tower at the start; 1, 2, or 3\n", + " destination (int): spot of the tower at the end; 1, 2, or 3\n", + " \"\"\"\n", + " # answer to Q5\n", + " if n_disks <= 0:\n", + " return\n", + "\n", + " # answer to Q6\n", + " if origin == 1 and destination == 2:\n", + " intermediate = 3\n", + " elif origin == 1 and destination == 3:\n", + " intermediate = 2\n", + " elif origin == 2 and destination == 1:\n", + " intermediate = 3\n", + " elif origin == 2 and destination == 3:\n", + " intermediate = 1\n", + " elif origin == 3 and destination == 1:\n", + " intermediate = 2\n", + " else: # origin == 3 and destination == 2\n", + " intermediate = 1\n", + "\n", + " # answer to Q7\n", + " sol(n_disks - 1, origin, intermediate)\n", + " print(origin, \"->\", destination)\n", + " sol(n_disks - 1, intermediate, destination)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What is the `n_disks` argument when the function reaches its **base case**? Check for the base case with a simple `if` statement and return from the function using the **early exit** pattern!\n", + "\n", + "Hint: The base case in the Python implementation may be slightly different than the one shown in the generalized mathematical relationship above!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: If not in the base case, `sol()` determines the `intermediate` spot given concrete `origin` and `destination` arguments. For example, if called with `origin=1` and `destination=2`, `intermediate` must be `3`.\n", + "\n", + "Add *one* compound `if` statement to `sol()` that has a branch for *every* possible `origin`-`destination`-pair that assigns the correct temporary spot to a variable `intermediate`.\n", + "\n", + "Hint: How many 2-tuples of 3 elements can there be if the order matters?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: `sol()` calls itself *two* more times with the correct 2-tuples chosen from the three available spots `origin`, `intermediate`, and `destination`.\n", + "\n", + "*In between* the two recursive function calls, use [print() ](https://docs.python.org/3/library/functions.html#print) to print out from where to where the \"remaining and largest\" disk has to be moved!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: Execute the code cells below and confirm that the moves are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 -> 3\n" + ] + } + ], + "source": [ + "sol(1, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 -> 2\n", + "1 -> 3\n", + "2 -> 3\n" + ] + } + ], + "source": [ + "sol(2, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 -> 3\n", + "1 -> 2\n", + "3 -> 2\n", + "1 -> 3\n", + "2 -> 1\n", + "2 -> 3\n", + "1 -> 3\n" + ] + } + ], + "source": [ + "sol(3, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 -> 2\n", + "1 -> 3\n", + "2 -> 3\n", + "1 -> 2\n", + "3 -> 1\n", + "3 -> 2\n", + "1 -> 2\n", + "1 -> 3\n", + "2 -> 3\n", + "2 -> 1\n", + "3 -> 1\n", + "2 -> 3\n", + "1 -> 2\n", + "1 -> 3\n", + "2 -> 3\n" + ] + } + ], + "source": [ + "sol(4, 1, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pythonic Refactoring" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The previous `sol()` implementation does the job, but the conditional statement is unnecessarily tedious. \n", + "\n", + "Let's create a concise `hanoi()` function that, in addition to a positional `n_disks` argument, takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\n", + "\n", + "Write your answers to **Q9** and **Q10** into the subsequent code cell and finalize `hanoi()`!" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi(n_disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n", + " \"\"\"A Pythonic implementation of Towers of Hanoi.\n", + "\n", + " This function prints out the moves to solve a Towers of Hanoi problem.\n", + "\n", + " Args:\n", + " n_disks (int): number of disks in the tower\n", + " origin (str, optional): label for the spot of the tower at the start\n", + " intermediate (str, optional): label for the intermediate spot\n", + " destination (str, optional): label for the spot of the tower at the end\n", + " \"\"\"\n", + " # answer to Q9\n", + " if n_disks <= 0:\n", + " return\n", + "\n", + " # answer to Q10\n", + " hanoi(\n", + " n_disks - 1,\n", + " origin=origin,\n", + " intermediate=destination,\n", + " destination=intermediate,\n", + " )\n", + " print(origin, \"->\", destination)\n", + " hanoi(\n", + " n_disks - 1,\n", + " origin=intermediate,\n", + " intermediate=origin,\n", + " destination=destination,\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Copy the base case from `sol()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: Instead of conditional logic, `hanoi()` calls itself *two* times with the *three* arguments `origin`, `intermediate`, and `destination` passed on in a *different* order.\n", + "\n", + "Figure out how the arguments are passed on in the two recursive `hanoi()` calls and finish `hanoi()`.\n", + "\n", + "Hint: Do not forget to use [print() ](https://docs.python.org/3/library/functions.html#print) to print out the moves!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Execute the code cells below and confirm that the moves are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "left -> right\n" + ] + } + ], + "source": [ + "hanoi(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "left -> center\n", + "left -> right\n", + "center -> right\n" + ] + } + ], + "source": [ + "hanoi(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "left -> right\n", + "left -> center\n", + "right -> center\n", + "left -> right\n", + "center -> left\n", + "center -> right\n", + "left -> right\n" + ] + } + ], + "source": [ + "hanoi(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "left -> center\n", + "left -> right\n", + "center -> right\n", + "left -> center\n", + "right -> left\n", + "right -> center\n", + "left -> center\n", + "left -> right\n", + "center -> right\n", + "center -> left\n", + "right -> left\n", + "center -> right\n", + "left -> center\n", + "left -> right\n", + "center -> right\n" + ] + } + ], + "source": [ + "hanoi(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We could, of course, also use *numeric* labels for the three steps like so." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 -> 3\n", + "1 -> 2\n", + "3 -> 2\n", + "1 -> 3\n", + "2 -> 1\n", + "2 -> 3\n", + "1 -> 3\n" + ] + } + ], + "source": [ + "hanoi(3, origin=1, intermediate=2, destination=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Passing a Value \"down\" the Recursion Tree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The above `hanoi()` prints the optimal solution's moves in the correct order but fails to label each move with an order number. This is built in the `hanoi_ordered()` function below by passing on a \"private\" `_offset` argument \"down\" the recursion tree. The leading underscore `_` in the parameter name indicates that it is *not* to be used by the caller of the function. That is also why the parameter is *not* mentioned in the docstring.\n", + "\n", + "Write your answers to **Q12** and **Q13** into the subsequent code cell and finalize `hanoi_ordered()`! As the logic gets a bit \"involved,\" `hanoi_ordered()` below is almost finished." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi_ordered(n_disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\", _offset=None):\n", + " \"\"\"A Pythonic implementation of Towers of Hanoi.\n", + "\n", + " This function prints out the moves to solve a Towers of Hanoi problem.\n", + " Each move is labeled with an order number.\n", + "\n", + " Args:\n", + " n_disks (int): number of disks in the tower\n", + " origin (str, optional): label for the spot of the tower at the start\n", + " intermediate (str, optional): label for the intermediate spot\n", + " destination (str, optional): label for the spot of the tower at the end\n", + " \"\"\"\n", + " # answer to Q12\n", + " if n_disks <= 0:\n", + " return\n", + "\n", + " total = (2 ** n_disks - 1)\n", + " half = (2 ** (n_disks - 1) - 1)\n", + " count = total - half\n", + "\n", + " if _offset is not None:\n", + " count += _offset\n", + "\n", + " # answer to Q13\n", + " hanoi_ordered(\n", + " n_disks - 1,\n", + " origin=origin,\n", + " intermediate=destination,\n", + " destination=intermediate,\n", + " _offset=_offset,\n", + " )\n", + " print(count, origin, \"->\", destination)\n", + " hanoi_ordered(\n", + " n_disks - 1,\n", + " origin=intermediate,\n", + " intermediate=origin,\n", + " destination=destination,\n", + " _offset=count,\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Copy the base case from the original `hanoi()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: Complete the two recursive function calls with the same arguments as in `hanoi()`! Do *not* change the already filled in `offset` arguments!\n", + "\n", + "Then, adjust the use of [print() ](https://docs.python.org/3/library/functions.html#print) from above to print out the moves with their order number!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: Execute the code cells below and confirm that the order numbers are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 left -> right\n" + ] + } + ], + "source": [ + "hanoi_ordered(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 left -> center\n", + "2 left -> right\n", + "3 center -> right\n" + ] + } + ], + "source": [ + "hanoi_ordered(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 left -> right\n", + "2 left -> center\n", + "3 right -> center\n", + "4 left -> right\n", + "5 center -> left\n", + "6 center -> right\n", + "7 left -> right\n" + ] + } + ], + "source": [ + "hanoi_ordered(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 left -> center\n", + "2 left -> right\n", + "3 center -> right\n", + "4 left -> center\n", + "5 right -> left\n", + "6 right -> center\n", + "7 left -> center\n", + "8 left -> right\n", + "9 center -> right\n", + "10 center -> left\n", + "11 right -> left\n", + "12 center -> right\n", + "13 left -> center\n", + "14 left -> right\n", + "15 center -> right\n" + ] + } + ], + "source": [ + "hanoi_ordered(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, it is to be mentioned that for problem instances with a small `n_disks` argument, it is easier to collect all the moves first in a `list` object and then add the order number with the [enumerate() ](https://docs.python.org/3/library/functions.html#enumerate) built-in." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Open Question" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15**: Conducting your own research on the internet, what can you say about generalizing the **[Towers of Hanoi ](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem to a setting with *more than three* landing spots?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "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.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/04_iteration/04_exercises_solved.ipynb b/04_iteration/04_exercises_solved.ipynb new file mode 100644 index 0000000..83014a5 --- /dev/null +++ b/04_iteration/04_exercises_solved.ipynb @@ -0,0 +1,547 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 4: Recursion & Looping (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) of Chapter 4.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Throwing Dice" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this exercise, you will model the throwing of dice within the context of a guessing game similar to the one shown in the \"*Example: Guessing a Coin Toss*\" section in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-Guessing-a-Coin-Toss).\n", + "\n", + "As the game involves randomness, we import the [random ](https://docs.python.org/3/library/random.html) module from the [standard library ](https://docs.python.org/3/library/index.html). To follow best practices, we set the random seed as well." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A die has six sides that we labeled with integers `1` to `6` in this exercise. For a fair die, the probability for each side is the same.\n", + "\n", + "**Q1**: Model a `fair_die` as a `list` object!" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "fair_die = [1, 2, 3, 4, 5, 6]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What function from the [random ](https://docs.python.org/3/library/random.html) module that we have seen already is useful for modeling a single throw of the `fair_die`? Write a simple expression (i.e., one function call) that draws one of the equally likely sides! Execute the cell a couple of times to \"see\" the probability distribution!" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.choice(fair_die)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's check if the `fair_die` is indeed fair. To do so, we create a little numerical experiment and throw the `fair_die` `100000` times. We track the six different outcomes in a `list` object called `throws` that we initialize with all `0`s for each outcome.\n", + "\n", + "**Q3**: Complete the `for`-loop below such that it runs `100000` times! In the body, use your answer to **Q2** to simulate a single throw of the `fair_die` and update the corresponding count in `throws`!\n", + "\n", + "Hints: You need to use the indexing operator `[]` and calculate an `index` in each iteration of the loop. Do do not actually need the target variable provided by the `for`-loop and may want to indicate that with an underscore `_`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[16689, 16554, 16470, 16936, 16486, 16865]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "throws = [0, 0, 0, 0, 0, 0]\n", + "\n", + "for _ in range(100000):\n", + " throw = random.choice(fair_die)\n", + " index = throw - 1\n", + " throws[index] += 1\n", + "\n", + "throws" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`throws` contains the simulation results as absolute counts.\n", + "\n", + "**Q4**: Complete the `for`-loop below to convert the counts in `throws` to relative frequencies stored in a `list` called `frequencies`! Round the frequencies to three decimals with the built-in [round() ](https://docs.python.org/3/library/functions.html#round) function!\n", + "\n", + "Hints: Initialize `frequencies` just as `throws` above. How many iterations does the `for`-loop have? `6` or `100000`? You may want to obtain an `index` variable with the [enumerate() ](https://docs.python.org/3/library/functions.html#enumerate) built-in." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.167, 0.166, 0.165, 0.169, 0.165, 0.169]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "frequencies = [0, 0, 0, 0, 0, 0]\n", + "\n", + "for index, counts in enumerate(throws):\n", + " frequencies[index] = round(counts / 100000, 3)\n", + "\n", + "frequencies" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: How could we adapt the `list` object used above to model an `unfair_die` where `1` is as likely as `2`, `2` is twice as likely as `3`, and `3` is twice as likely as `4`, `5`, or `6`, who are all equally likely?" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "unfair_die = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 5, 6]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Copy your solution to **Q2** for the `unfair_die`! Execute the cell a couple of times to \"see\" the probability distribution!" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.choice(unfair_die)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Copy and adapt your solutions to **Q3** and **Q4** to calculate the `frequencies` for the `unfair_die`!" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.309, 0.307, 0.154, 0.077, 0.076, 0.078]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "throws = [0, 0, 0, 0, 0, 0]\n", + "frequencies = [0, 0, 0, 0, 0, 0]\n", + "\n", + "for _ in range(100000):\n", + " throw = random.choice(unfair_die)\n", + " index = throw - 1\n", + " throws[index] += 1\n", + "\n", + "for index, counts in enumerate(throws):\n", + " frequencies[index] = round(counts / 100000, 3)\n", + "\n", + "frequencies" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: The built-in [input() ](https://docs.python.org/3/library/functions.html#input) allows us to ask the user to enter a `guess`. What is the data type of the object returned by [input() ](https://docs.python.org/3/library/functions.html#input)? Assume the user enters the `guess` as a number (i.e., \"1\", \"2\", ...) and not as a text (e.g., \"one\")." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess the side of the die: 1\n" + ] + } + ], + "source": [ + "guess = input(\"Guess the side of the die: \")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'1'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guess" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(guess)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Use a built-in constructor to cast `guess` as an `int` object!\n", + "\n", + "Hint: Simply wrap `guess` or `input(\"Guess the side of the die: \")` with the constructor you choose." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(guess)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: What type of error is raised if `guess` cannot be cast as an `int` object?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Write a `try` statement that catches the type of error (i.e., your answer to **Q10**) raised if the user's input cannot be cast as an `int` object! Print out some nice error message notifying the user of the bad input!" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess the side of the die: random\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Make sure to enter your guess correctly!\n" + ] + } + ], + "source": [ + "try:\n", + " guess = int(input(\"Guess the side of the die: \"))\n", + "except ValueError:\n", + " print(\"Make sure to enter your guess correctly!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Write a function `get_guess()` that takes a user's input and checks if it is a valid side of the die! The function should *return* either an `int` object between `1` and `6` or `None` if the user enters something invalid.\n", + "\n", + "Hints: You may want to re-use the `try` statement from **Q11**. Instead of printing out an error message, you can also `return` directly from the `except`-clause (i.e., early exit) with `None`. So, the user can make *two* kinds of input errors and maybe you want to model that with two *distinct* `return None` statements. Also, you may want to allow the user to enter leading and trailing whitespace that gets removed without an error message." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "def get_guess():\n", + " \"\"\"Process the user's input.\n", + " \n", + " Returns:\n", + " guess (int / NoneType): either 1, 2, 3, 4, 5 or 6\n", + " if the input can be parsed and None otherwise\n", + " \"\"\"\n", + " guess = input(\"Guess the side of the die: \")\n", + "\n", + " # Check if the user entered an integer.\n", + " try:\n", + " guess = int(guess.strip())\n", + " except ValueError:\n", + " return None\n", + "\n", + " # Check if the user entered a valid side.\n", + " if 1 <= guess <= 6:\n", + " return guess\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13** Test your function for all *three* cases!" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess the side of the die: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_guess()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: Write an *indefinite* loop where in each iteration a `fair_die` is thrown and the user makes a guess! Print out an error message if the user does not enter something that can be understood as a number between `1` and `6`! The game should continue until the user makes a correct guess." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess the side of the die: 1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Yes, it was 1\n" + ] + } + ], + "source": [ + "while True:\n", + " guess = get_guess()\n", + " result = random.choice(fair_die)\n", + "\n", + " if guess is None:\n", + " print(\"Make sure to enter your guess correctly!\")\n", + " elif guess == result:\n", + " print(\"Yes, it was\", result)\n", + " break\n", + " else:\n", + " print(\"Ooops, it was\", result)" + ] + } + ], + "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.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}