Commit f8568285 authored by Steve Tjoa's avatar Steve Tjoa

dynamic programming

parent d0d59a02
This diff is collapsed.
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import seaborn\n",
"import numpy, scipy, scipy.spatial, matplotlib.pyplot as plt\n",
"plt.rcParams['figure.figsize'] = (14, 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[← Back to Index](index.html)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Dynamic Programming"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Dynamic programming** ([Wikipedia](https://en.wikipedia.org/wiki/Dynamic_programming); FMP, p. 137) is a method for solving problems by breaking them into simpler subproblems, each solved only once, and storing their solutions for future reference. The act of storing solutions to subproblems is known as **memoization** ([Wikipedia](https://en.wikipedia.org/wiki/Memoization))."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Example: min coin sum"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Given a positive value and a list of possible coin values, write a function that determines the minimum number of coins needed to achieve the input value. Example:\n",
"\n",
" coins = [1, 5, 10, 25]\n",
" min_coin_sum(1) -> 1\n",
" min_coin_sum(6) -> 2 # 1*5 + 1*1\n",
" min_coin_sum(49) -> 7 # 1*25 + 2*10 + 4*1\n",
" min_coin_sum(52) -> 4 # 2*25 + 2*1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Suppose that we use the coin values `[1, 5, 10, 25]`. The recursive solution to this uses the observation:\n",
"\n",
" min_coin_sum(val) = 1 + min(\n",
" min_coin_sum(val-1),\n",
" min_coin_sum(val-5),\n",
" min_coin_sum(val-10),\n",
" min_coin_sum(val-25),\n",
" )\n",
"\n",
"Suppose `val = 49`. Notice that \n",
"\n",
" 49 = 48 + 1 \n",
" 49 = 44 + 5\n",
" 49 = 39 + 10\n",
" 49 = 24 + 25\n",
" \n",
"In other words, 49 is achieved by adding one coin to the minimum coin total used to achieve 48, 44, 39, or 24."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create a recursive solution:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def min_coin_sum(val, coins=None):\n",
" if coins is None:\n",
" coins = [1, 5, 10, 25]\n",
" if val == 0:\n",
" return 0\n",
" return 1 + min(min_coin_sum(val-coin) for coin in coins if val-coin >= 0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Test:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"val num_coins\n",
" 1 1\n",
" 6 2\n",
" 42 5\n",
" 49 7\n"
]
}
],
"source": [
"coins = [1, 5, 10, 25]\n",
"print 'val num_coins'\n",
"for val in (1, 6, 42, 49):\n",
" print '%3d %d' % (val, min_coin_sum(val, coins))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice how it takes a little while to compute the answer to `49`? That's because the recursive solution is computing the solution to subproblems multiple times. For example, the solution to `49` uses the solutions to `44` and `39`. However, the solution to `44` itself needs the solution to `39` which it needlessly computes again from scratch."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Memoization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A better solution is to use *memoization* by storing the answer to previous subproblems and referring to them later:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def min_coin_sum(val, coins=None):\n",
" if coins is None:\n",
" coins = [1, 5, 10, 25]\n",
"\n",
" # Initialize table.\n",
" table = [0 for _ in range(val+1)]\n",
" \n",
" # Dynamic programming.\n",
" for cur_val in range(1, val+1):\n",
" table[cur_val] = 1 + min([table[cur_val-coin] for coin in coins if cur_val-coin >= 0])\n",
" return table[val]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"val num_coins\n",
" 1 1\n",
" 6 2\n",
" 42 5\n",
" 49 7\n",
" 52 4\n"
]
}
],
"source": [
"coins = [1, 5, 10, 25]\n",
"print 'val num_coins'\n",
"for val in (1, 6, 42, 49, 52):\n",
" print '%3d %d' % (val, min_coin_sum(val, coins))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice how this executes much faster than the fully recursive version. The time complexity is only $O(v n)$, where $v$ is the input coin value, and $n$ is the number of coin values."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[← Back to Index](index.html)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
......@@ -11939,6 +11939,7 @@ div#notebook {
<div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<ol>
<li><a href="dp.html">Dynamic Programming</a> (<a href="dp.ipynb">ipynb</a>)</li>
<li><a href="dtw.html">Dynamic Time Warping</a> (<a href="dtw.ipynb">ipynb</a>)</li>
</ol>
......
......@@ -136,6 +136,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"1. [Dynamic Programming](dp.html) ([ipynb](dp.ipynb))\n",
"1. [Dynamic Time Warping](dtw.html) ([ipynb](dtw.ipynb))"
]
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment