Add video and streamline content

This commit is contained in:
Alexander Hess 2020-03-30 08:32:16 +02:00
parent 0151478bae
commit 6e68175252
4 changed files with 1032 additions and 558 deletions

File diff suppressed because one or more lines are too long

View file

@ -19,7 +19,9 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb) of the book. Then, work through the questions below." "The questions below assume that you have read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb) in the book.\n",
"\n",
"Be concise in your answers! Most questions can be answered in *one* sentence."
] ]
}, },
{ {
@ -29,13 +31,6 @@
"### Essay Questions " "### Essay Questions "
] ]
}, },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Answer the following questions *briefly*!"
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
@ -47,7 +42,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -61,7 +56,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -75,7 +70,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -89,7 +84,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -103,7 +98,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -117,7 +112,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -145,7 +140,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -159,7 +154,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -173,7 +168,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -187,7 +182,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -201,7 +196,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -215,7 +210,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -240,7 +235,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
} }
], ],

View file

@ -19,7 +19,9 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb) of the book. Then, work through the exercises below. The `...` indicate where you need to fill in your answers. You should not need to create any additional code cells. Occasionally, the `...` mean that you have to write more than one line of code." "The exercises below assume that you have read the \"*Recursion*\" part in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb) of the book.\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."
] ]
}, },
{ {
@ -95,7 +97,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -109,7 +111,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -123,7 +125,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -137,7 +139,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
}, },
{ {
@ -151,53 +153,52 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "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)**." "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", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Let's first **generalize** the mathematical relationship from above.\n", "Let's first **generalize** the mathematical relationship from above and then introduce the variable names used in our `sol()` implementation below.\n",
"\n", "\n",
"While the first number of $Sol(\\cdot)$ is the number of `disks` $n$, the second and third \"numbers\" are the **labels** for the three spots. Instead of spots `1`, `2`, and `3`, we could also call them `\"left\"`, `\"center\"`, and `\"right\"` in our Python implementation. When \"passed\" to the $Sol(\\cdot)$ \"function\" they take on the role of an `origin` (= $o$) and `destination` (= $d$) pair.\n", "Unsurprisingly, the recursive relationship in the video may be generalized into:\n",
"\n", "\n",
"So, the expression $Sol(4, 1, 3)$ is the same as $Sol(4, \\text{\"left\"}, \\text{\"right\"})$ and describes the problem of moving a tower consisting of $n = 4$ disks from `origin` `1` / `\"left\"` to `destination` `3` / `right`. As we have seen in the video, we need some `intermediate` (= $i$) spot." "$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", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"In summary, the generalized functional relationship can be expressed as:\n", "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", "\n",
"$Sol(n, o, d) = Sol(n-1, o, i) ~ \\bigoplus ~ Sol(1, o, d) ~ \\bigoplus ~ Sol(n-1, i, d)$" "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", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"In words, this means that to move a tower consisting of $n$ disks from an `origin` $o$ to a `destination` $d$, three steps must be executed:\n", "Write your answers to **Q5** to **Q7** into the skeleton of `sol()` below.\n",
"\n", "\n",
"1. Move the topmost $n - 1$ disks of the tower temporarily from $o$ to $i$ (= sub-problem 1)\n", "`sol()` takes three arguments `n_disks`, `origin`, and `destination` that mirror $n$, $o$, and $d$ above.\n",
"2. Move the remaining and largest disk from $o$ to $d$\n",
"3. Move the the $n - 1$ disks from the temporary spot $i$ to $d$ (= sub-problem 2)\n",
"\n", "\n",
"The two sub-problems can be solved via the same recursive logic." "For now, assume that all arguments to `sol()` are `int` objects! We generalize this into real labels further below in the `hanoi()` function.\n",
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$Sol(\\cdot)$ can be written in Python as a function `sol()` that takes three arguments `disks`, `origin`, and `destination` that mirror $n$, $o$, and $d$.\n",
"\n", "\n",
"Assume that all arguments to `sol()` are `int` objects!\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`.\""
"\n",
"Once completed, `sol()` should print out all the moves in the correct order. With **printing a move**, we mean a line like \"1 -> 3\", short for \"Move the top-most disk from spot 1 to spot 3\".\n",
"\n",
"Write your answers to **Q5** to **Q7** into the subsequent code cell and finalize `sol()`! No need to write a docstring or validate the input here."
] ]
}, },
{ {
@ -206,48 +207,74 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"def sol(disks, origin, destination):\n", "def sol(n_disks, origin, destination):\n",
" \"\"\"A naive implementation of Towers of Hanoi.\n",
"\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", " # answer to Q5\n",
" # ...\n", " ...\n",
" ...\n",
"\n", "\n",
" # answer to Q6\n", " # answer to Q6\n",
" # ...\n", " ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n", "\n",
" # answer to Q7\n", " # answer to Q7\n",
" # ..." " ...\n",
" ...\n",
" ..."
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q5**: What is the `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!" "**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",
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: If not in the base case, `sol()` needs to determine 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", "\n",
"Add **one** compound `if` statement to `sol()` that has a branch for **every** possible `origin`-`destination` pair that sets a variable `intermediate` to the correct temporary spot. **How many** branches will there be?" "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", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q7**: `sol()` needs to call itself **two more times** with the correct 2-pairs chosen from the three available spots `origin`, `intermediate`, and `destination`.\n", "**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", "\n",
"In between the two recursive function calls, write a `print()` statement that prints out from where to where the \"remaining and largest\" disk has to be moved!" "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", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q8**: Execute the code cells below and confirm that the printed moves are correct!" "**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!"
] ]
}, },
{ {
@ -297,11 +324,11 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"The previous `sol()` implementation does the job, but the conditional statement needed in unnecessarily tedious. \n", "The previous `sol()` implementation does the job, but the conditional statement is unnecessarily tedious. \n",
"\n", "\n",
"Let's create a more concise `hanoi()` function that, in addition to a positional `disks` argument, takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\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", "\n",
"Write your answers to **Q9** and **Q10** into the subsequent code cell and finalize `hanoi()`! No need to write a docstring or validate the input here." "Write your answers to **Q9** and **Q10** into the subsequent code cell and finalize `hanoi()`!"
] ]
}, },
{ {
@ -310,13 +337,25 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"def hanoi(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n", "def hanoi(n_disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n",
" \"\"\"A Pythonic implementation of Towers of Hanoi.\n",
"\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", " # answer to Q9\n",
" # ...\n", " ...\n",
" ...\n",
"\n", "\n",
" # answer to Q10\n", " # answer to Q10\n",
" # ..." " ...\n",
" ...\n",
" ..."
] ]
}, },
{ {
@ -330,18 +369,18 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "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", "**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", "\n",
"Figure out how the arguments are passed on in the two recursive `hanoi()` calls!\n", "Figure out how the arguments are passed on in the two recursive `hanoi()` calls and finish `hanoi()`.\n",
"\n", "\n",
"Also, write a `print()` statement analogous to the one in `sol()` in between the two recursive function calls. Is it ok to copy and paste it?" "Hint: Do not forget to use [print()](https://docs.python.org/3/library/functions.html#print) to print out the moves!"
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q11**: Execute the code cells below and confirm that the printed moves are correct!" "**Q11**: Execute the code cells below and confirm that the moves are correct!"
] ]
}, },
{ {
@ -384,7 +423,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"We could, of course, also use **numeric labels** for the three steps like so." "We could, of course, also use *numeric* labels for the three steps like so."
] ]
}, },
{ {
@ -400,22 +439,16 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"#### Passing a Value \"up\" the Recursion Tree" "#### Passing a Value \"down\" the Recursion Tree"
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Let's say we did not know about the **analytical formula** for the number of **minimal moves** given $n$.\n", "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", "\n",
"In such cases, we could modify a recursive function to return a count value to be passed up the recursion tree.\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."
"\n",
"This is similar to what we do in the recursive versions of `factorial()` and `fibonacci()` in [Chapter 4](https://github.com/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb), where we pass up an intermediate result.\n",
"\n",
"Let's create a `hanoi_moves()` function that follows the same internal logic as `hanoi()`, but, instead of printing out the moves, returns the number of steps done so far in the recursion.\n",
"\n",
"Write your answers to **Q12** to **Q14** into the subsequent code cell and finalize `hanoi_moves()`! No need to write a docstring or validate the input here."
] ]
}, },
{ {
@ -424,166 +457,56 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"def hanoi_moves(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n", "def hanoi_ordered(n_disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\", _offset=None):\n",
" \"\"\"A Pythonic implementation of Towers of Hanoi.\n",
"\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", " # answer to Q12\n",
" # ...\n", " ...\n",
" ...\n",
"\n", "\n",
" moves = ... # <- answer to Q13\n", " total = (2 ** n_disks - 1)\n",
" moves += hanoi_moves(...) # <- answer to Q14 between the ()\n", " half = (2 ** (n_disks - 1) - 1)\n",
" moves += hanoi_moves(...) # <- answer to Q14 between the ()\n",
"\n",
" return moves"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q12**: Copy the base case from `hanoi()`! What count should be returned when it is reached?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q13**: Initialize the variable `moves` with an appropriate count! This is the number of moves that corresponds to **one** recursive function call."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q14**: `moves` is updated with the counts passed up from the two recursive calls.\n",
"\n",
"Complete the two recursive function calls with the same arguments as in `hanoi()`!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q15**: Write a `for`-loop that prints out the **minimum number** of moves needed to solve Towers of Hanoi for any number of `disks` from `1` through `20` to confirm your answer to **Q2**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for ... in ...:\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Time Complexity"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Observe how quickly the `hanoi_moves()` function slows down for increasing `disks` arguments.\n",
"\n",
"With `disks` in the range from `24` through `26`, the computation time roughly doubles for each increase of `disks` by 1.\n",
"\n",
"**Q16**: Execute the code cells below and see for yourself!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%timeit -n 1 -r 1\n",
"print(\"Number of moves:\", hanoi_moves(24))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%timeit -n 1 -r 1\n",
"print(\"Number of moves:\", hanoi_moves(25))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%timeit -n 1 -r 1\n",
"print(\"Number of moves:\", hanoi_moves(26))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Passing a Value \"down\" the Recursion Tree (Advanced)"
]
},
{
"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 can be built in by passing on one more argument `offset` down the recursion tree. As the logic gets a bit \"involved,\" `hanoi_ordered()` below is almost finished.\n",
"\n",
"Write your answers to **Q17** and **Q18** into the subsequent code cell and finalize `hanoi_ordered()`! No need to write a docstring or validate the input here."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def hanoi_ordered(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\", offset=None):\n",
"\n",
" # answer to Q17\n",
" # ...\n",
"\n",
" total = (2 ** disks - 1)\n",
" half = (2 ** (disks - 1) - 1)\n",
" count = total - half\n", " count = total - half\n",
"\n", "\n",
" if offset is not None:\n", " if _offset is not None:\n",
" count += offset\n", " count += _offset\n",
"\n", "\n",
" hanoi_ordered(..., offset=offset) # <- answer to Q18\n", " # answer to Q18\n",
" # ... <- answer to Q18\n", " hanoi_ordered(..., _offset=_offset)\n",
" hanoi_ordered(..., offset=count) # <- answer to Q18" " ...\n",
" hanoi_ordered(..., _offset=count)"
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q17**: Copy the base case from the original `hanoi()`!" "**Q12**: Copy the base case from the original `hanoi()`!"
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q18**: Complete the two recursive function calls with the same arguments as in `hanoi()` or `hanoi_moves()`! Do not change the already filled in `offset` arguments!\n", "**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", "\n",
"Then, copy the `print()` statement from `hanoi()` and adjust it to print out `count` as well!" "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", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q19**: Execute the code cells below and confirm that the order numbers are correct!" "**Q14**: Execute the code cells below and confirm that the order numbers are correct!"
] ]
}, },
{ {
@ -626,7 +549,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Lastly, it is to be mentioned that for problem instances with a small `disks` argument, it is easier to collect all the moves first in a list and then add the order number with the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in." "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."
] ]
}, },
{ {
@ -640,14 +563,14 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"**Q20**: Conducting your own research on the internet (max. 15 minutes), 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?" "**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", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" " " < your answer >"
] ]
} }
], ],

View file

@ -19,7 +19,9 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb) of the book. Then, work through the exercises below. The `...` indicate where you need to fill in your answers. You should not need to create any additional code cells." "The exercises below assume that you have read the \"*Looping*\" part in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb) of the book.\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."
] ]
}, },
{ {
@ -264,7 +266,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
" ..." " < your answer >"
] ]
}, },
{ {