Commit 9474c0be authored by Ben Glocker's avatar Ben Glocker
Browse files

added divide and conquer notebook

parent 046828c7
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# CO202 - Algorithms 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tutorial on Divide-and-Conquer: Integer Multiplication"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Some helper functions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import math\n",
"\n",
"# calculates the number of digits of an integer\n",
"def digits(x):\n",
" return int(math.log(x, 10) + 1)\n",
"\n",
"# alternative\n",
"digits_alt = lambda i: len(str(i))\n",
"\n",
"\n",
"# splits an integer into two parts after digit i\n",
"def split(x, i):\n",
" left = int(x / (10 ** i)) # ** is the power operator\n",
" right = int(x % (10 ** i))\n",
" return left, right\n",
"\n",
"print(digits(1234) == digits_alt(1234))\n",
"print(split(1234, 2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Recursive Multiplication"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# recursive multiplication breaks down the problem into four subproblems\n",
"def recursive_mul(x, y):\n",
" if x < 10 or y < 10:\n",
" return x * y\n",
" \n",
" # Tuple unpacking can be nice for repeated function calls..\n",
" nx, ny = digits(x), digits(y)\n",
" n = max(nx, ny)\n",
" n2 = int(math.floor(n / 2))\n",
" \n",
" a, b = split(x, n2)\n",
" c, d = split(y, n2)\n",
" \n",
" ac = recursive_mul(a, c)\n",
" ad = recursive_mul(a, d)\n",
" bc = recursive_mul(b, c)\n",
" bd = recursive_mul(b, d)\n",
" \n",
" return ac * (10 ** (2 * n2)) + (ad + bc) * (10 ** n2) + bd\n",
"\n",
"x = 1234\n",
"y = 5678\n",
"print(recursive_mul(x,y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Karatsuba Multiplication"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def karatsuba_mul(x, y):\n",
" if x < 10 or y < 10:\n",
" return x * y\n",
" \n",
" nx, ny = digits(x), digits(y)\n",
" n = max(nx, ny)\n",
" n2 = int(math.floor(n / 2))\n",
" \n",
" a, b = split(x, n2)\n",
" c, d = split(y, n2)\n",
" \n",
" ac = karatsuba_mul(a, c)\n",
" bd = karatsuba_mul(b, d)\n",
" abcd = karatsuba_mul(a + b, c + d)\n",
" adbc = abcd - ac - bd;\n",
" \n",
" return ac * (10 ** (2 * n2)) + adbc * (10 ** n2) + bd\n",
"\n",
"print(karatsuba_mul(x, y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Baseline Multiplication"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def baseline_mul(x, y):\n",
" return x * y\n",
"\n",
"# alternative using lambda\n",
"baseline_mul_alt = lambda x, y: x * y\n",
"\n",
"print(baseline_mul(x, y))\n",
"\n",
"print(baseline_mul(x, y) == baseline_mul_alt(x, y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Comparison"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Generate some test data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from random import randint\n",
"\n",
"# Generate an integer with n digits\n",
"def randint_of_len(n):\n",
" return randint(10 ** n, (10 ** (n + 1)) - 1)\n",
"\n",
"\n",
"def rand_pair(n):\n",
" m = randint(2, n) # choose a random length\n",
" return randint_of_len(m), randint_of_len(m)\n",
"\n",
"n = 200\n",
"\n",
"# '_' is commonly used when you don't care about a variable\n",
"random_pairs = [rand_pair(n) for _ in range(200)]\n",
"\n",
"print(rand_pair(n))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Timer"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import time\n",
"\n",
"def time_f(f):\n",
" before = time.clock()\n",
" f()\n",
" after = time.clock()\n",
" return after - before"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Running Recursive Multiplication"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"tr = []\n",
"nr = []\n",
"\n",
"for r_pair in random_pairs:\n",
" tr.append(time_f(lambda: recursive_mul(r_pair[0], r_pair[1])))\n",
" nr.append(digits(r_pair[0]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Running Karatsuba Multiplication"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"tk = []\n",
"nk = []\n",
"\n",
"for r_pair in random_pairs:\n",
" tk.append(time_f(lambda: karatsuba_mul(r_pair[0], r_pair[1])))\n",
" nk.append(digits(r_pair[0]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Plot Recursive vs Karatsuba"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"from matplotlib import pyplot as plt\n",
"\n",
"plt.scatter(nr, tr, c='red')\n",
"plt.scatter(nk, tk, c='blue')\n",
"plt.xlabel('n')\n",
"plt.ylabel('time (/s)')\n",
"plt.xlim(0)\n",
"plt.ylim(0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Running Baseline Multiplication"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"tb = []\n",
"nb = []\n",
"\n",
"for r_pair in random_pairs:\n",
" tb.append(time_f(lambda: baseline_mul(r_pair[0], r_pair[1])))\n",
" nb.append(digits(r_pair[0]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"from matplotlib import pyplot as plt\n",
"\n",
"plt.scatter(nr, tr, c='red')\n",
"plt.scatter(nk, tk, c='blue')\n",
"plt.scatter(nb, tb, c='green')\n",
"plt.xlabel('n')\n",
"plt.ylabel('time (/s)')\n",
"plt.xlim(0)\n",
"plt.ylim(0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Exploring built-in multiplication"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"random_pairs = [rand_pair(200) for _ in range(200)]\n",
"#andom_pairs = [rand_pair(2000) for _ in range(200)]\n",
"#random_pairs = [rand_pair(200000) for _ in range(200)]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"tb = []\n",
"nb = []\n",
"\n",
"for r_pair in random_pairs:\n",
" tb.append(time_f(lambda: baseline_mul(r_pair[0], r_pair[1])))\n",
" nb.append(digits(r_pair[0]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"from matplotlib import pyplot as plt\n",
"\n",
"plt.scatter(nb, tb, c='green')\n",
"plt.xlabel('n')\n",
"plt.ylabel('time (/s)')\n",
"plt.xlim(0)\n",
"plt.ylim(0)"
]
}
],
"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.6.2"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
%% Cell type:markdown id: tags:
# CO202 - Algorithms 2
%% Cell type:markdown id: tags:
## Tutorial on Divide-and-Conquer: Integer Multiplication
%% Cell type:markdown id: tags:
### Some helper functions
%% Cell type:code id: tags:
``` python
import math
# calculates the number of digits of an integer
def digits(x):
return int(math.log(x, 10) + 1)
# alternative
digits_alt = lambda i: len(str(i))
# splits an integer into two parts after digit i
def split(x, i):
left = int(x / (10 ** i)) # ** is the power operator
right = int(x % (10 ** i))
return left, right
print(digits(1234) == digits_alt(1234))
print(split(1234, 2))
```
%% Cell type:markdown id: tags:
### Recursive Multiplication
%% Cell type:code id: tags:
``` python
# recursive multiplication breaks down the problem into four subproblems
def recursive_mul(x, y):
if x < 10 or y < 10:
return x * y
# Tuple unpacking can be nice for repeated function calls..
nx, ny = digits(x), digits(y)
n = max(nx, ny)
n2 = int(math.floor(n / 2))
a, b = split(x, n2)
c, d = split(y, n2)
ac = recursive_mul(a, c)
ad = recursive_mul(a, d)
bc = recursive_mul(b, c)
bd = recursive_mul(b, d)
return ac * (10 ** (2 * n2)) + (ad + bc) * (10 ** n2) + bd
x = 1234
y = 5678
print(recursive_mul(x,y))
```
%% Cell type:markdown id: tags:
### Karatsuba Multiplication
%% Cell type:code id: tags:
``` python
def karatsuba_mul(x, y):
if x < 10 or y < 10:
return x * y
nx, ny = digits(x), digits(y)
n = max(nx, ny)
n2 = int(math.floor(n / 2))
a, b = split(x, n2)
c, d = split(y, n2)
ac = karatsuba_mul(a, c)
bd = karatsuba_mul(b, d)
abcd = karatsuba_mul(a + b, c + d)
adbc = abcd - ac - bd;
return ac * (10 ** (2 * n2)) + adbc * (10 ** n2) + bd
print(karatsuba_mul(x, y))
```
%% Cell type:markdown id: tags:
### Baseline Multiplication
%% Cell type:code id: tags:
``` python
def baseline_mul(x, y):
return x * y
# alternative using lambda
baseline_mul_alt = lambda x, y: x * y
print(baseline_mul(x, y))
print(baseline_mul(x, y) == baseline_mul_alt(x, y))
```
%% Cell type:markdown id: tags:
### Comparison
%% Cell type:markdown id: tags:
#### Generate some test data
%% Cell type:code id: tags:
``` python
from random import randint
# Generate an integer with n digits
def randint_of_len(n):
return randint(10 ** n, (10 ** (n + 1)) - 1)
def rand_pair(n):
m = randint(2, n) # choose a random length
return randint_of_len(m), randint_of_len(m)
n = 200
# '_' is commonly used when you don't care about a variable
random_pairs = [rand_pair(n) for _ in range(200)]
print(rand_pair(n))
```
%% Cell type:markdown id: tags:
#### Timer
%% Cell type:code id: tags:
``` python
import time
def time_f(f):
before = time.clock()
f()
after = time.clock()
return after - before
```
%% Cell type:markdown id: tags:
#### Running Recursive Multiplication
%% Cell type:code id: tags:
``` python
tr = []
nr = []
for r_pair in random_pairs:
tr.append(time_f(lambda: recursive_mul(r_pair[0], r_pair[1])))
nr.append(digits(r_pair[0]))
```
%% Cell type:markdown id: tags:
#### Running Karatsuba Multiplication
%% Cell type:code id: tags:
``` python
tk = []
nk = []
for r_pair in random_pairs:
tk.append(time_f(lambda: karatsuba_mul(r_pair[0], r_pair[1])))
nk.append(digits(r_pair[0]))
```
%% Cell type:markdown id: tags:
#### Plot Recursive vs Karatsuba
%% Cell type:code id: tags:
``` python
%matplotlib inline
from matplotlib import pyplot as plt
plt.scatter(nr, tr, c='red')
plt.scatter(nk, tk, c='blue')
plt.xlabel('n')
plt.ylabel('time (/s)')
plt.xlim(0)
plt.ylim(0)
```
%% Cell type:markdown id: tags:
#### Running Baseline Multiplication
%% Cell type:code id: tags:
``` python
tb = []
nb = []
for r_pair in random_pairs:
tb.append(time_f(lambda: baseline_mul(r_pair[0], r_pair[1])))
nb.append(digits(r_pair[0]))
```
%% Cell type:code id: tags:
``` python
%matplotlib inline
from matplotlib import pyplot as plt
plt.scatter(nr, tr, c='red')
plt.scatter(nk, tk, c='blue')
plt.scatter(nb, tb, c='green')
plt.xlabel('n')
plt.ylabel('time (/s)')
plt.xlim(0)
plt.ylim(0)
```
%% Cell type:markdown id: tags:
#### Exploring built-in multiplication
%% Cell type:code id: tags:
``` python
random_pairs = [rand_pair(200) for _ in range(200)]
#andom_pairs = [rand_pair(2000) for _ in range(200)]
#random_pairs = [rand_pair(200000) for _ in range(200)]
```
%% Cell type:code id: tags:
``` python
tb = []
nb = []
for r_pair in random_pairs:
tb.append(time_f(lambda: baseline_mul(r_pair[0], r_pair[1])))
nb.append(digits(r_pair[0]))
```
%% Cell type:code id: tags:
``` python
%matplotlib inline
from matplotlib import pyplot as plt
plt.scatter(nb, tb, c='green')
plt.xlabel('n')
plt.ylabel('time (/s)')
plt.xlim(0)
plt.ylim(0)
```
Supports Markdown
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