Repository: coells/100days
Branch: master
Commit: 986a81130a13
Files: 104
Total size: 13.5 MB
Directory structure:
gitextract_c9zpuegw/
├── .gitignore
├── README.md
├── bonus - fast convex hull.ipynb
├── day 00 - logo.ipynb
├── day 01 - hanoi tower.ipynb
├── day 02 - matrix chain multiplication.ipynb
├── day 03 - next permutation.ipynb
├── day 04 - counting 1-bits.ipynb
├── day 05 - eratosthenes sieve.ipynb
├── day 06 - postfix notation.ipynb
├── day 07 - binary addition FSA.ipynb
├── day 08 - binary search.ipynb
├── day 09 - monte carlo - pi.ipynb
├── day 10 - karatsuba multiplication.ipynb
├── day 11 - McCarthy 91.ipynb
├── day 12 - roots of polynomial.ipynb
├── day 13 - extended euclidean algorithm.ipynb
├── day 14 - huffman codes.ipynb
├── day 15 - breaking OTP.ipynb
├── day 16 - no-condition swap.ipynb
├── day 17 - perceptron.ipynb
├── day 18 - monopoly.ipynb
├── day 19 - counting inversions.ipynb
├── day 20 - linearithmic multiplication.ipynb
├── day 21 - k-means.ipynb
├── day 22 - determinant.ipynb
├── day 23 - sudoku.ipynb
├── day 24 - closest pair.ipynb
├── day 25 - conjugate gradients.ipynb
├── day 26 - karger's mincut.ipynb
├── day 27 - spiral matrix.ipynb
├── day 28 - convex hull.ipynb
├── day 29 - string searching.ipynb
├── day 30 - strassen multiplication.ipynb
├── day 31 - timeit.ipynb
├── day 32 - pagerank.ipynb
├── day 33 - reservoir sampling.ipynb
├── day 34 - aho-corasick.ipynb
├── day 35 - median.ipynb
├── day 36 - bulls and cows.ipynb
├── day 37 - longest common subsequence.ipynb
├── day 38 - burrows-wheeler.ipynb
├── day 39 - 4sum.ipynb
├── day 40 - counting ones.ipynb
├── day 41 - union-find.ipynb
├── day 42 - hamming codes.ipynb
├── day 43 - shuffle.ipynb
├── day 44 - gradient approximation.ipynb
├── day 45 - binary search tree.ipynb
├── day 46 - bézier curve.ipynb
├── day 47 - factoradic.ipynb
├── day 48 - dijkstra.ipynb
├── day 49 - ford-fulkerson.ipynb
├── day 50 - game of life.ipynb
├── day 51 - rabin-miller.ipynb
├── day 52 - RSA.ipynb
├── day 53 - RSA encryption scheme.ipynb
├── day 54 - longest unique sequence.ipynb
├── day 55 - quincunx.ipynb
├── day 56 - lzw.ipynb
├── day 57 - quicksort.ipynb
├── day 58 - integer exponentation.ipynb
├── day 59 - colored tiling.ipynb
├── day 60 - bloom filter.ipynb
├── day 61 - hanoi tower II.ipynb
├── day 62 - linked-list cycle.ipynb
├── day 63 - zig-zag.ipynb
├── day 64 - k-clique.ipynb
├── day 65 - floyd-warshall.ipynb
├── day 66 - gram-schmidt.ipynb
├── day 67 - linked-list mergesort.ipynb
├── day 68 - gale-shapley.ipynb
├── day 69 - rmsprop.ipynb
├── day 70 - deadlock.ipynb
├── day 71 - hashtable - chaining.ipynb
├── day 72 - hashtable - open addressing.ipynb
├── day 73 - bresenhams line.ipynb
├── day 74 - google interview question.ipynb
├── day 75 - merkles puzzles.ipynb
├── day 76 - 2-3 tree.ipynb
├── day 77 - unification.ipynb
├── day 78 - horn-satifiability.ipynb
├── day 79 - logistic regression.ipynb
├── day 80 - hopfield net.ipynb
├── day 81 - topological sort.ipynb
├── day 82 - flood fill.ipynb
├── day 83 - breaking AES.ipynb
├── day 84 - maze generation.ipynb
├── day 85 - coin success runs.ipynb
├── day 86 - binary heap.ipynb
├── day 87 - gray code.ipynb
├── day 88 - perlin noise.ipynb
├── day 89 - bipartiteness.ipynb
├── day 90 - simple nim - AI.ipynb
├── day 91 - variations.ipynb
├── day 92 - PCA.ipynb
├── day 93 - first and follow.ipynb
├── day 94 - earley parser.ipynb
├── day 95 - strongly connected components.ipynb
├── day 96 - floyd-steinberg.ipynb
├── day 97 - locally weighted regression.ipynb
├── day 98 - romberg integration.ipynb
├── day 99 - simplex.ipynb
└── day I00 - segmented eratosthenes sieve.ipynb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.ipynb_checkpoints
================================================
FILE: README.md
================================================
### 100 days of algorithms
This repository contains notebooks with live code to accompany [100 days of algorithms](https://medium.com/100-days-of-algorithms) challenge.
I set the challenge for myself to implement algorithm by algorithm, day by day, until the number reaches **100**.
If you are interested, here's the [intro to the series](https://medium.com/100-days-of-algorithms/100-days-of-algorithms-challenge-41996f7e1ec8) and [all the articles](https://medium.com/100-days-of-algorithms/latest) sorted by date from the latest.
The challenge was quite fun and rough, as well. Do not expect the implementations to be the best, nor fastest, nor nicest, nor bug-free. Do expect to see code written in haste. A code that contains the same amount of enthusiasm and love to algorithms as many it contains bugs.
Feel free to (re)use my code in any way you wish, but bare in mind that the source code is provided "as-is". It is on your own risk and you are solely responsible for whatever happens then.
#### local machine
* download and install the latest version of [Anaconda](https://www.continuum.io/downloads) distribution
* clone the repo: `git clone https://github.com/coells/100days.git`
* open terminal and run Jupyter notebook: `jupyter notebook`
* open [localhost:8888](http://localhost:8888/tree) in your browser
#### notes
* the codebase was developed using `Python 3.6` and `Anaconda 4.3.1`
* notebooks containing [Bokeh](http://bokeh.pydata.org/en/latest/) plots are not directly supported by Github; you better clone the repo a run notebooks locally
#### alternate repository
[Microsoft Azure Notebooks](https://notebooks.azure.com/coells/libraries/100days) with `Python 3.5` support
================================================
FILE: bonus - fast convex hull.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## original algorithm: [day 28 - convex hull](https://github.com/coells/100days/blob/master/day 28 - convex hull.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## refactored algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def extend(points, u, v, hull):\n",
" if not len(points):\n",
" return\n",
"\n",
" # find W as the furthest point from U-V\n",
" w = points[np.argmin(np.cross(points - u, v - u))]\n",
" p = points - w\n",
"\n",
" # extend hull for U-W and V-W\n",
" extend(points[np.cross(p, v - w) < 0], w, v, hull)\n",
" hull.append(w)\n",
" extend(points[np.cross(p, u - w) > 0], u, w, hull)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def convex_hull(points):\n",
" # U is left-most hull point, V is right-most hull point\n",
" u = points[np.argmin(points[:, 0])]\n",
" v = points[np.argmax(points[:, 0])]\n",
" w = np.cross(points - u, v - u)\n",
"\n",
" # recurse on hull construction\n",
" hull = [v]\n",
" extend(points[w < 0], u, v, hull)\n",
" hull.append(u)\n",
" extend(points[w > 0], v, u, hull)\n",
" hull.append(v)\n",
"\n",
" return hull"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## benchmark"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1000 loops, best of 3: 987 µs per loop\n"
]
}
],
"source": [
"%timeit convex_hull(np.random.rand(10**3, 2))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"100 loops, best of 3: 2.35 ms per loop\n"
]
}
],
"source": [
"%timeit convex_hull(np.random.rand(10**4, 2))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"100 loops, best of 3: 13.8 ms per loop\n"
]
}
],
"source": [
"%timeit convex_hull(np.random.rand(10**5, 2))"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10 loops, best of 3: 167 ms per loop\n"
]
}
],
"source": [
"%timeit convex_hull(np.random.rand(10**6, 2))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 loop, best of 3: 1.98 s per loop\n"
]
}
],
"source": [
"%timeit convex_hull(np.random.rand(10**7, 2))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 00 - logo.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"xs, ys, cs = [], [], []\n",
"\n",
"for x in range(0, 30):\n",
" for y in range(0, 30):\n",
" up = np.random.randint(0, 2)\n",
" xs.append([x, x + .8])\n",
" ys.append([y + .8 * (up >= .5), y + .8 * (up < .5)])\n",
"\n",
" col = np.random.randint(0, 4)\n",
" cs.append({0: 'blue', 1: 'red', 2: 'white', 3: 'white'}[col])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## plot"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"
\n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"b0521627-9a43-4f6f-9bf6-1fb6ff634f4a\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"b0521627-9a43-4f6f-9bf6-1fb6ff634f4a\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'b0521627-9a43-4f6f-9bf6-1fb6ff634f4a' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"b0521627-9a43-4f6f-9bf6-1fb6ff634f4a\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"b0521627-9a43-4f6f-9bf6-1fb6ff634f4a\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot = figure(plot_width=800, plot_height=800)\n",
"\n",
"plot.grid.visible = False\n",
"plot.xaxis.visible = False\n",
"plot.yaxis.visible = False\n",
"\n",
"plot.multi_line(xs, ys, color=cs, line_width=6, line_alpha=.1, line_cap='round')\n",
"plot.multi_line(xs, ys, color='black')\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 01 - hanoi tower.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def hanoi(height, left='left', right='right', middle='middle'):\n",
" if height:\n",
" hanoi(height - 1, left, middle, right)\n",
" print(left, '=>', right)\n",
" hanoi(height - 1, middle, right, left)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"left => right\n"
]
}
],
"source": [
"hanoi(1)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"left => middle\n",
"left => right\n",
"middle => right\n"
]
}
],
"source": [
"hanoi(2)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"left => right\n",
"left => middle\n",
"right => middle\n",
"left => right\n",
"middle => left\n",
"middle => right\n",
"left => right\n"
]
}
],
"source": [
"hanoi(3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 02 - matrix chain multiplication.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def mult(chain):\n",
" n = len(chain)\n",
" \n",
" # single matrix chain has zero cost\n",
" aux = {(i, i): (0,) + chain[i] for i in range(n)}\n",
"\n",
" # i: length of subchain\n",
" for i in range(1, n):\n",
" # j: starting index of subchain\n",
" for j in range(0, n - i):\n",
" best = float('inf')\n",
"\n",
" # k: splitting point of subchain\n",
" for k in range(j, j + i):\n",
" # multiply subchains at splitting point\n",
" lcost, lname, lrow, lcol = aux[j, k]\n",
" rcost, rname, rrow, rcol = aux[k + 1, j + i]\n",
" cost = lcost + rcost + lrow * lcol * rcol\n",
" var = '(%s%s)' % (lname, rname)\n",
"\n",
" # pick the best one\n",
" if cost < best:\n",
" best = cost\n",
" aux[j, j + i] = cost, var, lrow, rcol\n",
"\n",
" return dict(zip(['cost', 'order', 'rows', 'cols'], aux[0, n - 1]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'cols': 40, 'cost': 18000, 'order': '((AB)C)', 'rows': 10}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mult([('A', 10, 20), ('B', 20, 30), ('C', 30, 40)])"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'cols': 1, 'cost': 110, 'order': '(A(B(C(DE))))', 'rows': 10}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mult([('A', 10, 5), ('B', 5, 1), ('C', 1, 5), ('D', 5, 10), ('E', 10, 1)])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 03 - next permutation.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def permute(values):\n",
" n = len(values)\n",
" \n",
" # i: position of pivot\n",
" for i in reversed(range(n - 1)):\n",
" if values[i] < values[i + 1]:\n",
" break\n",
" else:\n",
" # very last permutation\n",
" values[:] = reversed(values[:])\n",
" return values\n",
" \n",
" # j: position of the next candidate\n",
" for j in reversed(range(i, n)):\n",
" if values[i] < values[j]:\n",
" # swap pivot and reverse the tail\n",
" values[i], values[j] = values[j], values[i]\n",
" values[i + 1:] = reversed(values[i + 1:])\n",
" break\n",
" \n",
" return values"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2, 3, 4]\n",
"[1, 2, 4, 3]\n",
"[1, 3, 2, 4]\n",
"[1, 3, 4, 2]\n",
"[1, 4, 2, 3]\n",
"[1, 4, 3, 2]\n",
"[2, 1, 3, 4]\n",
"[2, 1, 4, 3]\n",
"[2, 3, 1, 4]\n",
"[2, 3, 4, 1]\n",
"[2, 4, 1, 3]\n",
"[2, 4, 3, 1]\n",
"[3, 1, 2, 4]\n",
"[3, 1, 4, 2]\n",
"[3, 2, 1, 4]\n",
"[3, 2, 4, 1]\n",
"[3, 4, 1, 2]\n",
"[3, 4, 2, 1]\n",
"[4, 1, 2, 3]\n",
"[4, 1, 3, 2]\n",
"[4, 2, 1, 3]\n",
"[4, 2, 3, 1]\n",
"[4, 3, 1, 2]\n",
"[4, 3, 2, 1]\n",
"[1, 2, 3, 4]\n"
]
}
],
"source": [
"x = [4, 3, 2, 1]\n",
"for i in range(25):\n",
" print(permute(x))"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"['F', 'A', 'E', 'D']"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"permute(list('FADE'))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 04 - counting 1-bits.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def count_of_1bits(value):\n",
" n = 0\n",
" while value:\n",
" value &= value - 1\n",
" n += 1\n",
" return n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"count_of_1bits(0)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"count_of_1bits(1)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"count_of_1bits(0b11001100)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 05 - eratosthenes sieve.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def eratosthenes(n):\n",
" n = (n + 1) >> 1\n",
" p = np.ones(n, dtype=np.int8)\n",
" i, j = 1, 3\n",
" \n",
" while i < n:\n",
" if p[i]:\n",
" p[j * j >> 1::j] = 0\n",
" i, j = i + 1, j + 2\n",
"\n",
" return p.sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10 4\n",
"100 25\n",
"1000 168\n",
"10000 1229\n",
"100000 9592\n",
"1000000 78498\n",
"10000000 664579\n"
]
}
],
"source": [
"for j in range(1, 8):\n",
" print(10 ** j, eratosthenes(10 ** j))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 06 - postfix notation.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"ops = {\n",
" '+': float.__add__,\n",
" '-': float.__sub__,\n",
" '*': float.__mul__,\n",
" '/': float.__truediv__,\n",
" '^': float.__pow__,\n",
"}\n",
"\n",
"def postfix(expression):\n",
" stack = []\n",
" \n",
" for x in expression.split():\n",
" if x in ops:\n",
" x = ops[x](stack.pop(-2), stack.pop(-1))\n",
" else:\n",
" x = float(x)\n",
" stack.append(x)\n",
" \n",
" return stack.pop()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"8.0"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"postfix('1 2 + 4 3 - + 10 5 / *')"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"25.0"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"postfix('1 2 * 6 2 / + 9 7 - ^')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"15.0"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"postfix('1 2 3 4 5 + + + +')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 07 - binary addition FSA.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from itertools import zip_longest"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# states\n",
"p0c0 = 0, {}\n",
"p1c0 = 1, {}\n",
"p0c1 = 0, {}\n",
"p1c1 = 1, {}\n",
"\n",
"# transitions between states\n",
"p0c0[1].update({(0, 0): p0c0, (1, 0): p1c0, (0, 1): p1c0, (1, 1): p0c1})\n",
"p1c0[1].update({(0, 0): p0c0, (1, 0): p1c0, (0, 1): p1c0, (1, 1): p0c1})\n",
"p0c1[1].update({(0, 0): p1c0, (1, 0): p0c1, (0, 1): p0c1, (1, 1): p1c1})\n",
"p1c1[1].update({(0, 0): p1c0, (1, 0): p0c1, (0, 1): p0c1, (1, 1): p1c1})\n",
"\n",
"def add(x, y):\n",
" x = map(int, reversed(x))\n",
" y = map(int, reversed(y))\n",
" z = []\n",
"\n",
" # simulate automaton\n",
" value, transition = p0c0\n",
" for r, s in zip_longest(x, y, fillvalue=0):\n",
" value, transition = transition[r, s]\n",
" z.append(value)\n",
"\n",
" # handle carry\n",
" z.append(transition[0, 0][0])\n",
" \n",
" return ''.join(map(str, reversed(z)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'10001000111100'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"add('1100100100100', '100100011000')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'0b10001000111100'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bin(0b1100100100100 + 0b100100011000)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 08 - binary search.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def search(data, item):\n",
" left, right = 0, len(data) - 1\n",
" \n",
" while left <= right:\n",
" middle = (left + right) // 2\n",
" \n",
" if item < data[middle]:\n",
" right = middle - 1\n",
" elif item > data[middle]:\n",
" left = middle + 1\n",
" else:\n",
" return middle\n",
" \n",
" return -1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"data = [2, 3, 4, 8, 22, 23, 24, 25, 26, 28, 31, 39, 40, 43, 45, 49, 54, 58, 59, 60, 72, 73, 76, 87, 95, 97, 98]\n",
"data = sorted(data)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"search(data, 4)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"-1"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"search(data, 74)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"-1"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"search(data, 0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 09 - monte carlo - pi.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def pi(n, batch=1000):\n",
" t = 0\n",
" for i in range(n // batch):\n",
" p = np.random.rand(batch, 2)\n",
" p = (p * p).sum(axis=1)\n",
" t += (p <= 1).sum()\n",
" return 4 * t / n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"3.1400000000000001"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pi(10 ** 3)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"3.1418360000000001"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pi(10 ** 6)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"3.14145728"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pi(10 ** 8)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 10 - karatsuba multiplication.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from itertools import zip_longest"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def add(x, y):\n",
" z, carry = [], 0\n",
"\n",
" for r, s in zip_longest(x, y, fillvalue=0):\n",
" t = r + s + carry\n",
" carry = t // 10\n",
" z.append(t % 10)\n",
" if carry:\n",
" z.append(carry)\n",
"\n",
" return z"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def sub(x, y):\n",
" z, carry = [], 0\n",
"\n",
" for r, s in zip_longest(x, y, fillvalue=0):\n",
" t = r - s + carry\n",
" carry = t // 10\n",
" z.append(t % 10)\n",
"\n",
" return z"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def karatsuba(x, y):\n",
" # ensure same length\n",
" while len(x) < len(y):\n",
" x.append(0)\n",
" while len(x) > len(y):\n",
" y.append(0)\n",
"\n",
" # length and split\n",
" n = len(x)\n",
" n_2 = (n + 1) >> 1\n",
"\n",
" # trivial case\n",
" if n == 1:\n",
" return add([x[0] * y[0]], [])\n",
"\n",
" # split\n",
" x0, x1 = x[:n_2], x[n_2:]\n",
" y0, y1 = y[:n_2], y[n_2:]\n",
"\n",
" # karatsuba algorithm\n",
" z0 = karatsuba(x0, y0)\n",
" z1 = karatsuba(x1, y1)\n",
" z2 = karatsuba(add(x0, x1), add(y0, y1))\n",
" z2 = sub(sub(z2, z0), z1)\n",
"\n",
" z = add(z0, [0] * (n_2 << 1) + z1)\n",
" z = add(z, [0] * n_2 + z2)\n",
"\n",
" return z"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def mult(x, y):\n",
" print(x, '*', y, '=', int(x) * int(y), end=' = ')\n",
"\n",
" x = list(map(int, reversed(x)))\n",
" y = list(map(int, reversed(y)))\n",
" z = karatsuba(x, y)\n",
"\n",
" print(''.join(map(str, reversed(z))))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1234 * 4321 = 5332114 = 5332114\n"
]
}
],
"source": [
"mult('1234', '4321')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5678 * 8765 = 49767670 = 49767670\n"
]
}
],
"source": [
"mult('5678', '8765')"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"9999 * 9999 = 99980001 = 99980001\n"
]
}
],
"source": [
"mult('9999', '9999')"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"60504 * 36056 = 2181532224 = 2181532224\n",
"7644 * 2034 = 15547896 = 15547896\n",
"2 * 1 = 2 = 2\n",
"939 * 700 = 657300 = 657300\n",
"977707258 * 389036934 = 380364234001866972 = 380364234001866972\n",
"1079459668 * 7762768164 = 8379595145072409552 = 08379595145072409552\n",
"4609807 * 2350979 = 10837559451053 = 10837559451053\n",
"36740 * 97490 = 3581782600 = 3581782600\n",
"29 * 19 = 551 = 0551\n",
"913789 * 733694 = 670441506566 = 670441506566\n",
"476646777 * 451303071 = 215112154242352167 = 215112154242352167\n",
"23369 * 43069 = 1006479461 = 1006479461\n",
"9185982 * 3983922 = 36596235781404 = 036596235781404\n",
"7806584211 * 8415537629 = 65696603181627775719 = 65696603181627775719\n",
"317 * 106 = 33602 = 33602\n",
"507729648 * 898501571 = 456195886371277008 = 456195886371277008\n",
"780843 * 778950 = 608237654850 = 608237654850\n",
"61 * 79 = 4819 = 4819\n",
"310094 * 443993 = 137679565342 = 137679565342\n",
"1564 * 7634 = 11939576 = 11939576\n",
"746602083 * 909270015 = 678862887208441245 = 678862887208441245\n",
"2 * 6 = 12 = 12\n",
"1798067708 * 3523547357 = 6335576720230447756 = 06335576720230447756\n",
"7 * 0 = 0 = 0\n",
"6290 * 0797 = 5013130 = 5013130\n",
"9958199 * 6994130 = 69648938371870 = 069648938371870\n",
"1661 * 0701 = 1164361 = 1164361\n",
"022046 * 410144 = 9042034624 = 09042034624\n",
"8505673479 * 1870256036 = 15907787164344869244 = 15907787164344869244\n",
"5 * 2 = 10 = 10\n"
]
}
],
"source": [
"for _ in range(30):\n",
" n = np.random.randint(1, 11)\n",
" x = ''.join(map(str, np.random.randint(0, 10, n)))\n",
" y = ''.join(map(str, np.random.randint(0, 10, n)))\n",
" mult(x, y)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 11 - McCarthy 91.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def mccarthy91(n):\n",
" k = 1\n",
" while k:\n",
" if n > 100:\n",
" n -= 10\n",
" k -= 1\n",
" else:\n",
" n += 11\n",
" k += 1\n",
" return n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"don't forget to increase stack limit for recursive version"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def mccarthy91_rec(n):\n",
" if n > 100:\n",
" return n - 10\n",
" else:\n",
" return mccarthy91_rec(mccarthy91_rec(n + 11))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"70 91\n",
"71 91\n",
"80 91\n",
"81 91\n",
"90 91\n",
"91 91\n",
"100 91\n",
"101 91\n",
"110 100\n",
"111 101\n",
"120 110\n",
"121 111\n"
]
}
],
"source": [
"for i in range(70, 130, 10):\n",
" print(i, mccarthy91(i))\n",
" print(i + 1, mccarthy91(i + 1)) "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 12 - roots of polynomial.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def roots(*coeffs):\n",
" matrix = np.eye(len(coeffs) - 1, k=-1)\n",
" matrix[:,-1] = np.array(coeffs[:0:-1]) / -coeffs[0]\n",
" return np.linalg.eigvals(matrix)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0.1])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 10x - 1 = 0\n",
"roots(10, -1)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([ 1., 1.])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# x^2 - 2x + 1 = 0\n",
"roots(1, -2, 1)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([ 3., -3.])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# 2x^2 - 18 = 0\n",
"roots(2, 0, -18)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0.5+0.8660254j, 0.5-0.8660254j, -1.0+0.j , -0.5+0.8660254j,\n",
" -0.5-0.8660254j])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# x^5 + x^4 + x^3 + x^2 + x + 1 = 0\n",
"roots(1, 1, 1, 1, 1, 1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 13 - extended euclidean algorithm.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def gcd(x, y):\n",
" u0, v0 = 1, 0\n",
" u1, v1 = 0, 1\n",
" while y:\n",
" q = x // y\n",
" u0, u1 = u1, u0 - q * u1\n",
" v0, v1 = v1, v0 - q * v1\n",
" x, y = y, x % y\n",
" return x, u0, v0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(1, 3, -2)"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gcd(5, 7)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(18, -9, 40)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gcd(2*3*7*9*11, 6*12*13)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(6, -1351389, 189739)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gcd(32423940, 230934894)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(50, 1, -1)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gcd(150, 100)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(1, -49, 74)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gcd(151, 100)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 14 - huffman codes.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from collections import Counter"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def find_min(freq):\n",
" item = min(freq, key=lambda i: i[0])\n",
" freq.remove(item)\n",
" return item"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def huffman_codes(text):\n",
" freq = [(i, x) for x, i in Counter(text).items()]\n",
"\n",
" while len(freq) > 1:\n",
" li, lx = find_min(freq)\n",
" ri, rx = find_min(freq)\n",
" freq.append((li + ri, (lx, rx)))\n",
"\n",
" print_codes(freq.pop()[1])"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def print_codes(tree, prefix=''):\n",
" if isinstance(tree, tuple):\n",
" print_codes(tree[0], prefix + '0')\n",
" print_codes(tree[1], prefix + '1')\n",
" else:\n",
" print(tree, prefix)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a 0\n",
"b 10\n",
"c 11\n"
]
}
],
"source": [
"huffman_codes('abca')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"i 000\n",
" 001\n",
"t 01\n",
"l 1000\n",
"v 1001\n",
"s 101\n",
"a 11\n"
]
}
],
"source": [
"huffman_codes('astala vista tasta')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 15 - breaking OTP.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## ciphertext"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"ciphertext = [\n",
" b'm\\x99QH\\xfc\\x99\\xcel\\xfc>\\x11\\xf81\\xda:\\x15\"6\\xd3b\\x07\\x7f\\xed\\x87\\xd5\\xd4\\xf0\\xbb',\n",
" b'x\\x96^\\r\\xb5\\x83\\x86u\\xeel\\x0e\\xf8,\\xce:\\x06 6\\xd0b\\nx\\xfd\\x87\\xd9\\xc9\\xe8',\n",
" b'm\\x90O^\\xfc\\x80\\xd3f\\xe7>\\x16\\xf46\\x89w\\x05r8\\xcb-\\x04',\n",
" b'`\\x97O\\r\\xbd\\x9f\\xc3%\\xe1q\\x0e\\xb15\\xdbu\\x0e5y\\xca*\\x1c7\\xec\\xc2\\xd2\\xcb',\n",
" b\"m\\x90[Y\\xfc\\x80\\xdf%\\xeb\\x7f\\x03\\xe2b\\xc1{\\x167y\\xdf'\\x16y\\xa8\\xc6\\x97\\xc2\\xed\\xa9p(\",\n",
" b'`\\x9dN\\r\\xb5\\x8b\\x86m\\xe0n\\x1f\\xb1*\\xc8i@45\\xd25\\x1d7\\xe9\\xd0\\xd6\\xdf',\n",
" b'p\\x96\\x1aL\\xfc\\x83\\xcfb\\xe7jZ\\xfe0\\x89s\\x0er8\\x9d&\\x12n',\n",
" b'p\\x96\\x1aL\\xfc\\x9b\\xcfv\\xe6q\\x14\\xb1-\\xdb:\\t\\x0e\\xfe0\\xc4\\x7f\\x0e&<\\xd9b\\x00\\x7f\\xe7\\xd5\\xd2',\n",
" b'x\\x96^\\r\\x95\\xcd\\xcej\\xe3zZ\\xe6+\\xddr\\t\\x0e\\xf9'\\x89}\\x0f>=\\xd8,Sd\\xe9\\xc9\\xd3\",\n",
" b'q\\x97M\\r\\xba\\x88\\xd1%\\xf6{\\x0e\\xb1*\\xc6m@&1\\xd8;St\\xfa\\xc2\\xd2\\xd6',\n",
" b'm\\x90HB\\xa9\\x8a\\xce%\\xe2gZ\\xf7+\\xc7}\\x05 *\\x9d6\\x1c7\\xfc\\xcf\\xd2\\x86\\xfb\\xa9t5',\n",
" b'n\\x90SA\\xb9\\xcd\\xcf%\\xf8{\\x1f\\xe1b\\xder\\t><\\x9d\\x0bS`\\xed\\xc2\\xc7',\n",
" b'9\\xd8_I\\xbb\\x8c\\xd4%\\xeer\\x16\\xf0,\\x89j\\x0f7y\\x9dbS7\\xa8\\x87\\x97\\x86\\xbf',\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from string import ascii_lowercase\n",
"\n",
"# set of allowed characters\n",
"letters = set(ascii_lowercase + ' ')\n",
"\n",
"# reconstructed messages\n",
"plaintext = [''] * len(ciphertext)\n",
"\n",
"# take all the codes at the same position\n",
"for messages in zip(*ciphertext):\n",
" keys = set()\n",
" \n",
" # find viable keys\n",
" for key in range(256):\n",
" for m in messages:\n",
" if chr(m ^ key) not in letters:\n",
" break\n",
" else:\n",
" keys.add(key)\n",
"\n",
" key = keys.pop() if len(keys) == 1 else None\n",
"\n",
" # reconstruct plaintext\n",
" for i, m in enumerate(messages):\n",
" if key is not None:\n",
" plaintext[i] += chr(m ^ key)\n",
" else:\n",
" plaintext[i] += '?'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## result"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"['take?this kiss upon?t',\n",
" 'and ?n parting from?y',\n",
" 'thus?much let me av?w',\n",
" 'you ?re not wrong w?o',\n",
" 'that?my days have b?e',\n",
" 'yet ?f hope has flo?n',\n",
" 'in a?night or in a ?a',\n",
" 'in a?vision or in n?n',\n",
" 'is i? therefore the?l',\n",
" 'all ?hat we see or ?e',\n",
" 'is b?t a dream with?n',\n",
" 'i st?nd amid the ro?r',\n",
" 'of a?surf tormented?s',\n",
" 'and ? hold within m? ',\n",
" 'grai?s of the golde? ',\n",
" 'how ?ew yet how the? ',\n",
" 'thro?gh my fingers ?o',\n",
" 'whil? i weep while ? ',\n",
" ' ed?ar allan poe ? ']"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"plaintext"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 16 - no-condition swap.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"this is C code"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"```C\n",
"void swap(int *x, int *y) {\n",
" int u = *x, v = *y;\n",
" int s = (u - v) >> (sizeof(int) * 8 - 1);\n",
" *x = v * (1 + s) - u * s;\n",
" *y = u * (1 + s) - v * s;\n",
"}\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"this is Python equivalent"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def swap(x, y):\n",
" s = x < y\n",
" return x * s + y * (1 - s), y * s + x * (1 - s)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"((3, 15), (3, 15))"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"swap(3, 15), swap(15, 3)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"((-13, 5), (-13, 5))"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"swap(-13, 5), swap(5, -13)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 17 - perceptron.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## data"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"X = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 1], [-1, 1, 1], [1, -1, 1]])\n",
"Y = np.array([1, 1, 1, 0, 0])\n",
"W = np.zeros(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def perceptron(x, w):\n",
" return (x @ w >= 0).astype(int)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def train(x, y, w):\n",
" for i in range(len(x)):\n",
" # evaluate perceptron\n",
" h = perceptron(x[i, :], w)\n",
" \n",
" # misclassification\n",
" if h != y[i]:\n",
" # positive sample\n",
" if y[i] == 1: \n",
" w += x[i, :]\n",
" # negative sample\n",
" else: \n",
" w -= x[i, :]\n",
" \n",
" # evaluate\n",
" return perceptron(x, w)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## training"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"y= [1 1 1 0 0]\n",
"w= [ 0. 0. -2.] acc= 0.4\n",
"w= [ 1. 1. -2.] acc= 0.6\n",
"w= [ 2. 1. -2.] acc= 0.8\n",
"w= [ 2. 2. -1.] acc= 1.0\n",
"w= [ 2. 2. -1.] acc= 1.0\n"
]
}
],
"source": [
"print('y=', Y)\n",
"for _ in range(5):\n",
" h = train(X, Y, W)\n",
" print('w=', W, 'acc=', np.mean(h == Y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## plot"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"2160c5cf-de49-4299-a4aa-5510f6f6883a\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"2160c5cf-de49-4299-a4aa-5510f6f6883a\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '2160c5cf-de49-4299-a4aa-5510f6f6883a' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"2160c5cf-de49-4299-a4aa-5510f6f6883a\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"2160c5cf-de49-4299-a4aa-5510f6f6883a\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"color = list(map({0: 'red', 1: 'green'}.__getitem__, Y))\n",
"x0, y0 = -1.5, (-1.5 * -W[0] - W[2]) / W[1]\n",
"x1, y1 = 1.5, (1.5 * -W[0] - W[2]) / W[1]\n",
"\n",
"plot = figure()\n",
"plot.circle(x=X[:, 0], y=X[:, 1], color=color, size=10)\n",
"plot.line(x=[x0, x1], y=[y0, y1])\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 18 - monopoly.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def probability(n):\n",
" # initial probabilities\n",
" p = [0, 0, 0, 0, 0, 1]\n",
" \n",
" # next field is conditioned on previous six fields\n",
" for _ in range(n):\n",
" p.append(sum(p[-6:]) / 6)\n",
"\n",
" return p[6:]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fields = probability(24)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"3655099f-822b-4a5f-904f-bfd9ca0ea296\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"3655099f-822b-4a5f-904f-bfd9ca0ea296\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '3655099f-822b-4a5f-904f-bfd9ca0ea296' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"3655099f-822b-4a5f-904f-bfd9ca0ea296\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"3655099f-822b-4a5f-904f-bfd9ca0ea296\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"plot = figure(y_range=(0, .5))\n",
"plot.scatter(x=range(1, 25), y=fields)\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 19 - counting inversions.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def inversions(items):\n",
" n = len(items)\n",
" if n <= 1:\n",
" return items, 0\n",
"\n",
" # number of inversions in partitions\n",
" left, linv = inversions(items[:n // 2])\n",
" right, rinv = inversions(items[n // 2:])\n",
"\n",
" inv = linv + rinv\n",
" llen, rlen = len(left), len(right)\n",
" i, j, aux = 0, 0, []\n",
"\n",
" # merge and count inversions\n",
" for k in range(n):\n",
" if i < llen and j < rlen and left[i] > right[j]:\n",
" inv += llen - i\n",
" aux.append(right[j])\n",
" j += 1\n",
" elif i < llen:\n",
" aux.append(left[i])\n",
" i += 1\n",
" else:\n",
" aux.append(right[j])\n",
" j += 1\n",
" \n",
" return aux, inv"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[29, 25, 17, 15, 6, 8, 11, 15, 7, 5]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"items = list(np.random.randint(0, 30, 10))\n",
"items"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"([5, 6, 7, 8, 11, 15, 15, 17, 25, 29], 37)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"inversions(items)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 20 - linearithmic multiplication.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def mult(x, y):\n",
" nx, ny = len(x), len(y)\n",
"\n",
" # auxiliary x\n",
" fx = np.zeros(nx + ny, dtype=np.float64)\n",
" fx[:nx] = list(map(int, reversed(x)))\n",
"\n",
" # auxiliary y\n",
" fy = np.zeros(nx + ny, np.float64)\n",
" fy[:ny] += list(map(int, reversed(y)))\n",
"\n",
" # convolution via FFT\n",
" fx = np.fft.fft(fx)\n",
" fy = np.fft.fft(fy)\n",
" z = np.fft.ifft(fx * fy).real.round().astype(int)\n",
"\n",
" # carry over\n",
" for i in range(nx + ny - 1):\n",
" z[i + 1] += z[i] // 10\n",
" z[i] %= 10\n",
"\n",
" return ''.join(map(str, reversed(z)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2092214183 * 2448001885 = 05121744263807734955\n",
"7122461902 * 9593715983 = 68330876587525979666\n",
"8617908461 * 3158102416 = 27216237531550941776\n",
"3655867966 * 9064788350 = 33139669347334996100\n",
"6622180692 * 4377254226 = 28986968419392604392\n",
"6147274168 * 3384584963 = 20805971712451135784\n",
"4714353304 * 5151447888 = 24285745371176621952\n",
"9370611380 * 145283374 = 1361394037729196120\n",
"2465911620 * 2801092645 = 06907246902002034900\n",
"7836421288 * 8791219244 = 68891697631156866272\n"
]
}
],
"source": [
"for _ in range(10):\n",
" x, y = np.random.randint(1e+3, 1e+10, 2)\n",
" print(x, '*', y, '=', mult(str(x), str(y)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 21 - k-means.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, gridplot, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def kmeans(points, n_clusters):\n",
" # sample initial centroids\n",
" sample = np.random.choice(len(points), n_clusters, replace=False)\n",
" centroid = points[sample]\n",
" \n",
" loss = [-1, -2]\n",
" while not np.allclose(*loss):\n",
" # compute distance for each pair: point/centroid\n",
" distance = [np.sqrt(((points - c) ** 2).sum(1)) for c in centroid]\n",
" # new loss\n",
" loss = loss[1:] + [np.sum(distance)]\n",
" # assign new clusters\n",
" cluster = np.argmin(distance, axis=0)\n",
" # update centroids by new cluster means\n",
" for i in range(n_clusters):\n",
" centroid[i] = np.mean(points[cluster == i], axis=0)\n",
" \n",
" return cluster"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"generate clusters"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"n = 100\n",
"A = np.random.multivariate_normal([2, 0], [[1, .1], [-4, 1]], n)\n",
"B = np.random.multivariate_normal([-2, 0], [[1, -4], [.1, 1]], n)\n",
"C = np.random.multivariate_normal([2, -2], [[1, 4], [-.1, 1]], n)\n",
"D = ['red', 'green', 'blue']\n",
"\n",
"points = np.r_[A, B, C]\n",
"original_color = np.repeat(D[:3], n)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"detect k-means clusters"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"cluster = kmeans(points, 3)\n",
"new_color = [D[i] for i in cluster]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"plot original and new clusters"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"16889866-a9f1-4cfe-af87-3320ebc9b66a\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"16889866-a9f1-4cfe-af87-3320ebc9b66a\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '16889866-a9f1-4cfe-af87-3320ebc9b66a' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"16889866-a9f1-4cfe-af87-3320ebc9b66a\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"16889866-a9f1-4cfe-af87-3320ebc9b66a\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"plot1 = figure(title='original clusters', plot_height=300)\n",
"plot1.scatter(x=points[:, 0], y=points[:, 1], color=original_color)\n",
"\n",
"plot2 = figure(title='k-means clusters', plot_height=300)\n",
"plot2.scatter(x=points[:, 0], y=points[:, 1], color=new_color)\n",
"\n",
"show(gridplot([[plot1], [plot2]]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 22 - determinant.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def determinant(x):\n",
" if x.size == 1:\n",
" return x[0, 0]\n",
" \n",
" # pivot\n",
" i = np.abs(x[:, 0]).argmax()\n",
" pivot = x[i, 0]\n",
" if np.abs(pivot) < 1e-15:\n",
" return 0\n",
" \n",
" # gauss elimination\n",
" n = len(x)\n",
" y = x - x[:, 0].reshape(n, 1) @ (x[i, :] / x[i, 0]).reshape(1, n)\n",
" y = y[np.arange(n) != i, 1:]\n",
"\n",
" # recursion\n",
" return pivot * (-1) ** (i % 2) * determinant(y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0.86407152, -0.48119607, -0.60195809, -0.15813597, -0.25164858],\n",
" [-0.45656693, -0.86405085, -0.26996402, 0.00863821, -0.42482759],\n",
" [ 0.62699481, -0.15693623, 0.88928594, -0.59483779, 0.45047394],\n",
" [ 0.27561899, 0.08707643, -0.63000059, 0.19882408, 0.17816101],\n",
" [-0.36012304, -0.47399834, 0.17859948, 0.23234741, 0.65332936]])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = np.random.rand(5, 5) * 2 - 1\n",
"X"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0.14322328293127826"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"determinant(X)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 23 - sudoku.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def sudoku(matrix, n=0):\n",
" if n >= 81:\n",
" return matrix\n",
" if matrix.A1[n]:\n",
" return sudoku(matrix, n + 1)\n",
"\n",
" i, j, k, l = n // 9, n % 9, n // 27 * 3, (n % 9) // 3 * 3\n",
"\n",
" # get viable values\n",
" x = set(range(1, 10)) - (\n",
" set(matrix[i].A1) |\n",
" set(matrix.T[j].A1) |\n",
" set(matrix[k:k + 3, l:l + 3].A1)\n",
" )\n",
"\n",
" # backtracking\n",
" for value in x:\n",
" matrix[i, j] = value\n",
" if sudoku(matrix, n + 1) is not None:\n",
" return matrix\n",
" else:\n",
" matrix[i, j] = 0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"matrix([[8, 2, 4, 1, 3, 9, 5, 7, 6],\n",
" [6, 9, 1, 5, 2, 7, 8, 3, 4],\n",
" [5, 7, 3, 6, 4, 8, 2, 1, 9],\n",
" [4, 6, 5, 3, 8, 1, 7, 9, 2],\n",
" [9, 1, 7, 2, 6, 5, 3, 4, 8],\n",
" [2, 3, 8, 9, 7, 4, 1, 6, 5],\n",
" [3, 8, 2, 7, 9, 6, 4, 5, 1],\n",
" [1, 4, 6, 8, 5, 3, 9, 2, 7],\n",
" [7, 5, 9, 4, 1, 2, 6, 8, 3]])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sudoku(np.matrix(\"\"\"\n",
" 8 0 0 1 0 9 0 7 0;\n",
" 0 9 0 0 0 0 8 0 0;\n",
" 5 0 3 0 4 0 0 0 0;\n",
" 0 0 0 0 0 0 7 9 0;\n",
" 0 0 7 2 6 5 3 0 0;\n",
" 0 3 8 0 0 0 0 0 0;\n",
" 0 0 0 0 9 0 4 0 1;\n",
" 0 0 6 0 0 0 0 2 0;\n",
" 0 5 0 4 0 2 0 0 3\n",
"\"\"\"))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"matrix([[1, 2, 3, 4, 5, 8, 9, 6, 7],\n",
" [4, 5, 8, 6, 7, 9, 1, 2, 3],\n",
" [9, 6, 7, 1, 2, 3, 8, 4, 5],\n",
" [2, 1, 9, 8, 3, 4, 5, 7, 6],\n",
" [3, 8, 4, 5, 6, 7, 2, 1, 9],\n",
" [5, 7, 6, 9, 1, 2, 3, 8, 4],\n",
" [8, 9, 1, 3, 4, 6, 7, 5, 2],\n",
" [6, 3, 2, 7, 8, 5, 4, 9, 1],\n",
" [7, 4, 5, 2, 9, 1, 6, 3, 8]])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sudoku(np.matrix(np.zeros((9, 9), dtype=int)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 24 - closest pair.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"l1x = lambda a, b: abs(a[0] - b[0])\n",
"l1y = lambda a, b: abs(a[1] - b[1])\n",
"l2 = lambda a, b: np.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def merge(points_y, l, m, r):\n",
" i, j, aux = l, m, []\n",
" while i < m or j < r:\n",
" if i < m and j < r and points_y[i][1] > points_y[j][1]:\n",
" aux.append(points_y[j])\n",
" j += 1\n",
" elif i < m:\n",
" aux.append(points_y[i])\n",
" i += 1\n",
" else:\n",
" aux.append(points_y[j])\n",
" j += 1\n",
" points_y[l:r] = aux"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def search(points_x, points_y, l, r):\n",
" if r - l < 2:\n",
" return np.inf\n",
"\n",
" m = (l + r) // 2\n",
"\n",
" # search inside partitions\n",
" delta1 = search(points_x, points_y, l, m)\n",
" delta2 = search(points_x, points_y, m, r)\n",
" delta = min(delta1, delta2)\n",
"\n",
" # sort points by y\n",
" merge(points_y, l, m, r)\n",
"\n",
" # find the middle band in delta of x\n",
" q = points[m]\n",
" band = [p for p in points_y[l:r] if l1x(p, q) < delta]\n",
"\n",
" # search the middle band in delta of y\n",
" for i in range(len(band)):\n",
" p1 = band[i]\n",
" for j in range(i + 1, len(band)):\n",
" p2 = band[j]\n",
" if l1y(p1, p2) < delta:\n",
" delta = min(delta, l2(p1, p2))\n",
" else:\n",
" break\n",
"\n",
" # min distance\n",
" return delta"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def closest_pair(points):\n",
" points = sorted(points)\n",
" return search(points, points[:], 0, len(points))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[(0.098408754012625277, 0.74280296333228502),\n",
" (0.077086466963399936, 0.29657822643731369),\n",
" (0.56160732190468798, 0.54675608365280715),\n",
" (0.82076413876723719, 0.3014490061746683),\n",
" (0.42758995432097113, 0.19199854966752694)]"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"points = [tuple(i) for i in np.random.rand(100, 2)]\n",
"points[:5]"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0.012360394883333799"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"closest_pair(points)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 25 - conjugate gradients.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def conjugate_gradients(A, b):\n",
" x = np.zeros(A.shape[1])\n",
" residuals = b - A @ x\n",
" direction = residuals\n",
" error = residuals.T @ residuals\n",
"\n",
" # step along conjugate directions\n",
" while error > 1e-8:\n",
" x += direction * error / (direction.T @ A @ direction)\n",
" residuals = b - A @ x\n",
" error1 = error\n",
" error = residuals.T @ residuals\n",
" direction = residuals + error / error1 * direction\n",
"\n",
" return x"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"A\n",
"[[ 0.72202644 0.70073834 0.09483571]\n",
" [ 0.97657699 0.41447392 0.96942563]\n",
" [ 0.35596098 0.91433461 0.52105508]\n",
" [ 0.66857021 0.48664146 0.46385892]\n",
" [ 0.65044125 0.98123656 0.55633599]]\n",
"b\n",
"[ 0.97013697 0.76971217 0.92539487 0.10820739 0.98406079]\n",
"x\n",
"[ 0.26730959 0.87199275 -0.04536451]\n"
]
}
],
"source": [
"A = np.random.rand(5, 3)\n",
"b = np.random.rand(5)\n",
"\n",
"print('A')\n",
"print(A)\n",
"print('b')\n",
"print(b)\n",
"print('x')\n",
"\n",
"# make system positive semidefinite\n",
"print(conjugate_gradients(A.T @ A, A.T @ b))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 26 - karger's mincut.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from random import choice\n",
"from itertools import combinations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def contract(graph, u, v):\n",
" aux, w = [], f'{u},{v}'\n",
" for x, y in graph:\n",
" x = w if x in [u, v] else x\n",
" y = w if y in [u, v] else y\n",
" if x < y:\n",
" aux.append((x, y))\n",
" elif x > y:\n",
" aux.append((y, x))\n",
" return aux"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def mincut(graph, n):\n",
" components, cost = ['', ''], float('inf')\n",
" \n",
" # n^2 attempts\n",
" for i in range(n * n):\n",
" aux = graph\n",
" \n",
" # remove edges one by one\n",
" while len(set(aux)) > 1:\n",
" aux = contract(aux, *choice(aux))\n",
" \n",
" # min cut so far\n",
" if len(aux) < cost:\n",
" components, cost = aux[0], len(aux)\n",
" \n",
" return components, cost"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## generate graph"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# fully connected\n",
"nodes_a = [f'A{i}' for i in range(20)]\n",
"graph_a = [(u, v) for u, v in combinations(nodes_a, 2)]\n",
"\n",
"# fully connected\n",
"nodes_b = [f'B{i}' for i in range(20)]\n",
"graph_b = [(u, v) for u, v in combinations(nodes_b, 2)]\n",
"\n",
"# interconnections\n",
"graph_c = [(choice(nodes_a), choice(nodes_b)) for i in range(10)]\n",
"\n",
"graph = graph_a + graph_b + graph_c"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"best cut: 10\n",
"component #1: A0,A3,A4,A7,A9,A16,A19,A2,A1,A5,A12,A18,A17,A8,A15,A13,A6,A10,A11,A14\n",
"component #2: B0,B1,B16,B12,B15,B4,B17,B19,B9,B3,B5,B14,B18,B2,B11,B8,B10,B7,B6,B13\n"
]
}
],
"source": [
"components, cost = mincut(graph, 40)\n",
"print('best cut:', cost)\n",
"print('component #1:', components[0])\n",
"print('component #2:', components[1])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 27 - spiral matrix.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from itertools import count"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def spiral(n):\n",
" matrix = [range(i * n + n)[-n:] for i in range(n)]\n",
" X, C, R = {}, count(1), matrix\n",
"\n",
" while R:\n",
" X.update(zip(R[0], C))\n",
" R = list(zip(*[i[::-1] for i in R[1:]]))\n",
"\n",
" return [[X[j] for j in i] for i in matrix]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\t2\t3\n",
"8\t9\t4\n",
"7\t6\t5\n"
]
}
],
"source": [
"for i in spiral(3):\n",
" print('\\t'.join(map(str, i)))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\t2\t3\t4\t5\n",
"16\t17\t18\t19\t6\n",
"15\t24\t25\t20\t7\n",
"14\t23\t22\t21\t8\n",
"13\t12\t11\t10\t9\n"
]
}
],
"source": [
"for i in spiral(5):\n",
" print('\\t'.join(map(str, i)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 28 - convex hull.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, output_notebook, show"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def split(u, v, points):\n",
" # return points on left side of UV\n",
" return [p for p in points if np.cross(p - u, v - u) < 0]\n",
"\n",
"def extend(u, v, points):\n",
" if not points:\n",
" return []\n",
"\n",
" # find furthest point W, and split search to WV, UW\n",
" w = min(points, key=lambda p: np.cross(p - u, v - u))\n",
" p1, p2 = split(w, v, points), split(u, w, points)\n",
" return extend(w, v, p1) + [w] + extend(u, w, p2)\n",
"\n",
"def convex_hull(points):\n",
" # find two hull points, U, V, and split to left and right search\n",
" u = min(points, key=lambda p: p[0])\n",
" v = max(points, key=lambda p: p[0])\n",
" left, right = split(u, v, points), split(v, u, points)\n",
" \n",
" # find convex hull on each side\n",
" return [v] + extend(u, v, left) + [u] + extend(v, u, right) + [v]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 9.95456010e-01, 7.65907653e-01],\n",
" [ 9.64101937e-01, 9.47636262e-01],\n",
" [ 7.49840951e-01, 9.68180575e-01],\n",
" [ 2.13580026e-01, 9.91177434e-01],\n",
" [ 1.18181500e-01, 9.56758533e-01],\n",
" [ 3.77237324e-02, 8.81109123e-01],\n",
" [ 2.75085295e-02, 2.27043559e-01],\n",
" [ 5.72172004e-02, 2.45227737e-02],\n",
" [ 4.40115492e-01, 5.46663495e-06],\n",
" [ 7.37937895e-01, 6.71680586e-02],\n",
" [ 8.47805083e-01, 9.26355385e-02],\n",
" [ 8.97086651e-01, 1.21673019e-01],\n",
" [ 9.57764617e-01, 1.98416655e-01],\n",
" [ 9.95456010e-01, 7.65907653e-01]])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"points = np.random.rand(100, 2)\n",
"hull = np.array(convex_hull(points))\n",
"hull"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"fbaf469f-9b57-4056-8a23-b41b401a6262\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"fbaf469f-9b57-4056-8a23-b41b401a6262\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'fbaf469f-9b57-4056-8a23-b41b401a6262' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"fbaf469f-9b57-4056-8a23-b41b401a6262\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"fbaf469f-9b57-4056-8a23-b41b401a6262\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"plot = figure()\n",
"plot.scatter(x=points[:, 0], y=points[:, 1])\n",
"plot.line(x=hull[:, 0], y=hull[:, 1], color='red')\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 29 - string searching.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def search(text, pattern):\n",
" i, k = 0, len(pattern)\n",
" table = {c: k - i for i, c in enumerate(pattern)}\n",
"\n",
" while True:\n",
" print(f'search @ {i}')\n",
" if text[i:i + k] == pattern:\n",
" print(f'FOUND @ {i}')\n",
" if i + k < len(text):\n",
" i += table.get(text[i + k], k + 1)\n",
" else:\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"73"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"text = 'A parabolic (or paraboloid or paraboloidal) reflector (or dish or mirror)'\n",
"len(text)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"search @ 0\n",
"search @ 10\n",
"search @ 20\n",
"search @ 30\n",
"search @ 40\n",
"search @ 44\n",
"FOUND @ 44\n",
"search @ 54\n",
"search @ 56\n",
"search @ 66\n"
]
}
],
"source": [
"search(text, 'reflector')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"search @ 0\n",
"search @ 6\n",
"search @ 10\n",
"search @ 11\n",
"search @ 17\n",
"search @ 33\n",
"search @ 40\n",
"search @ 44\n",
"search @ 60\n"
]
}
],
"source": [
"search(text, 'not to be found')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 30 - strassen multiplication.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def strassen(A, B):\n",
" k = A.shape[0] // 2\n",
" if k == 0:\n",
" return A * B\n",
"\n",
" A11, A12 = A[:k, :k], A[:k, k:]\n",
" A21, A22 = A[k:, :k], A[k:, k:]\n",
" B11, B12 = B[:k, :k], B[:k, k:]\n",
" B21, B22 = B[k:, :k], B[k:, k:]\n",
" \n",
" T1 = strassen(A11 + A22, B11 + B22)\n",
" T2 = strassen(A21 + A22, B11)\n",
" T3 = strassen(A11, B12 - B22)\n",
" T4 = strassen(A22, B21 - B11)\n",
" T5 = strassen(A11 + A12, B22)\n",
" T6 = strassen(A21 - A11, B11 + B12)\n",
" T7 = strassen(A12 - A22, B21 + B22)\n",
" \n",
" C = np.zeros(A.shape, dtype=A.dtype)\n",
" C[:k, :k] = T1 + T4 - T5 + T7\n",
" C[:k, k:] = T3 + T5\n",
" C[k:, :k] = T2 + T4\n",
" C[k:, k:] = T1 - T2 + T3 + T6\n",
" \n",
" return C"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[9, 1, 1, 2, 5, 2, 8, 3],\n",
" [4, 7, 1, 9, 6, 6, 9, 5],\n",
" [0, 1, 0, 1, 7, 6, 0, 8],\n",
" [9, 0, 4, 3, 3, 4, 4, 5],\n",
" [6, 9, 4, 7, 1, 5, 5, 0],\n",
" [3, 5, 8, 1, 6, 2, 3, 8],\n",
" [3, 1, 0, 4, 2, 5, 1, 7],\n",
" [2, 6, 4, 6, 3, 4, 6, 3]])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = np.random.randint(0, 10, (8, 8))\n",
"X"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[8, 1, 1, 7, 6, 2, 7, 3],\n",
" [2, 3, 6, 5, 8, 2, 0, 0],\n",
" [5, 5, 1, 6, 3, 4, 4, 9],\n",
" [4, 0, 6, 0, 4, 0, 7, 8],\n",
" [2, 7, 0, 7, 3, 1, 5, 2],\n",
" [8, 1, 9, 3, 3, 7, 6, 2],\n",
" [8, 5, 7, 4, 7, 8, 9, 7],\n",
" [4, 6, 2, 9, 2, 8, 0, 7]])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Y = np.random.randint(0, 10, (8, 8))\n",
"Y"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[189, 112, 108, 174, 156, 131, 190, 143],\n",
" [239, 153, 228, 210, 228, 186, 242, 215],\n",
" [100, 106, 82, 144, 67, 115, 78, 90],\n",
" [194, 104, 105, 181, 137, 137, 175, 164],\n",
" [196, 90, 186, 153, 201, 122, 187, 157],\n",
" [162, 165, 102, 226, 147, 156, 129, 182],\n",
" [122, 72, 99, 122, 84, 109, 98, 111],\n",
" [170, 113, 162, 152, 165, 135, 165, 167]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"strassen(X, Y)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 31 - timeit.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from time import perf_counter\n",
"from itertools import combinations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def timeit(fn, fargs, n_range, seconds=5):\n",
" print(f'[timeit] {seconds} seconds per N')\n",
" \n",
" # timeit for N\n",
" bench = []\n",
" for n in n_range:\n",
" args = fargs(n)\n",
" calls = 0\n",
"\n",
" # benchmark\n",
" timer = perf_counter()\n",
" while perf_counter() - timer < seconds:\n",
" fn(args)\n",
" calls += 1\n",
" timer = perf_counter() - timer\n",
"\n",
" # results\n",
" bench.append([np.e, n, timer / calls])\n",
" print(f'[N={n}] {calls / timer:.2f} calls/sec')\n",
"\n",
" # estimate complexity\n",
" bench = np.log(bench)\n",
" (alpha, beta), *_ = np.linalg.lstsq(bench[:, :2], bench[:, -1])\n",
" print(f'estimated O({np.exp(alpha):.3} * N ^ {beta:.3f})')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## setup"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def combinatorial_sort(data):\n",
" data = data.copy()\n",
" for i, j in combinations(range(len(data)), 2):\n",
" if data[i] > data[j]:\n",
" data[i], data[j] = data[j], data[i]\n",
" return data"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def get_array(n):\n",
" return np.random.randint(0, n, n)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## built-in sorted"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[timeit] 5 seconds per N\n",
"[N=100] 44502.77 calls/sec\n",
"[N=1000] 3092.61 calls/sec\n",
"[N=10000] 231.72 calls/sec\n",
"[N=100000] 16.99 calls/sec\n",
"[N=1000000] 1.06 calls/sec\n",
"estimated O(1.11e-07 * N ^ 1.151)\n"
]
}
],
"source": [
"n_range = [100, 1000, 10000, 100000, 1000000]\n",
"timeit(sorted, get_array, n_range)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## numpy sort"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[timeit] 5 seconds per N\n",
"[N=100] 304622.14 calls/sec\n",
"[N=1000] 55807.77 calls/sec\n",
"[N=10000] 1966.49 calls/sec\n",
"[N=100000] 164.66 calls/sec\n",
"[N=1000000] 13.92 calls/sec\n",
"estimated O(1.38e-08 * N ^ 1.121)\n"
]
}
],
"source": [
"n_range = [100, 1000, 10000, 100000, 1000000]\n",
"timeit(np.sort, get_array, n_range)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## combinatorial sort"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[timeit] 5 seconds per N\n",
"[N=10] 49694.04 calls/sec\n",
"[N=50] 2011.77 calls/sec\n",
"[N=100] 515.40 calls/sec\n",
"[N=500] 19.56 calls/sec\n",
"[N=1000] 4.74 calls/sec\n",
"estimated O(1.92e-07 * N ^ 2.010)\n"
]
}
],
"source": [
"n_range = [10, 50, 100, 500, 1000]\n",
"timeit(combinatorial_sort, get_array, n_range)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 32 - pagerank.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import networkx as nx\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def pagerank(graph, alpha=.9):\n",
" n = len(graph)\n",
"\n",
" # remove links to self\n",
" graph[range(n), range(n)] = 0\n",
" \n",
" # ensure stochasticity\n",
" graph[:, graph.sum(0) == 0] = 1\n",
" graph /= graph.sum(0)\n",
" \n",
" # add random teleports\n",
" graph = alpha * graph + (1 - alpha) / n * np.ones((n, n))\n",
"\n",
" # power iteration\n",
" prev = np.zeros(n)\n",
" rank = prev + 1 / n\n",
" while (rank - prev) @ (rank - prev) > 1e-8:\n",
" prev = rank\n",
" rank = graph @ rank\n",
"\n",
" return rank"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## generate graph"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\ntMnVZjg/LAPElMXmox25sfLcXU9GRob09fW1EqANGzbIMWPGyLFjx9ZKjH/44Qc5btw4GRgYKF98\n8UV56tSpBrC85ly9elUGBQXJs2fPWiY0//3vf8vVq1fLV155pcYTmuHh4fLSpUvV3jcqKsrq9d29\ne7fs3LmzVVtiYmJDDbvOJCcnW16bdevWSaPR6GqTXMarr74q3333XVeb4bC4O5IKeQSIEkJ0AtKB\n0cCYih2EEJHAFmCclLJpZfo3Y1atWkVhYaHlvF27dly7do3U1FT27t3rcL2QiqUBzp07x+TJk+2W\nBnA2JSUlnD171pJOGBkZSVxcHCaTqc4rNMtz3du1a1dlvwEDBvDLL79Yzr/99lvOnj1rORdCVFlU\nzNX07duXHTt28P333zN79mxL3Zrf/va39Zpe2hRo164d58+fd7UZDlPtp1hKaRRCTAZ2Upo584mU\n8qQQ4vmy66uBN4Bg4E9lgmCUDuzOrXAdBoPBqrgVwLBhw1iyZAkHDx5Ep6s+tzg3N5c///nPNSoN\n0BA4WnL22WefZd68eWzevLnOy/1rUtd9/fr1+LX2I+7pOPLvymfi5xMpzC3kyskrFKcUu/xL0BEG\nDRrE999/z86dO63q1jiyAMtdaNeuHXv31k89emfg0CImKeV2YPsdbasrPJ4ITKxf0xT1SfkikQxT\nBkWyiEtnL9Hrv3uh/1yPPlOPn58ff/3rX/nss8+q3OAY4Ny5cyxfvtxSGuCzzz6jf//+tbYtIyPD\nRpgDAgLYuHGjpU99lZz18vJi4cKFPPDAA3USJUfFvXdCbyZ8OoEeD/VASmm1xVxxQTGeXp5szd9K\nP59+tPFoU2t7nIEQgkcffZSHH36YLVu2MHXqVEJCQli4cCH33Xefq81rcJraQiZRGsJxPv369ZPJ\nyckuuXdz4prxGsmGZM6XnAdK958sp7igGKERpPwzhaPrjjJ2yFhee+01u88jpWTfvn0sW7aMpKQk\nJkyYwOTJkx0uq2s0Gjl//rxdcc7MtN0P09/fn6lTp1a6QrPiUZOSs0ajkT59+rB06VKGDh3q0N/Y\nY9q0abRt27bS1wvguOE4ewr3UGwqRqOtOoTh6toptcFkMrFx40bmzZtH9+7dWbBgAX379nW1WQ3G\npUuX6N+/P+np6S61QwjxgyOREVV+wI0pF5fKCjN56Uq9yD5D+tB7cG8eDLANVVQsDZCTk8OUKVNK\n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\nSWRkpOVLSq/XW8I8n3zyCRMnTiQgIMAi9AMHDqR3794uWXgUGhrKe++9x9SpU3nrrbeIjo7mlVde\nYcqUKQ3qSbdr185K3C9fvuyyOYDqUOLeTNBpdI1aXBoLDz30EI888gg7d+60tJVv7FGxNMGd4m5v\n447yhUVPPvlkwxncgLRo0YKEhAQSEhKA0l82p0+ftnj2K1asID09nXvuucdqojYgwLbaaEPRvn17\n1qxZw4wZM5g7dy5RUVHMnDmTF154oUFCSk0p112lQioUd7Bo0SKrMExKSgrr16+36lOd515SUsLO\nnTsJDAy0mWhtqmg0Grp3786ECRP46KOPSElJ4dy5c0ydOhWTycSiRYto3749ffr04Q9/+APr16/n\nl19+ccqK065du/LZZ5+xa9cukpKSiIqKYs2aNZSUlNTrfZS4KxRNmNjYWJ555hmrtj/+8Y/o9bdL\nIFfMmgBbcd+7dy9+fn6MHj260Wag1AdBQUEMHTqUBQsW8K9//YusrCzWr19Pnz592LlzJ4MHDyY0\nNJTHH3+cRYsWsWfPHgoLCxvMnj59+vC3v/2NzZs389VXX9GjRw8+//zzesvzV+KuUDRx3nrrLatd\noK5evcqyZcss59V57tu2bcNgMDTZkExt8fDw4O6772by5Ml8/vnnnD9/nmPHjjFu3DiuX7/O9OnT\nad26NfHx8UydOpWvvvqqQTacjo+PZ9euXaxZs4bly5cTGxvL119/XedfEU1J3FW2jEJRCa+//jqL\nFi2ynPv5+XHmzBlCQ0M5deqU1URa586dOXPmdsmBTp06YTabOX/+vFt77rWhsLCQ5ORkq8wcnU5n\nlZUTExNTbxUvpZRs27aN2bNn4+Pjw8KFC3nwwQdr9b4cPnyY+Ph4y3lsbKxVdpUzcDRbBimlS46+\nfftKhaIxk5WVJYOCgiRgOV588UUppZS3bt2yavfz87P83dmzZ6VOp5OzZs1ylelNCrPZLE+fPi3X\nrVsnn3vuOdm7d2/p5+cnExIS5KxZs+TWrVtlRkZGne9jMpnkpk2bZFRUlLz//vvl/v37a/wc6enp\nVu97cHBwne2qKUCydEBjleeuUFTB+++/z9SpUy3nHh4enDx5kqioKLy8vDAaby8Q0+v16HQ6VqxY\nweuvv86+ffuIjY11hdlNnuzsbA4dOmTx7A8fPkx4eLiVd9+tW7daVbw0Go2sX7+eN998k9jYWBYs\n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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"n = 10\n",
"\n",
"graph = nx.DiGraph()\n",
"graph.add_nodes_from(range(n))\n",
"graph.add_edges_from(np.random.randint(0, n, (3 * n, 2)))\n",
"\n",
"nx.draw_networkx(graph, node_color='lightgreen')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"ranks = pagerank(\n",
" np.array(nx.adjacency_matrix(graph).todense(), dtype=np.float32)\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0.08, 0.1 , 0.06, 0.2 , 0.09, 0.02, 0.08, 0.12, 0.07, 0.17])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ranks.round(2)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\nWrQIkyZNgoqKisy2s9RPZF1QBREp5OXg4EAsLHVNdnY2bdu2jbp27UoAKnxxOBwaPnw4nTx5kng8\nXrXGW7p0Kbm6upJQKBQf44v49Jr/mu4V36OrhVfpSuEViiqOolelr6hUVFpmP+Hh4WRkZESRkZFl\nnn/+/DlNmzaNuFxuufdjaWlJ27dvp5KSkmrdC0v9AEAkyaCxrLizNHqEQiGFhYXR2LFjSU1NrVJR\nt7a2pnXr1lFqamqNxo2IiCATExN6/fq1XO4jKCiITE1N6fHjx+W2SUpKohkzZpCysnK597d27Vq5\n2MOiGFhxZ/nkef78OS1ZsoSaN29eqaBra2vTjBkz6ObNmyQSiWo8dn5+PrVq1YoCAwPlcCf/sW/f\nPrK0tKQXL15U2O7Fixc0c+ZMUlFRkbhPhmFo3bp1crWJpW5hxZ3lk0YkElHLli0rFXVnZ2c6ePAg\nFRYWynX8qVOn0pQpU+Ta53t+++03ateuHaWnp1faNjk5mWbPnk2qqqoEgOzs7IjL5ZKenh5t2rSp\nVuxjqV1YcWf5pHn69Ck5OTmVKehWVlb0888/U2JiYq2MffLkSWrZsiXl5eXVSv9ERIsWLSIHBwfK\nzc2Vqf3r169p3rx5lJWVRYWFheTr60tcLpd0dHRow4YNEmsCLPUbVtxZPjny8/Np79695OTkRMbG\nxuTr60sMwxAAUldXp/Hjx9Pff/9dq0KWkpJCJiYmFBERUWtjEL17MpkxYwb179+fiouLq9VHcXEx\nTZs2jZSVlUlbW5vWrl3LinwDgBV3lk8CkUhEV69eJR8fH9LV1ZWKbvnmm29ox44dlJOTUye2DB48\nmJYsWVLrYxERCQQCGj16NHl4eBCfz692P8XFxfTll1+SiooKaWlp0apVq1iRr8ew4s7SqElOTqaV\nK1dS69at5RbdUlO2bNlCXbt2pdLSssMZawMej0eurq7k4+NT44VgHo8nXoRt0qQJrVixghX5eggr\n7iyNjuLiYjp69Ci5urqSnp6eXKNbasqjR4/I0NCQ4uPj63zsgoIC6tGjB82bN08uPwsej0dz5swh\nVVVV0tTUpKVLl7IiX49gxZ2lUSASiSgqKopmzZpFBgYGtRbdUhN4PB7Z29vTzp07FWZDZmYmderU\niVatWiW3Pvl8Pn377bekqqpKGhoa9NNPP7EiXw9gxZ2lQZOenk6///472dra1np0S0354YcfyN3d\n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\n0bHS6/Py8ggLC6N58+asXbu2yvN1iWv+/PlERUWxfft27rvvPtPxyMhI5s6dy8yZM3n99detvmvX\nggULEELw1ltvWfW8jZlK7kqdXCq9xLb8bZTIklptYAFlm1gYSgxsfnMzV7+/SlJSks12CyoqKmLX\nrl1ERUWxefNmpJSUlJQwcOBAJk+eTGhoKB4eHja5tiVWrFjByy+/XOnYoEGD2L17N46OjuTk5PDc\nc8+RnZ3N+vXrq2xaUlRUxLhx4ygpKWH9+vV1/uRRXFzM5MmTOXv2LFu2bKFly5ZA2eYq06ZNIzU1\nldWrV1e72mNdPP/88zz22GNMnDjRJudvjKy6nrsQYoQQ4rQQIlkI8Xo15foKIfRCiLGWBKs0LOdK\nzvFd3ncUysJaJ3YArU6Lo4sjoW+F8s6Od6zeBJKdnc0///lPnn76aVq1asXixYvp3bs3R48e5caN\nG1y5coWJEyfy7bff1mqpYluYPn16leQeFxfHK6+8gpQSDw8PNm3axGOPPUafPn344YcfTOWKjEVk\n67JZHrUc/z7+PP3i01zPvl7rWK5fv05ISAiFhYXs2bOHli1bIqXkn//8Jz179qRXr14cOXLEZokd\n4Pz582qZXxupseYuhNACZ4DHgTQgAXhWSnnyNuVigSLgCynl+urOq2ruDdPF0otsyduCHr1Vz6tD\nR2/n3gxoMqBO57l06RKbNm3iu+++4/DhwwQHBxMeHs4TTzyBt7f3HV+Xm5vLtm3biI6OJjY2lqCg\nICIiIggLC6v3Lf1KS0t5/PHHq2xSsnz5cmbOnGl6vHHzRv6+5e+E/zEcx9aOFMviSmvMFxUXITWS\nZrpmtHduT3en7nhpzdsYOyUlhVGjRhESEsLf/vY3tFot6enpTJ8+nV9//ZXIyEgeeugh63zD1fDz\n8yMuLo527drZ/FqNhdWaZYQQA4CFUsqQ8sd/BpBSvntLuVlAKdAX2KqS+92n0FjI6pzVddooujo6\ndIxxG0Nbh6qbSFfn1KlTpj1Ef/31V0aPHk1YWBghISG12v4vPz+fmJgY1q9fz44dO+jZsydjx44l\nPDwcX19fi89XG1lZWfTp06fSZhUajYaYmBgeHvowCUUJnCg+gdFoxKAx49OTLNtNylPrSZBzEB0d\nOt5x16bExETGjBnDa6+9ZnozWbduHTNnzmTKlCm8+eabNt0o/abi4mLc3d3Jz89vcHvBNmTmJndz\n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\nAElFTkSuQmCC\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"nx.draw_networkx(graph, node_color='lightgreen', node_size=ranks * 5000)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 33 - reservoir sampling.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def reservoir_sampling(size):\n",
" i, sample = 0, []\n",
"\n",
" while True:\n",
" item = yield i, sample\n",
" \n",
" i += 1\n",
" k = np.random.randint(0, i)\n",
"\n",
" if len(sample) < size:\n",
" sample.append(item)\n",
" elif k < size:\n",
" sample[k] = item"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"100 [15, 49, 54, 63, 60]\n",
"200 [180, 49, 54, 63, 197]\n",
"300 [218, 49, 54, 63, 197]\n",
"400 [218, 49, 54, 63, 197]\n",
"500 [218, 49, 54, 63, 197]\n",
"600 [519, 49, 54, 63, 197]\n",
"700 [691, 49, 54, 63, 197]\n",
"800 [691, 49, 54, 63, 197]\n",
"900 [691, 49, 54, 63, 197]\n",
"1000 [691, 49, 54, 63, 197]\n"
]
}
],
"source": [
"reservoir = reservoir_sampling(5)\n",
"next(reservoir)\n",
"\n",
"for i in range(1000):\n",
" k, sample = reservoir.send(i)\n",
" if k % 100 == 0:\n",
" print(k, sample)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 34 - aho-corasick.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from collections import deque, defaultdict\n",
"from itertools import count"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def aho_corasick():\n",
" G = defaultdict(count(1).__next__) # transitions\n",
" W = defaultdict(set) # alphabet\n",
" F = defaultdict(lambda: 0) # fallbacks\n",
" O = defaultdict(set) # outputs\n",
" \n",
" # automaton\n",
" return G, W, F, O"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def add_word(word, G, W, F, O):\n",
" state = 0\n",
"\n",
" # add transitions between states\n",
" for w in word:\n",
" W[state].add(w)\n",
" state = G[state, w]\n",
" \n",
" # add output\n",
" O[state].add(word)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def build_fsa(G, W, F, O):\n",
" # initial states\n",
" queue = deque(G[0, w] for w in W[0])\n",
" \n",
" while queue:\n",
" state = queue.popleft()\n",
" \n",
" # for each letter in alphabet\n",
" for w in W[state]:\n",
" # find fallback state\n",
" t = F[state]\n",
" while t and (t, w) not in G:\n",
" t = F[t]\n",
" \n",
" # for next state define its fallback and output\n",
" s = G[state, w]\n",
" F[s] = G[t, w] if (t, w) in G else 0\n",
" O[s] |= O[F[s]]\n",
" \n",
" queue.append(s)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def search(text, G, W, F, O):\n",
" state = 0\n",
" \n",
" for i, t in enumerate(text):\n",
" # fallback\n",
" while state and (state, t) not in G:\n",
" state = F[state]\n",
" \n",
" # transition\n",
" state = G[state, t] if (state, t) in G else 0\n",
" \n",
" # output\n",
" if O[state]:\n",
" print('@', i, O[state])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"AC = aho_corasick()\n",
"add_word('bar', *AC)\n",
"add_word('ara', *AC)\n",
"add_word('bara', *AC)\n",
"add_word('barbara', *AC)\n",
"build_fsa(*AC)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"@ 2 {'bar'}\n",
"@ 5 {'bar'}\n",
"@ 12 {'bar'}\n",
"@ 15 {'bar'}\n",
"@ 16 {'ara', 'barbara', 'bara'}\n",
"@ 26 {'bar'}\n",
"@ 27 {'ara', 'bara'}\n"
]
}
],
"source": [
"search('barbarian barbara said: barabum', *AC)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 35 - median.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def kth(items, k, depth=1):\n",
" if len(items) == 1:\n",
" return items[0], depth\n",
"\n",
" # randomize on pivot\n",
" pivot = np.random.choice(items)\n",
" split = np.sum(items <= pivot)\n",
" \n",
" # search partition\n",
" if k < split:\n",
" return kth(items[items <= pivot], k, depth + 1)\n",
" else:\n",
" return kth(items[items > pivot], k - split, depth + 1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"items = np.arange(1000000)\n",
"np.random.shuffle(items)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(500000, 29)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"kth(items, len(items) // 2)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(0, 14)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"kth(items, 0)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(999999, 16)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"kth(items, len(items) - 1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 36 - bulls and cows.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from random import choice, sample\n",
"from itertools import permutations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## common"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def score(x, y):\n",
" bulls = sum(i == j for i, j in zip(x, y))\n",
" cows = len(set(x) & set(y)) - bulls\n",
" return bulls, cows"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## player 1"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def player1(player2):\n",
" secret = sample(range(10), 4)\n",
" \n",
" tip = next(player2)\n",
" while True:\n",
" b, c = score(secret, tip)\n",
" if b < 4:\n",
" print(b, 'bulls', c, 'cows')\n",
" tip = player2.send((b, c))\n",
" else:\n",
" print('you won')\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## player 2"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def player2():\n",
" tips = list(permutations(range(10), 4))\n",
" \n",
" while True:\n",
" tip = choice(tips)\n",
" print(tip, '?')\n",
" bc = yield tip\n",
" tips = [i for i in tips if score(i, tip) == bc]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## game"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(5, 8, 3, 9) ?\n",
"1 bulls 1 cows\n",
"(9, 1, 3, 0) ?\n",
"1 bulls 1 cows\n",
"(5, 9, 7, 0) ?\n",
"1 bulls 1 cows\n",
"(6, 9, 3, 7) ?\n",
"3 bulls 0 cows\n",
"(2, 9, 3, 7) ?\n",
"3 bulls 0 cows\n",
"(4, 9, 3, 7) ?\n",
"you won\n"
]
}
],
"source": [
"player1(player2())"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(3, 6, 2, 8) ?\n",
"0 bulls 1 cows\n",
"(7, 0, 8, 4) ?\n",
"0 bulls 2 cows\n",
"(2, 7, 5, 0) ?\n",
"0 bulls 1 cows\n",
"(8, 5, 4, 9) ?\n",
"1 bulls 1 cows\n",
"(4, 3, 7, 9) ?\n",
"0 bulls 3 cows\n",
"(6, 9, 4, 7) ?\n",
"2 bulls 0 cows\n",
"(0, 9, 4, 3) ?\n",
"you won\n"
]
}
],
"source": [
"player1(player2())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 37 - longest common subsequence.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from collections import defaultdict"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def lcs(X, Y):\n",
" # memoize longest subsequences\n",
" table = defaultdict(lambda: 0)\n",
" \n",
" for i in range(len(X)):\n",
" for j in range(len(Y)):\n",
" if X[i] == Y[j]:\n",
" table[i, j] = table[i - 1, j - 1] + 1\n",
" else:\n",
" table[i, j] = max(table[i - 1, j], table[i, j - 1])\n",
"\n",
" # reconstruction\n",
" sequence = ''\n",
" i, j = len(X) - 1, len(Y) - 1\n",
" \n",
" while i >= 0 and j >= 0:\n",
" if X[i] == Y[j]:\n",
" sequence = X[i] + sequence\n",
" i -= 1\n",
" j -= 1\n",
" elif table[i - 1, j] < table[i, j - 1]:\n",
" j -= 1\n",
" else:\n",
" i -= 1\n",
" \n",
" # result\n",
" return table[len(X) - 1, len(Y) - 1], sequence"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(18, 'oest n subsequence')"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lcs('longest common sub/sequence', 'shortest unique sub-sequence')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 38 - burrows-wheeler.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def bwt(source):\n",
" aux = [source[i:] + source[:i] for i in range(len(source))]\n",
" aux.sort()\n",
" idx = aux.index(source)\n",
" return ''.join(i[-1] for i in aux), idx"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def ibwt(source, idx):\n",
" n = len(source)\n",
" aux = [''] * n\n",
" for _ in range(n):\n",
" aux = sorted([i + j for i, j in zip(source, aux)])\n",
" return aux[idx]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"('es,de,aet wnrhrhhhhttt taeeeaer ', 30)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"target, i = bwt('the theta, there and there, was her')\n",
"target, i"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'the theta, there and there, was her'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ibwt(target, i)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 39 - 4sum.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import numpy as np\n",
"from itertools import combinations, product\n",
"from collections import defaultdict"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def sum4(data):\n",
" # store 2-sums\n",
" sum_of_2 = defaultdict(list)\n",
" for i, j in combinations(range(len(data)), 2):\n",
" k = data[i] + data[j]\n",
" sum_of_2[k].append((i, j))\n",
"\n",
" # match pairs of 2-sums\n",
" sum_of_4 = set()\n",
" for k in sum_of_2:\n",
" if k >= 0 and -k in sum_of_2:\n",
" for i, j in product(sum_of_2[k], sum_of_2[-k]):\n",
" index = tuple(sorted(set(i + j)))\n",
" if len(index) == 4:\n",
" sum_of_4.add(index)\n",
"\n",
" return sum_of_4"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([ 5, 1, -9, -6, 4, -4, 7, 1, -10, -6])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n = 10\n",
"data = np.random.randint(-n, n, n)\n",
"data"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(0, 1, 4, 8) [ 5 1 4 -10]\n",
"(1, 2, 6, 7) [ 1 -9 7 1]\n",
"(1, 3, 4, 7) [ 1 -6 4 1]\n",
"(0, 3, 6, 9) [ 5 -6 7 -6]\n",
"(0, 4, 7, 8) [ 5 4 1 -10]\n",
"(1, 4, 7, 9) [ 1 4 1 -6]\n"
]
}
],
"source": [
"for index in sum4(data):\n",
" print(index, data[list(index)])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 40 - counting ones.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from collections import defaultdict\n",
"from itertools import count"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def stream_counter():\n",
" bucket = defaultdict(list)\n",
" timestamp = count(1)\n",
" estimate = None\n",
"\n",
" while True:\n",
" code = yield estimate\n",
" estimate = None\n",
"\n",
" # update buckets\n",
" if code is True:\n",
" bucket[1].append(next(timestamp))\n",
"\n",
" i = 1\n",
" while len(bucket[i]) == 3:\n",
" bucket[2 * i].append(bucket[i][1])\n",
" del bucket[i][:2]\n",
" i *= 2\n",
"\n",
" elif code is False:\n",
" next(timestamp)\n",
"\n",
" # estimate count\n",
" elif isinstance(code, int):\n",
" counts = [i for i in bucket for t in bucket[i] if code < t] or [0]\n",
" estimate = sum(counts) - counts[-1] // 2\n",
" \n",
" # debug\n",
" elif code == 'debug':\n",
" for i in bucket:\n",
" print(i, bucket[i])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"last 10000 bits: 6932\n",
"last 257501 bits: 140052\n",
"last 505000 bits: 303892\n",
"last 752500 bits: 434964\n",
"last 1000000 bits: 434964\n"
]
}
],
"source": [
"n = 10 ** 6\n",
"ctr = stream_counter()\n",
"next(ctr)\n",
"for i in range(n):\n",
" ctr.send(np.random.rand() >= .5)\n",
"\n",
"for i in np.linspace(.99, 0, 5):\n",
" k = int(i * n)\n",
" print(f'last {n - k} bits: {ctr.send(k)}')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 [999999, 1000000]\n",
"2 [999998]\n",
"4 [999989, 999995]\n",
"8 [999984]\n",
"16 [999944, 999970]\n",
"32 [999910]\n",
"64 [999850]\n",
"128 [999732]\n",
"256 [998922, 999481]\n",
"512 [997478, 998417]\n",
"1024 [996449]\n",
"2048 [994456]\n",
"4096 [990348]\n",
"8192 [965868, 982223]\n",
"16384 [949681]\n",
"32768 [851510, 916898]\n",
"65536 [655300, 785917]\n",
"131072 [261942, 524040]\n"
]
}
],
"source": [
"ctr.send('debug')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 41 - union-find.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def find(data, i):\n",
" if i != data[i]:\n",
" data[i] = find(data, data[i])\n",
" return data[i]"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def union(data, i, j):\n",
" pi, pj = find(data, i), find(data, j)\n",
" if pi != pj:\n",
" data[pi] = pj"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def connected(data, i, j):\n",
" return find(data, i) == find(data, j)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"item 0 -> component 9\n",
"item 1 -> component 9\n",
"item 2 -> component 9\n",
"item 3 -> component 3\n",
"item 4 -> component 9\n",
"item 5 -> component 9\n",
"item 6 -> component 9\n",
"item 7 -> component 7\n",
"item 8 -> component 8\n",
"item 9 -> component 9\n"
]
}
],
"source": [
"n = 10\n",
"data = [i for i in range(n)]\n",
"connections = [(0, 1), (1, 2), (0, 9), (5, 6), (6, 4), (5, 9)]\n",
"\n",
"# union\n",
"for i, j in connections:\n",
" union(data, i, j)\n",
"\n",
"# find\n",
"for i in range(n):\n",
" print('item', i, '-> component', find(data, i))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 42 - hamming codes.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def encode(parity_bits, data):\n",
" n = len(data) + parity_bits\n",
" assert 2 ** parity_bits == n + 1\n",
"\n",
" # copy data to code\n",
" code = np.zeros(n, dtype=int)\n",
" code[np.arange(n) & np.arange(n) + 1 > 0] = data\n",
"\n",
" # parity mask\n",
" mask = np.zeros(n, dtype=int)\n",
" mask[::2] = 1\n",
"\n",
" # compute parity\n",
" i = 0\n",
" while i < n:\n",
" code[i] = code[i:][mask == 1].sum() & 1\n",
" i += i + 1\n",
" mask = np.repeat(mask, 2)[:n - i]\n",
"\n",
" # result\n",
" return code"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def decode(code):\n",
" n = len(code)\n",
"\n",
" # parity mask\n",
" mask = np.zeros(n, dtype=int)\n",
" mask[::2] = 1\n",
"\n",
" # compute parity\n",
" error, i = -1, 0\n",
" while i < n:\n",
" error += (i + 1) * (code[i:][mask == 1].sum() & 1)\n",
" i += i + 1\n",
" mask = np.repeat(mask, 2)[:n - i]\n",
"\n",
" # fix error\n",
" if error >= 0:\n",
" code[error] ^= 1\n",
"\n",
" # get data from code\n",
" data = code[np.arange(n) & np.arange(n) + 1 > 0]\n",
"\n",
" # result\n",
" return error, data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## encoding"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hamming code [1 0 0 1] -> [0 0 1 1 0 0 1]\n",
"with error [0 0 1 0 0 0 1]\n",
"error @ 3 -> [1 0 0 1]\n"
]
}
],
"source": [
"parity_bits = 3\n",
"data = np.random.randint(0, 2, 4)\n",
"\n",
"# generate code\n",
"code = encode(parity_bits, data)\n",
"print('hamming code', data, '->', code)\n",
"\n",
"# make error\n",
"code[3] ^= 1\n",
"print('with error', code)\n",
"\n",
"# reconstruct\n",
"error, recon = decode(code)\n",
"print('error @', error, '->', recon)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hamming code [0 0 0 1 0 0 0 0 1 1 1] -> [1 1 0 0 0 0 1 1 0 0 0 0 1 1 1]\n",
"with error [1 1 0 0 0 0 1 1 0 0 0 0 1 1 0]\n",
"error @ 14 -> [0 0 0 1 0 0 0 0 1 1 1]\n"
]
}
],
"source": [
"parity_bits = 4\n",
"data = np.random.randint(0, 2, 11)\n",
"\n",
"# generate code\n",
"code = encode(parity_bits, data)\n",
"print('hamming code', data, '->', code)\n",
"\n",
"# make error\n",
"code[14] ^= 1\n",
"print('with error', code)\n",
"\n",
"# reconstruct\n",
"error, recon = decode(code)\n",
"print('error @', error, '->', recon)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 43 - shuffle.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def shuffle(data):\n",
" n = len(data)\n",
" for i in range(n):\n",
" k = np.random.randint(i, n)\n",
" data[i], data[k] = data[k], data[i]\n",
" \n",
" return data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## shuffle"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"data = list(range(10))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[3, 8, 6, 4, 1, 5, 0, 7, 2, 9]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"shuffle(data)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[3, 8, 9, 7, 6, 2, 4, 5, 0, 1]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"shuffle(data)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[7, 6, 4, 2, 1, 9, 3, 8, 5, 0]"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"shuffle(data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 44 - gradient approximation.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def gradient(fun, x, delta=1e-4):\n",
" x = np.asfarray(x)\n",
" grad = np.zeros(x.shape, dtype=x.dtype)\n",
"\n",
" for i, t in np.ndenumerate(x):\n",
" x[i] = t + delta\n",
" grad[i] = fun(x)\n",
" x[i] = t - delta\n",
" grad[i] -= fun(x)\n",
" x[i] = t\n",
"\n",
" return grad / (2 * delta)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## quadratic form"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x= -1 grad= [-4.]\n",
"x= 0 grad= [ 2.]\n",
"x= 1 grad= [ 8.]\n"
]
}
],
"source": [
"def function(x):\n",
" return 3 * x**2 + 2 * x + 1\n",
"\n",
"for x in [-1, 0, 1]:\n",
" print('x=', x, 'grad=', gradient(function, [x]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## transcendental function"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x= [0, 0, 0] grad= [ 0. 1. 1.]\n",
"x= [0, 0, 1] grad= [ 1.84147099 1.54030231 1. ]\n",
"x= [0, 1, 1] grad= [ 3.55975282 3.25858414 1.87681085]\n",
"x= [1, 1, 1] grad= [ 8.2305271 7.92935842 7.08788742]\n"
]
}
],
"source": [
"def function(X):\n",
" x, y, z = X\n",
" return x * np.sin(z) + y * np.cos(z) + z * np.exp(x + y)\n",
"\n",
"for x in [[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 1, 1]]:\n",
" print('x=', x, 'grad=', gradient(function, x))"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"## determinant"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x=\n",
"[[1, 2, 3], [2, 3, 1], [3, 1, 2]]\n",
"grad=\n",
"[[ 5. -1. -7.]\n",
" [-1. -7. 5.]\n",
" [-7. 5. -1.]]\n",
"x=\n",
"[[1, 1, 1], [1, 1, 1], [1, 1, 1]]\n",
"grad=\n",
"[[ 0. 0. 0.]\n",
" [ 0. 0. 0.]\n",
" [ 0. 0. 0.]]\n",
"x=\n",
"[[1, 1], [1, 1]]\n",
"grad=\n",
"[[ 1. -1.]\n",
" [-1. 1.]]\n"
]
}
],
"source": [
"function = np.linalg.det\n",
"\n",
"for x in [\n",
" [[1, 2, 3], [2, 3, 1], [3, 1, 2]],\n",
" [[1, 1, 1], [1, 1, 1], [1, 1, 1]],\n",
" [[1, 1], [1, 1]],\n",
"]:\n",
" print('x=')\n",
" print(x)\n",
" print('grad=')\n",
" print(gradient(function, x))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 45 - binary search tree.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from sklearn.linear_model import LinearRegression\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def search(node, value):\n",
" if node:\n",
" x, left, right = node\n",
" this = value == x\n",
" lsearch = value < x and search(left, value)\n",
" rsearch = value > x and search(right, value)\n",
" return this or lsearch or rsearch"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def add(node, value):\n",
" if node:\n",
" x, left, right = node\n",
" this = value == x and node\n",
" ladd = value < x and (x, add(left, value), right)\n",
" radd = value > x and (x, left, add(right, value))\n",
" return this or ladd or radd\n",
" return value, None, None"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def depth(node):\n",
" return node and max(depth(node[1]), depth(node[2])) + 1 or 0"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def iterate(node):\n",
" if node:\n",
" x, left, right = node\n",
" yield from iterate(left)\n",
" yield x\n",
" yield from iterate(right)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"depth 7\n",
"[0, 2, 3, 4, 6, 7, 9, 11, 12, 14, 16]\n",
"10 False\n",
"16 True\n"
]
}
],
"source": [
"data = [2, 16, 4, 2, 2, 11, 9, 0, 14, 11, 11, 9, 12, 7, 2, 12, 3, 9, 6, 12]\n",
"\n",
"root = None\n",
"for value in data:\n",
" root = add(root, value)\n",
" \n",
"print('depth', depth(root))\n",
"print(list(iterate(root)))\n",
"print(10, search(root, 10))\n",
"print(16, search(root, 16))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## simulation"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"tree_vs_depth = {\n",
" 10: [], 20: [], 50: [], \n",
" 100: [], 200: [], 500: [], \n",
" 1000: [], 2000: []\n",
"}\n",
"\n",
"for _ in range(1000):\n",
" root = None\n",
" for i, value in enumerate(np.random.randint(100000, size=2500)):\n",
" root = add(root, value)\n",
" if i + 1 in tree_vs_depth:\n",
" tree_vs_depth[i + 1].append(depth(root))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## statistics"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 10 items: depth[mean,min,max,std]=[ 5.6 4. 9. 0.9]\n",
" 20 items: depth[mean,min,max,std]=[ 7.7 5. 13. 1.2]\n",
" 50 items: depth[mean,min,max,std]=[ 10.8 8. 19. 1.5]\n",
" 100 items: depth[mean,min,max,std]=[ 13.3 10. 22. 1.5]\n",
" 200 items: depth[mean,min,max,std]=[ 15.8 12. 25. 1.7]\n",
" 500 items: depth[mean,min,max,std]=[ 19.3 15. 28. 1.8]\n",
"1000 items: depth[mean,min,max,std]=[ 22. 18. 30. 1.9]\n",
"2000 items: depth[mean,min,max,std]=[ 24.7 21. 33. 1.9]\n"
]
}
],
"source": [
"x, y = [], []\n",
"\n",
"for i, d in tree_vs_depth.items():\n",
" x.append([np.log(i)])\n",
" y.append([np.mean(d), np.min(d), np.max(d), np.std(d)])\n",
" print('{:4} items: depth[mean,min,max,std]={}'.format(i, np.round(y[-1], 1)))\n",
" \n",
"x, y = np.array(x), np.array(y)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"mean 3.62404195054 -3.14916237239\n",
"min 3.21930613708 -4.4059750537\n",
"max 4.4103123923 0.413246463845\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/tobbi/anaconda/lib/python3.6/site-packages/scipy/linalg/basic.py:1018: RuntimeWarning: internal gelsd driver lwork query error, required iwork dimension not returned. This is likely the result of LAPACK bug 0038, fixed in LAPACK 3.2.2 (released July 21, 2010). Falling back to 'gelss' driver.\n",
" warnings.warn(mesg, RuntimeWarning)\n"
]
}
],
"source": [
"model_mean = LinearRegression().fit(x, y[:, 0])\n",
"print('mean', model_mean.coef_[0], model_mean.intercept_)\n",
"\n",
"model_min = LinearRegression().fit(x, y[:, 1])\n",
"print('min', model_min.coef_[0], model_min.intercept_)\n",
"\n",
"model_max = LinearRegression().fit(x, y[:, 2])\n",
"print('max', model_max.coef_[0], model_max.intercept_)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"ac8820c8-0096-4d95-8516-cc729a1b0731\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"ac8820c8-0096-4d95-8516-cc729a1b0731\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'ac8820c8-0096-4d95-8516-cc729a1b0731' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"ac8820c8-0096-4d95-8516-cc729a1b0731\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"ac8820c8-0096-4d95-8516-cc729a1b0731\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"plot = figure()\n",
"\n",
"plot.scatter(x.ravel(), y[:, 0])\n",
"plot.scatter(x.ravel(), y[:, 1])\n",
"plot.scatter(x.ravel(), y[:, 2])\n",
"\n",
"plot.line([0, 10], [model_mean.predict(0), model_mean.predict(10)], color='green')\n",
"plot.line([0, 10], [model_min.predict(0), model_min.predict(10)], color='red')\n",
"plot.line([0, 10], [model_max.predict(0), model_max.predict(10)], color='red')\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 46 - bézier curve.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"import scipy.special\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def bezier(points, steps=100):\n",
" n = len(points)\n",
" b = [scipy.special.binom(n - 1, i) for i in range(n)]\n",
" r = np.arange(n)\n",
"\n",
" for t in np.linspace(0, 1, steps):\n",
" u = np.power(t, r) * np.power(1 - t, n - r - 1) * b\n",
" yield t, u @ points"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## plot"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"beb0e9b7-ff9f-4884-9f2c-f62b1fbbb62f\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"beb0e9b7-ff9f-4884-9f2c-f62b1fbbb62f\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'beb0e9b7-ff9f-4884-9f2c-f62b1fbbb62f' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"beb0e9b7-ff9f-4884-9f2c-f62b1fbbb62f\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"beb0e9b7-ff9f-4884-9f2c-f62b1fbbb62f\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"points = np.array([[0, 0], [0, 1], [1, 1], [1, 0]] * 4)\n",
"plot = figure()\n",
"\n",
"for i in range(3, 14):\n",
" curve = np.array([p for _, p in bezier(points[:i])])\n",
" color = tuple(np.random.randint(0, 256, 3))\n",
" plot.line(curve[:, 0], curve[:, 1], color=color)\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 47 - factoradic.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"fac = lambda i, *j: i and fac(*divmod(i, len(j) + 1), *j) or j or (i,)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"dec = lambda i, k=0, *j: j and dec(i * len(j) + i + k, *j) or i"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 <-> 0\n",
"1 <-> 1 0\n",
"8 <-> 1 1 0 0\n",
"27 <-> 1 0 1 1 0\n",
"64 <-> 2 2 2 0 0\n",
"125 <-> 1 0 0 2 1 0\n",
"216 <-> 1 4 0 0 0 0\n",
"343 <-> 2 4 1 0 1 0\n",
"512 <-> 4 1 1 1 0 0\n",
"729 <-> 1 0 0 1 1 1 0\n",
"1000 <-> 1 2 1 2 2 0 0\n"
]
}
],
"source": [
"for i in range(0, 11):\n",
" f = fac(i ** 3)\n",
" d = dec(*f)\n",
" print(d, '<->', ' '.join(map(str, f)))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(1, 1, 4, 1, 2, 2, 1, 0)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fac(6281)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"6281"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dec(*(1, 1, 4, 1, 2, 2, 1, 0))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 48 - dijkstra.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from heapq import heappush, heappop\n",
"import numpy as np\n",
"import networkx as nx\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def dijkstra(graph, source):\n",
" distance = {}\n",
" queue = [(0, source)]\n",
" \n",
" while queue:\n",
" # shortest unexplored path\n",
" p, v = heappop(queue)\n",
" if v in distance:\n",
" continue\n",
"\n",
" # shortest path (source, ..., v)\n",
" print('({}, ..., {}) = {}'.format(source, v, p))\n",
" distance[v] = p\n",
"\n",
" # extend path to (source, ..., v, u)\n",
" for _, u, e in graph.edges(v, data=True):\n",
" heappush(queue, (p + e['weight'], u))\n",
" \n",
" return distance"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"n = 20\n",
"graph = nx.Graph()\n",
"graph.add_nodes_from(range(n))\n",
"for u, v in np.random.randint(0, n, (n, 2)):\n",
" graph.add_edge(u, v, weight=abs(u - v))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAsYAAAHVCAYAAADywj0dAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3Xl8nFXZ//HPPemSriwtZacspVAoLUhRFrFIi0HDWpEK\ntoDihrhjcXkMl5dREfj5iCL6IItLCwIiKBg10IJlFVnL1pV9LVCga5q2yfz+OHe6JpOZZCb3LN/3\n65VXM9vp1TZNvnPuc64TpdNpREREREQqXSrpAkREREREioGCsYiIiIgICsYiIiIiIoCCsYiIiIgI\noGAsIiIiIgIoGIuIiIiIAArGIiIiIiKAgrGIiIiICKBgLCIiIiICKBiLiIiIiAAKxiIiIiIigIKx\niIiIiAigYCwiIiIiAigYi4iIiIgACsYiIiIiIoCCsYiIiIgIoGAsIiIiIgIoGIuIiIiIAArGIiIi\nIiKAgrGIiIiICKBgLCIiIiICKBiLiIiIiAAKxiIiIiIigIKxiIiIiAigYCwiIiIiAigYi4iIiIgA\nCsYiIiIiIgD0SroAEcmPmvqGvsABwCigGlgNzAWebKyrbU6yNhERkVIQpdPppGsQkS6qqW9IAROB\nacB4oAmIgCqgBUgD/YDZwCXAzMa62tZkqhURESluCsYiJaqmvmEEcAMwEhhACMQdSQMrgQXA5Ma6\n2kWFr1BERKS0KBiLlKCa+oZJwHSgL2F2OFstQDMwtbGu9uZC1CYiIlKqFIxFSkwcimcQlkh0VRMw\nReFYRERkAwVjkRISL5+YA/TPw3CrgDGNdbXP5mEsERGRkqd2bSIlIt5odwNh+UQ+9AVujMcVERGp\neGrXJlI6JhI22rW7pviEccM5Zuwu7D5sEP9++jV+dusTnY1XFY83Abgjr5WKiIiUIM0UiZSOaYTu\nE+1asqKZ6+5dxO2Pv5LLmAOA87tbmIiISDlQMBYpAfHhHePJ0JLtvnlv8MD8xSxrWpPL0BEwPh5f\nRESkoikYi5SGAwidJAqhCRhdoLFFRERKhoKxSGkYReYDPLojAvYr0NgiIiIlQ8FYpDRUk9tBHrmo\niscXERGpaArGIqVhNeHUukJoiccXERGpaArGIqVhLpDxNJ5UFNG7KkUqFW34PMpq9UUaeCYfRYqI\niJQy9TEWKQ1P0skR0KcfOYKp40euvz1xzC5Mn72AGXcv7GzsfsBT3a5QRESkxOlIaJESUVPfcAfh\nMI58bsJLA7Ma62qPyeOYIiIiJUlLKURKxyXAyjyPuRK4OM9jioiIlCQFY5HSMRNYQP424bXE483K\n03giIiIlTUspREpITX3DCGAO0D8Pw60CxjTW1T6bh7FERERKnmaMRUpIY13tImAq3T8FrwmYqlAs\nIiKygWaMRUpQTX3DJGA60JfcDv5oAZoJofjmQtQmIiJSqhSMRUpUvKziBmAkMIDM3SrShI12C4BT\nNVMsIiKyJQVjkRJWU9+QIrRwOx8YT1giERFmkVsIgbgfMJvQfWJWY11tazLVioiIFDcFY5EyUVPf\n0BcYDewHVBOOeX4GeKqxrrY5ydpERERKgYKxiIiIiAjqSiEiIiIiAigYi4iIiIgACsYiIiIiIoCC\nsYiIiIgIoGAsIiIiIgIoGIuIiIiIAArGIiIiIiKAgrGIiIiICKBgLCIiIiICQK+kCxAREYH1x5of\nAIxiw7Hmc4Enday5iPQEHQktIiKJqalvSAETgWnAeKAJiIAqoAVIA/2A2cAlwMzGutrWZKoVkXKn\nYCwiIomoqW8YAdwAjAQGEAJxR9LASmABMLmxrnZR4SsUyUxXOcqPgrGIiPS4mvqGScB0oC9hdjhb\nLUAzMLWxrvbmQtQmnavkQKirHOVNwVhERHpUHIpnEMJDVzUBUxSOe44Coa5yVAIFYxER6TFxsJgD\n9M/DcKuAMY11tc/mYSzJQIFQVzkqhdq1iYhIj4hnHG8gBIt86AvcGI8rBRIHwjnAWGAgmUMx8eMD\n4+fPiV9f0ja6ytGf3EIx8fP7AzPK4e+i3OmbiYiI9JSJhBnH9cHihHHDuezsI7jtu8dy3glj1j+x\nVyri+6e8jz985cM01tUyZvi27Y1XFY83ocB1VywFwvWz5dPp3tIf4tdPr6lv2Kv7VUmhKBiLiEhP\nmUa4DL/ekhXNXHfvIm5//JUtnvz0S+9w8V8fZ8ny1ZnGHACcn9cqBVAgBF3lqET6hxERkYKLuxiM\nZ7PL8PfNe4MH5i9mWdOaTZ6/rjXNLf99gadffpfWzHthImB8PL7kiQLhellf5QA4cPchXHXOeP72\nnWO5eOqhDNtqi/cUuspR5ErtC1RERErTAYQuBvmXTjcPTi870t0Hu3sfd+9sDax0botA2E2lGgiz\nvsoxuF9vLvjEwfzh3/M55ZLbWfDae3xv0kHtjamrHEVMR0KLiEhPGEXnm7a6JEXrwGHpt+/Y6K60\nu68mBPHVHXx05bFsXtNsZuXQomyLQLi5i6ceyqhdtqalNczov718NZ/99exML2kLhHdkelKxyHSV\nA2DkjlsxtHf1+vuP2HcHXnxrBffMDY9Pv3shfz7vGHYdMoCXl6zceIj1VznKvedzKVIwFhGRnlBN\n/mYft5De9AJoRFjX2t21sV3i7mvomRC+xYeZre1u/R0FwvZc/s+n+dfjL2c7dMkEQnePqqIDD2yh\najVR1Dub1wzfbhDPLV62/nbz2hZee3clw7cbtHkwhvBvOBp4JG9FS14oGIuISE9YTejnWghpSK8g\nBK9qkv/Z1if+GNzTv7G7t9DNED6CbYc+G+3eko5SWQXCnKTTqwez/Eh3n8uGv6feWXye7fNy/bzD\nx/ZIv8Rz0XBas3w/169PL5au2jTvr2peR78+7X45RsB+KBgXnaS/eYiISGWYSzj4YROpKKIqFZFK\nRaSiiN5VKVpa07Sm0/Su2jAL3KsqRe+qFGtbtlyl0BpVLX822uPoX9d9+REAd+9F2OxVTZg1rm7n\no6P7u/pY2/1JbwKsIixZyLgMojMR6S3/sdrx6aP34TMT9uWVJSv4/V3zeeLFdzI+P0XroM2WvRSt\ndI7bsJrWrKN/303fSwzo25umNevae3oV4etFioyCsYiI9IQnaWdpw+lHjmDq+JHrb08cswvTZy9g\nxt0LuepL49lh63BA3oWf+gAAZ/zyThYv3WIPXz/gqbYbZrYOWEc4fa1HuXuKMOPYEyG8vfu7vak+\n20B49ax5vPT2cta1pBm//4745EP40pX38Pq7q/Iyfg7WAGvjX7P9vNPnNUXVo9JEx5Plm50X31rO\nMWN3WX+7b+8qdtymPy++tby9p7fN7EuR0ZHQIiLSI2rqG+4gdCXI5ya8NDCrsa72mDyOWbLcvTfd\nDOEvRTsd9Co7HJ3rUoofn3YIDy56i1sfeqHD50Tp1ubtefuGPdMv3Ut+wuw6MytIkKmpbxgHzGKz\nJTFtVzmmjN+boYOqufTvT9LSmmZQv9787tyj+N/bnuDBhW9yxlEjOWC3bfn67+5vb/hlwNGNdbVa\nSlFkNGMsIiI95RLS6UOJooF5HHMlcHEexytp8ea7tUC705TZ2CgQ5hSM03T+jicdpZrfYNgv/3DB\np0shEOZ8laP+pkc499jRnH/Sgcx79T0uvPmxjsbe5CqHFA8FYxER6RHjWuc88lS0z7rV6b5poigf\n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\nCIBe5SV8/98+weMvreHxl95qPdzH7WQWERGRVpQYSzZaCpS1fjLieRRFPCIRj4jnUVwUIdrk0xSc\nrDJpzEBeXLmBul0JJ5vLgGWdEnX6pHT/N858hS6R5nrku674FPfOWR6v3jgX7l9ERCQUSowl69RW\nVTZUVNfMwzWj2LtBbcqpQ5g2YdjecRNHD2TGvFXM/NubFBdFOG1Ef6p/l3BPmQ/My/ajytpz//uK\n+j7b6xupb9zvA0JO3L+IiEhYdI6xZKWK6pozgSdIss42SduBybVVlVnf4KLQ719ERCQMOsdYstVc\nYBXt3IQXQzS43rNpul5nK/T7FxERyTjNGEvWqqiuGQIsBsrTcLmdwOhc6iNf6PcvIiKSaZoxlqxV\nW1W5GphGjC5wKaoDpuVaUljo9y8iIpJpmjGWrFdRXTMZmAGUkFrjjyjQgEsKn+yM2DKh0O9fREQk\nU5QYS04IygoeA4YB3UjcMtoHduBqai/Ih5nSQr9/ERGRTFBiLDmjoromgjvC7AZgAq5EwMPNokZx\nCWEZMA+4HXi2tqoy6UYZ2a7Q719ERKSzKTGWnFRRXVMCjARGAKW4NsfLgWWFcE5vod+/iIhIZ1Bi\nLCIiIiKCTqUQEREREQGUGIuIiIiIAEqMRUREREQAJcYiIiIiIoASYxERERERQImxiIiIiAigxFhE\nREREBFBiLCIiIiICKDEWEREREQGUGIuIiIiIAEqMRUREREQAJcYiIiIiIoASYxERERERQImxiIiI\niAigxFhEREREBFBiLCIiIiICKDEWEREREQGUGIuIiIiIAEqMRUREREQAJcYiIiIiIoASYxERERER\nQImxiIiIiAigxFhEREREBFBiLCIiIiICKDEWEREREQGUGIuIiIiIAEqMRUREREQAJcYiIiIiIoAS\nYxERERERQImxiIiIiAigxFhEREREBFBiLCIiIiICKDEWEREREQGUGIuIiIiIAEqMRUREREQAJcYi\nIiIiIoASYxERERERQImxiIiIiAigxFhEREREBFBiLCIiIiICKDEWEREREQGUGIuIiIiIAEqMRURE\nREQAJcYiIiIiIoASYxERERERQImxiIiIiAigxFhEREREBID/B8lrBrNTdUYqAAAAAElFTkSuQmCC\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"weights = [e['weight'] / n * 10 for (u, v, e) in graph.edges(data=True)]\n",
"\n",
"plt.figure(figsize=(12, 8))\n",
"plt.axis('off')\n",
"\n",
"layout = nx.spring_layout(graph)\n",
"nx.draw_networkx_nodes(graph, layout, node_color='steelblue', node_size=520)\n",
"nx.draw_networkx_edges(graph, layout, edge_color='gray', width=weights)\n",
"nx.draw_networkx_labels(graph, layout, font_color='white')\n",
"\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(0, ..., 0) = 0\n",
"(0, ..., 8) = 8\n",
"(0, ..., 12) = 12\n",
"(0, ..., 2) = 14\n",
"(0, ..., 6) = 18\n",
"(0, ..., 18) = 18\n",
"(0, ..., 7) = 19\n",
"(0, ..., 16) = 20\n",
"(0, ..., 5) = 21\n",
"(0, ..., 14) = 22\n",
"(0, ..., 13) = 23\n",
"(0, ..., 11) = 25\n",
"(0, ..., 17) = 25\n",
"(0, ..., 10) = 26\n",
"(0, ..., 9) = 27\n",
"(0, ..., 4) = 32\n",
"(0, ..., 3) = 33\n"
]
}
],
"source": [
"distances = dijkstra(graph, 0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 49 - ford-fulkerson.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import networkx as nx\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def ford_fulkerson(graph, source, sink, debug=None):\n",
" flow, path = 0, True\n",
" \n",
" while path:\n",
" # search for path with flow reserve\n",
" path, reserve = depth_first_search(graph, source, sink)\n",
" flow += reserve\n",
"\n",
" # increase flow along the path\n",
" for v, u in zip(path, path[1:]):\n",
" if graph.has_edge(v, u):\n",
" graph[v][u]['flow'] += reserve\n",
" else:\n",
" graph[u][v]['flow'] -= reserve\n",
" \n",
" # show intermediate results\n",
" if callable(debug):\n",
" debug(graph, path, reserve, flow)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def depth_first_search(graph, source, sink):\n",
" undirected = graph.to_undirected()\n",
" explored = {source}\n",
" stack = [(source, 0, dict(undirected[source]))]\n",
" \n",
" while stack:\n",
" v, _, neighbours = stack[-1]\n",
" if v == sink:\n",
" break\n",
" \n",
" # search the next neighbour\n",
" while neighbours:\n",
" u, e = neighbours.popitem()\n",
" if u not in explored:\n",
" break\n",
" else:\n",
" stack.pop()\n",
" continue\n",
" \n",
" # current flow and capacity\n",
" in_direction = graph.has_edge(v, u)\n",
" capacity = e['capacity']\n",
" flow = e['flow']\n",
" neighbours = dict(undirected[u])\n",
"\n",
" # increase or redirect flow at the edge\n",
" if in_direction and flow < capacity:\n",
" stack.append((u, capacity - flow, neighbours))\n",
" explored.add(u)\n",
" elif not in_direction and flow:\n",
" stack.append((u, flow, neighbours))\n",
" explored.add(u)\n",
"\n",
" # (source, sink) path and its flow reserve\n",
" reserve = min((f for _, f, _ in stack[1:]), default=0)\n",
" path = [v for v, _, _ in stack]\n",
" \n",
" return path, reserve"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"graph = nx.DiGraph()\n",
"graph.add_nodes_from('ABCDEFGH')\n",
"graph.add_edges_from([\n",
" ('A', 'B', {'capacity': 4, 'flow': 0}),\n",
" ('A', 'C', {'capacity': 5, 'flow': 0}),\n",
" ('A', 'D', {'capacity': 7, 'flow': 0}),\n",
" ('B', 'E', {'capacity': 7, 'flow': 0}),\n",
" ('C', 'E', {'capacity': 6, 'flow': 0}),\n",
" ('C', 'F', {'capacity': 4, 'flow': 0}),\n",
" ('C', 'G', {'capacity': 1, 'flow': 0}),\n",
" ('D', 'F', {'capacity': 8, 'flow': 0}),\n",
" ('D', 'G', {'capacity': 1, 'flow': 0}),\n",
" ('E', 'H', {'capacity': 7, 'flow': 0}),\n",
" ('F', 'H', {'capacity': 6, 'flow': 0}),\n",
" ('G', 'H', {'capacity': 4, 'flow': 0}),\n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"layout = {\n",
" 'A': [0, 1], 'B': [1, 2], 'C': [1, 1], 'D': [1, 0],\n",
" 'E': [2, 2], 'F': [2, 1], 'G': [2, 0], 'H': [3, 1],\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def draw_graph():\n",
" plt.figure(figsize=(12, 4))\n",
" plt.axis('off')\n",
"\n",
" nx.draw_networkx_nodes(graph, layout, node_color='steelblue', node_size=600)\n",
" nx.draw_networkx_edges(graph, layout, edge_color='gray')\n",
" nx.draw_networkx_labels(graph, layout, font_color='white')\n",
"\n",
" for u, v, e in graph.edges(data=True):\n",
" label = '{}/{}'.format(e['flow'], e['capacity'])\n",
" color = 'green' if e['flow'] < e['capacity'] else 'red'\n",
" x = layout[u][0] * .6 + layout[v][0] * .4\n",
" y = layout[u][1] * .6 + layout[v][1] * .4\n",
" t = plt.text(x, y, label, size=16, color=color, \n",
" horizontalalignment='center', verticalalignment='center')\n",
" \n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"draw_graph()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def flow_debug(graph, path, reserve, flow):\n",
" print('flow increased by', reserve, \n",
" 'at path', path,\n",
" '; current flow', flow)\n",
" draw_graph()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"flow increased by 1 at path ['A', 'D', 'G', 'H'] ; current flow 1\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"flow increased by 6 at path ['A', 'D', 'F', 'H'] ; current flow 7\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"flow increased by 1 at path ['A', 'C', 'G', 'H'] ; current flow 8\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"flow increased by 4 at path ['A', 'C', 'E', 'H'] ; current flow 12\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"flow increased by 3 at path ['A', 'B', 'E', 'H'] ; current flow 15\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"flow increased by 0 at path [] ; current flow 15\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"ford_fulkerson(graph, 'A', 'H', flow_debug)"
]
},
{
"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.6.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 50 - game of life.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from scipy.fftpack import fft2, ifft2\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"from bokeh.io import push_notebook\n",
"from time import sleep"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def conway(size, prob=.5):\n",
" # initialize game\n",
" board = (np.random.rand(size, size) < prob).astype(int)\n",
"\n",
" # kernel\n",
" kernel = np.zeros(board.shape)\n",
" kernel[sorted([0, 1, -1] * 3), [-1, 0, 1] * 3] = 1\n",
" kernel[0, 0] = 10\n",
" kernel = fft2(kernel)\n",
" \n",
" # update step\n",
" def _conway():\n",
" cell = ifft2(fft2(board) * kernel).real.round()\n",
" board[:] = (cell == 3) | (cell == 12) | (cell == 13)\n",
" return board\n",
" \n",
" return _conway"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## plotting"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def animate(game, frames=30):\n",
" frame = game()\n",
" \n",
" output_notebook()\n",
" \n",
" plot = figure(x_range=(0, 1), y_range=(0, 1), \n",
" plot_width=frame.shape[1] * 5, \n",
" plot_height=frame.shape[0] * 5)\n",
" plot.axis.visible = False\n",
" image = plot.image([frame], x=0, y=0, dw=1, dh=1)\n",
"\n",
" handle = show(plot, notebook_handle=True)\n",
" \n",
" for i in range(frames):\n",
" image.data_source.data['image'] = [game()]\n",
" push_notebook()\n",
" sleep(.05)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"## pulsar"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"cc2565a7-d151-4b24-943f-752b1c7ba3af\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"cc2565a7-d151-4b24-943f-752b1c7ba3af\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'cc2565a7-d151-4b24-943f-752b1c7ba3af' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"cc2565a7-d151-4b24-943f-752b1c7ba3af\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"cc2565a7-d151-4b24-943f-752b1c7ba3af\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"game = conway(50, .0)\n",
"board = game()\n",
"\n",
"# pulsar\n",
"index = [4, 5, 6, 10, 11, 12]\n",
"board[2, index] = 1\n",
"board[7, index] = 1\n",
"board[9, index] = 1\n",
"board[14, index] = 1\n",
"board[index, 2] = 1\n",
"board[index, 7] = 1\n",
"board[index, 9] = 1\n",
"board[index, 14] = 1\n",
"\n",
"# glider\n",
"board[[30, 30, 30, 31, 32], [30, 31, 32, 30, 31]] = 1\n",
"\n",
"# animate\n",
"animate(game, frames=200)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## random game"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" document.getElementById(\"f3c96d14-2c63-4627-8a44-35e1e1a1f2d0\").textContent = \"BokehJS successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"f3c96d14-2c63-4627-8a44-35e1e1a1f2d0\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'f3c96d14-2c63-4627-8a44-35e1e1a1f2d0' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"f3c96d14-2c63-4627-8a44-35e1e1a1f2d0\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"f3c96d14-2c63-4627-8a44-35e1e1a1f2d0\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"animate(conway(70, .2), frames=200)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 51 - rabin-miller.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from random import randrange"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def rabin_miller(prime, tests):\n",
" if prime < 5:\n",
" return prime in [2, 3]\n",
" \n",
" # set: prime = q * 2**r + 1\n",
" q, r = prime - 1, 0\n",
" while not q & 1:\n",
" q >>= 1\n",
" r += 1\n",
"\n",
" # test repeatedly\n",
" for _ in range(tests):\n",
" a = randrange(2, prime - 1)\n",
"\n",
" # pass if: a**q == 1\n",
" x = pow(a, q, prime)\n",
" if x in [1, prime - 1]:\n",
" continue\n",
"\n",
" # pass if: a**(q * 2**s) == -1, s < r\n",
" for _ in range(r - 1):\n",
" x = pow(x, 2, prime)\n",
" if x == prime - 1:\n",
" break\n",
" else:\n",
" return False\n",
"\n",
" return True"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def prime(bits, tests):\n",
" while True:\n",
" # random number in [2**bits .. 2**(bits+1)-1]\n",
" prime = (1 << bits) | randrange(1 << bits) | 1\n",
"\n",
" # primality test\n",
" if rabin_miller(prime, tests):\n",
" return prime"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## primes"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"491"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prime(8, 32)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"167539476446412633052018914179671756072742261642391296029084943606975058499651"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prime(256, 32)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"327629758798678908807612458298442202698722670823449712916355625086421084819056655501101117430544648756451802433910857072317916389191167555508482465929724915679965943104133398992501561113977009936622055728794693540394304838984423959080924829552190406356895643910247318370440605397978334769846807883781807340317"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prime(1024, 32)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 52 - RSA.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def secure_prime_generator():\n",
" yield from [\n",
" 251352642263889039868309043894037481379002996715589396370854987834622532561522720403074015628816522584866374785754812790090831773387112312703220610291993961566100333483106513061700679351674883108504663868999773335993131871433147375498526830250690800432950741107471775936506033522777378528889986463928680062779,\n",
" 234601306906702217804957533486106543816960131695391266497422573355527800260716665381597389816091857137372406177664905766000014102540204528163683625043444669386812465309478832002368041295429725611772236019022712629169757194963880836723186721316763532024657471001347998077008043814690024358601642733925216784203,\n",
" ]"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def modinv(x, y):\n",
" r, s = 1, 0\n",
" while y:\n",
" r, s = s, r - x // y * s\n",
" x, y = y, x % y\n",
" return r"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def rsa_generate_keys():\n",
" p, q = secure_prime_generator()\n",
" n = p * q\n",
" t = n - p - q + 1\n",
" e = 65537\n",
" d = modinv(e, t) % t\n",
" assert (d * e) % t == 1\n",
" return (e, n), (d, n)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def rsa(plaintext, public_key):\n",
" return pow(plaintext, *public_key)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## keys"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"public_key, secret_key = rsa_generate_keys()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(65537,\n",
" 58967658369561163583995664151705537612631456941226585145001736155445085885436956133402962616775555500479429922140321605063456075222335023020218578571558003435174909963319619244821157746252197885628802071763470174413201522569356053296685834595362968800778468737693074613267684084217204017873750446802044584084498581219849973790017343888256411013653688556278788070745635045095995056877259642839730825907965544973672656542601570609068817838234644958846427643088478240335082249677864789882511592486797239674160452077169411971273434857626735582274817190984442183721945999865859466422472845277588368259261760233826535480137)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"public_key"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(32639742054323523661031580828650534544392003478949839063736255562124081596351847364013089886417596950354636310108218358259943735367279937975211699593540109138569129405212055903155962561652878992005591100527818545966603574053221236696683939389678915058929150433015761702105657992264877747720954135956649973789334911071168428227464085150820871588160770978551544646965210798269197906675922224772713666123225990305644372957419486169245295190574189157389340237417783311258488777336686103120891002317113842264416737708675921812070527474901946450952078789439410581693777829144977217172397092723130874770379072485175449578961,\n",
" 58967658369561163583995664151705537612631456941226585145001736155445085885436956133402962616775555500479429922140321605063456075222335023020218578571558003435174909963319619244821157746252197885628802071763470174413201522569356053296685834595362968800778468737693074613267684084217204017873750446802044584084498581219849973790017343888256411013653688556278788070745635045095995056877259642839730825907965544973672656542601570609068817838234644958846427643088478240335082249677864789882511592486797239674160452077169411971273434857626735582274817190984442183721945999865859466422472845277588368259261760233826535480137)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"secret_key"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## encryption"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"37930332066974779164345751815395228520383070918149654734779452314315201401578073995623676191126275180229634519536130536469650873769044314422167044966349923400485329143076986636753828936028839818231151694765870620828795561931693647118671097571595175457792224986081168799650305451662018565631898905521228230362451756374795462396041777695041246353088986331124068828091083876264418231632281088387993565061643784457276421962040835851861587580705505405821148340529587678730109599751672134343435130195273271395130139199246695649453457192908607858557747694289049617690930150092441884659797085102908069133974365162807747676330"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ciphertext = rsa(0xfacade17, public_key)\n",
"ciphertext"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'0xfacade17'"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"plaintext = rsa(ciphertext, secret_key)\n",
"hex(plaintext)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 53 - RSA encryption scheme.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from os import urandom\n",
"from hashlib import sha512"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def rsa_generate_keys():\n",
" return [\n",
" 65537,\n",
" 58967658369561163583995664151705537612631456941226585145001736155445085885436956133402962616775555500479429922140321605063456075222335023020218578571558003435174909963319619244821157746252197885628802071763470174413201522569356053296685834595362968800778468737693074613267684084217204017873750446802044584084498581219849973790017343888256411013653688556278788070745635045095995056877259642839730825907965544973672656542601570609068817838234644958846427643088478240335082249677864789882511592486797239674160452077169411971273434857626735582274817190984442183721945999865859466422472845277588368259261760233826535480137\n",
" ], [\n",
" 32639742054323523661031580828650534544392003478949839063736255562124081596351847364013089886417596950354636310108218358259943735367279937975211699593540109138569129405212055903155962561652878992005591100527818545966603574053221236696683939389678915058929150433015761702105657992264877747720954135956649973789334911071168428227464085150820871588160770978551544646965210798269197906675922224772713666123225990305644372957419486169245295190574189157389340237417783311258488777336686103120891002317113842264416737708675921812070527474901946450952078789439410581693777829144977217172397092723130874770379072485175449578961,\n",
" 58967658369561163583995664151705537612631456941226585145001736155445085885436956133402962616775555500479429922140321605063456075222335023020218578571558003435174909963319619244821157746252197885628802071763470174413201522569356053296685834595362968800778468737693074613267684084217204017873750446802044584084498581219849973790017343888256411013653688556278788070745635045095995056877259642839730825907965544973672656542601570609068817838234644958846427643088478240335082249677864789882511592486797239674160452077169411971273434857626735582274817190984442183721945999865859466422472845277588368259261760233826535480137\n",
" ]"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def bxor(x, y): \n",
" return bytes(i ^ j for i, j in zip(x, y))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def rsa_encrypt(plaintext, public_key):\n",
" # iv[64] -> h1[64] -> h2[64] -> h3[64]\n",
" iv = urandom(64)\n",
" h1 = sha512(iv).digest()\n",
" h2 = sha512(h1).digest()\n",
" h3 = sha512(h2).digest()\n",
" \n",
" # x[192] := pt[192] ^ (h1|h2|h3)[192]\n",
" pt = int.to_bytes(plaintext, 192, 'big')\n",
" x192 = bxor(pt, h1 + h2 + h3)\n",
" \n",
" # x[64] := iv[64] ^ x[192->64]\n",
" h4 = sha512(x192).digest()\n",
" x64 = bxor(iv, h4)\n",
"\n",
" # x[256] := x[192]|x[64]\n",
" x256 = int.from_bytes(x192 + x64, 'big')\n",
"\n",
" # rsa\n",
" return pow(x256, *public_key)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def rsa_decrypt(ciphertext, secret_key):\n",
" # rsa\n",
" x256 = pow(ciphertext, *secret_key)\n",
" \n",
" # x[192]|x[64] := x[256]\n",
" x256 = int.to_bytes(x256, 256, 'big')\n",
" x192, x64 = x256[:192], x256[192:]\n",
" \n",
" # iv[64] := x[64] ^ x[192->64]\n",
" h4 = sha512(x192).digest()\n",
" iv = bxor(x64, h4)\n",
"\n",
" # iv[64] -> h1[64] -> h2[64] -> h3[64]\n",
" h1 = sha512(iv).digest()\n",
" h2 = sha512(h1).digest()\n",
" h3 = sha512(h2).digest()\n",
" \n",
" # pt[192] := x[192] ^ (h1|h2|h3)[192]\n",
" pt = bxor(x192, h1 + h2 + h3)\n",
" \n",
" # plaintext\n",
" return int.from_bytes(pt, 'big')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RSA keys"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"public_key, secret_key = rsa_generate_keys()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## dummy message #1"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"4649656022129871410084758708819082040705568659517051556579109783035475319993774294723543806613482815096122822053878200544644228348778357274817339362282168110066486550282665463011704367534599444948593483500223005519493074230630668627398499549531921631775796975543367931028635911620992746826279895004397331478316347706112728194245433531056959434149744818592679608607592411699224535481159970074779969735262841301579300176942131772715376140763764664390765116238642078898123587380387775631210841037675092874380628290147828147232010081353547182250999150829887572214726851792733663068257347681398919656884923709141501978704"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ciphertext = rsa_encrypt(0, public_key)\n",
"plaintext = rsa_decrypt(ciphertext, secret_key)\n",
"assert plaintext == 0\n",
"ciphertext"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## dummy message #2"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"2415677512818998864159808469324913238979977385466951039435814444646249478008271176744226855135893423769242743432612025572303707000584164729306951233275711285198489275761956645351340457938947496004725650289180405814005966176789418536522136905331566410303015764282230218004512184067175477403903585752213765089378529719666774931767484072795951172112218811810533028840096512259305459794315163197200014614447695564147069690167232406952338511982673649011356652117898446604052520883179441268892168919553290957240304588682467546257412075550433499736393924753499707514346858917183759726898666883430903794585695045734222633925"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ciphertext = rsa_encrypt(0, public_key)\n",
"plaintext = rsa_decrypt(ciphertext, secret_key)\n",
"assert plaintext == 0\n",
"ciphertext"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 54 - longest unique sequence.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from functools import reduce"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## text"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"text = 'Premature optimization is the root of all evil -- DonaldKnuth'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## version 1"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def longest_unique_sequence(sequence):\n",
" i, j, k = 0, 0, set()\n",
" bi, bj = 0, 0\n",
" \n",
" while j < len(sequence):\n",
" if sequence[j] in k:\n",
" k.remove(sequence[i])\n",
" i += 1\n",
" else:\n",
" k.add(sequence[j])\n",
" j += 1\n",
"\n",
" if j - i > bj - bi:\n",
" bi, bj = i, j\n",
"\n",
" return bi, bj"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3 12 \"mature op\"\n"
]
}
],
"source": [
"i, j = longest_unique_sequence(text)\n",
"print(i, j, '\"%s\"' % text[i:j])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## version 2"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def longest_unique_sequence(sequence):\n",
" i, j = 0, 0\n",
" bi, bj = 0, 0\n",
" \n",
" while j < len(sequence):\n",
" if sequence[j] in sequence[i:j]:\n",
" i += 1\n",
" else:\n",
" j += 1\n",
" \n",
" if j - i > bj - bi:\n",
" bi, bj = i, j\n",
" \n",
" return bi, bj"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3 12 \"mature op\"\n"
]
}
],
"source": [
"i, j = longest_unique_sequence(text)\n",
"print(i, j, '\"%s\"' % text[i:j])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## version 3"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def longest_unique_sequence(sequence):\n",
" i, j = 0, 0\n",
" b = 0, 0, 0\n",
" \n",
" while j < len(sequence):\n",
" k = sequence[j] in sequence[i:j]\n",
" i, j = i + k, j + 1 - k\n",
" b = max(b, (j - i, i, j))\n",
"\n",
" return slice(b[1], b[2])"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"slice(48, 57, None) \"- DonaldK\"\n"
]
}
],
"source": [
"i = longest_unique_sequence(text)\n",
"print(i, '\"%s\"' % text[i])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## version 4"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def longest_unique_sequence(sequence):\n",
" i, b = 0, ''\n",
" \n",
" while i < len(sequence):\n",
" if sequence[i] in sequence[:i]:\n",
" i -= 1\n",
" sequence = sequence[1:]\n",
" else:\n",
" i += 1\n",
" b = max(b, sequence[:i], key=len)\n",
"\n",
" return b"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'mature op'"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"longest_unique_sequence(text)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## version 5"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def longest_unique_sequence(sequence):\n",
" i, j = 0, 0\n",
"\n",
" while j < len(sequence):\n",
" if sequence[j] in sequence[i:j]:\n",
" i += 1\n",
" else:\n",
" j += 1\n",
" yield sequence[i:j]"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'mature op'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"max(longest_unique_sequence(text), key=len)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## version 6"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def longest_unique_sequence(sequence):\n",
" i, k = 0, {}\n",
"\n",
" for j, x in enumerate(sequence):\n",
" i = max(i, k.get(x, 0))\n",
" k[x] = j + 1\n",
" yield sequence[i:j + 1]"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'mature op'"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"max(longest_unique_sequence(text), key=len)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"## version 7"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def longest_unique_sequence(sequence, best=''):\n",
" for x in sequence:\n",
" best = best[best.find(x) + 1:] + x\n",
" yield best"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'mature op'"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"max(longest_unique_sequence(text), key=len)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 55 - quincunx.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"from bokeh.io import push_notebook\n",
"from time import sleep"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def update_quincunx(n, balls, buckets):\n",
" x0, x1, y = balls.T\n",
"\n",
" # update buckets\n",
" buckets[x1[y == 1] + n] += 1\n",
"\n",
" # update y\n",
" y[:] -= 1\n",
" y[y <= 0] = n\n",
"\n",
" # update x\n",
" x1[y == n] = 0\n",
" x0[:] = x1\n",
" x1[:] += np.random.choice([-1, 1], len(x1))"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def animate_quincunx(time, balls):\n",
" x0, x1, y = balls.T\n",
"\n",
" # animate position\n",
" x = x0 * (1 - time) + x1 * time\n",
" y = y * (1 - time) + (y - 1) * time\n",
" \n",
" return x, y"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## initialization"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# quincunx size\n",
"N = 30"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# row ~ ball, column ~ (current_x, next_x, current_y)\n",
"balls = np.zeros((N - 1, 3), dtype=int)\n",
"balls[:, 2] = np.arange(N - 1, dtype=int) + N + 1"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# bottom buckets\n",
"buckets = np.zeros(2 * N + 1, dtype=float)\n",
"buckets_x = np.arange(-N, N + 1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## plot"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"59778c97-9325-4df5-8f3a-cfb5d4f73578\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"59778c97-9325-4df5-8f3a-cfb5d4f73578\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '59778c97-9325-4df5-8f3a-cfb5d4f73578' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"59778c97-9325-4df5-8f3a-cfb5d4f73578\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"59778c97-9325-4df5-8f3a-cfb5d4f73578\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"# figure\n",
"plot = figure(plot_width=640, plot_height=420, tools='',\n",
" x_range=(-N, N), y_range=(0, N))\n",
"plot.grid.visible = False\n",
"plot.yaxis.visible = False\n",
"\n",
"# initial plot\n",
"x, y = animate_quincunx(1, balls)\n",
"\n",
"vbar = plot.vbar(x=buckets_x, width=.8, bottom=0, top=buckets,\n",
" fill_alpha=.4, fill_color='red', line_color='red')\n",
"scatter = plot.scatter(x, y)\n",
"\n",
"# show\n",
"handle = show(plot, notebook_handle=True)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"for i in range(N * 5 * 30):\n",
" time = round(i / 5 % 1, 1)\n",
"\n",
" # update bars\n",
" if time == 0:\n",
" update_quincunx(N, balls, buckets)\n",
" vbar.data_source.data['top'] = buckets * .2\n",
" \n",
" # update scatter\n",
" x, y = animate_quincunx(time, balls)\n",
" scatter.data_source.data['x'] = x\n",
" scatter.data_source.data['y'] = y\n",
"\n",
" push_notebook()\n",
" sleep(.05)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 56 - lzw.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def lzw_encode(data):\n",
" code, code_bits = {bytes([i]): i for i in range(256)}, 8\n",
" buffer, buffer_bits = 0, 0\n",
" index, aux = 0, []\n",
"\n",
" while index < len(data):\n",
" # find word\n",
" for j in range(index + 1, len(data) + 1):\n",
" word = data[index:j]\n",
"\n",
" # store word\n",
" if word not in code:\n",
" code[word] = len(code)\n",
" word = word[:-1]\n",
" break\n",
"\n",
" # write buffer\n",
" buffer <<= code_bits\n",
" buffer |= code[word]\n",
" buffer_bits += code_bits\n",
"\n",
" # code length\n",
" if len(code) > 2 ** code_bits:\n",
" code_bits += 1\n",
"\n",
" # shift\n",
" index += len(word)\n",
"\n",
" # buffer alignment\n",
" if index >= len(data) and buffer_bits % 8:\n",
" r = 8 - (buffer_bits % 8)\n",
" buffer <<= r\n",
" buffer_bits += r\n",
"\n",
" # emit output\n",
" if not buffer_bits % 8:\n",
" aux += int.to_bytes(buffer, buffer_bits >> 3, 'big')\n",
" buffer, buffer_bits = 0, 0\n",
"\n",
" return bytes(aux)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def lzw_decode(data):\n",
" code, code_bits = {i: bytes([i]) for i in range(256)}, 8\n",
" buffer, buffer_bits = 0, 0\n",
" index, aux = 0, []\n",
" prefix = b''\n",
"\n",
" while index < len(data) or buffer_bits >= code_bits:\n",
" # read buffer\n",
" while index < len(data) and buffer_bits < code_bits:\n",
" buffer <<= 8\n",
" buffer |= data[index]\n",
" buffer_bits += 8\n",
" index += 1\n",
"\n",
" # find word\n",
" buffer_bits -= code_bits\n",
" key = buffer >> buffer_bits\n",
" buffer &= (1 << buffer_bits) - 1\n",
" word = code.get(key, prefix + prefix[:1])\n",
"\n",
" # store word\n",
" if prefix:\n",
" code[len(code)] = prefix + word[:1]\n",
" prefix = word\n",
"\n",
" # code length\n",
" if len(code) >= 2 ** code_bits:\n",
" code_bits += 1\n",
"\n",
" # emit output\n",
" aux += word\n",
" \n",
" return bytes(aux)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def compress(data):\n",
" encoded = lzw_encode(data.encode('ASCII'))\n",
" decoded = lzw_decode(encoded).decode('ASCII')\n",
" assert data == decoded\n",
" \n",
" print('compression', len(data), '->', len(encoded), 'bytes')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"compression 11 -> 9 bytes\n"
]
}
],
"source": [
"compress('ATGATCATGAG')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"compression 1000 -> 51 bytes\n"
]
}
],
"source": [
"compress('x' * 1000)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"compression 112 -> 84 bytes\n"
]
}
],
"source": [
"compress(\"\"\"\n",
"I wish that I knew what I know now\n",
"When I was younger.\n",
"I wish that I knew what I know now\n",
"When I was stronger.\n",
"\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 57 - quicksort.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def swap(data, i, j):\n",
" data[i], data[j] = data[j], data[i]"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def qsort3(data, left, right):\n",
" # sorted\n",
" if left >= right:\n",
" return\n",
"\n",
" # select pivot\n",
" i = np.random.randint(left, right + 1)\n",
" swap(data, left, i)\n",
" pivot = data[left]\n",
"\n",
" # i ~ points behind left partition\n",
" # j ~ points ahead of right partition\n",
" # k ~ current element\n",
" i, j, k = left, right, left + 1\n",
"\n",
" # split to [left] + [pivot] + [right]\n",
" while k <= j:\n",
" if data[k] < pivot:\n",
" swap(data, i, k)\n",
" i += 1\n",
" elif data[k] > pivot:\n",
" swap(data, j, k)\n",
" j -= 1\n",
" k -= 1\n",
" k += 1\n",
"\n",
" # recursion\n",
" qsort3(data, left, i - 1)\n",
" qsort3(data, j + 1, right)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def qsort(data):\n",
" qsort3(data, 0, len(data) - 1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[6 1 7 9 9 9 9 8 7 6 9 9 6 3 5 4 1 8 1 7 0 1 9 3 1 0 3 2 4 3 1 7 6 0 2 7 0\n",
" 7 9 1 0 4 9 2 3 4 5 9 5 8 9 1 8 2 0 5 4 9 5 3 1 0 1 1 2 3 8 1 4 2 2 4 7 9\n",
" 3 0 0 4 9 3 0 7 0 8 5 8 3 5 9 6 7 6 5 9 3 4 0 1 0 7]\n"
]
}
],
"source": [
"data = np.random.randint(0, 10, 100)\n",
"print(data)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3\n",
" 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7\n",
" 7 7 7 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9]\n"
]
}
],
"source": [
"qsort(data)\n",
"print(data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 58 - integer exponentation.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def _power(x, y, identity=None, op=None):\n",
" p = identity\n",
"\n",
" while y:\n",
" p = op(p, x) if y & 1 else p\n",
" x = op(x, x)\n",
" y >>= 1\n",
"\n",
" return p"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def power(x, y):\n",
" return _power(x, y, identity=type(x)(1), op=type(x).__mul__)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def mod_power(x, y, n):\n",
" return _power(x, y, \n",
" identity=1,\n",
" op=lambda a, b: (a * b) % n)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def matrix_power(x, y):\n",
" return _power(x, y, \n",
" identity=np.eye(x.shape[0], dtype=x.dtype),\n",
" op=np.ndarray.__matmul__)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(1267650600228229401496703205376, 1267650600228229401496703205376)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"power(2, 100), 2 ** 100"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(1.2676506002282294e+30, 1.2676506002282294e+30)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"power(2., 100), 2. ** 100"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(562, 562)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mod_power(2, 100, 1001), (2 ** 100) % 1001"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 1, 1000],\n",
" [ 0, 1]])"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"matrix_power(np.array([[1, 1], [0, 1]]), 1000)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 2.67041885e+111, 3.44456780e+111, 4.21871675e+111],\n",
" [ 8.19736832e+111, 1.05737686e+112, 1.29501688e+112],\n",
" [ 1.37243178e+112, 1.77029694e+112, 2.16816209e+112]])"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"matrix_power(np.arange(9, dtype=float).reshape(3, 3), 100)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 59 - colored tiling.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from collections import deque"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def add(wall, row, col, tile):\n",
" assert np.all(wall[row, col, :] == -1)\n",
" \n",
" # check neighbours if the tile fits\n",
" for i, j, m, n in [[-1, 0, 3, 0], [1, 0, 0, 3], [0, -1, 1, 2], [0, 1, 2, 1]]:\n",
" t = wall[row + i, col + j, m]\n",
" if t != -1 and t != tile[n]:\n",
" return False\n",
"\n",
" # add the tile\n",
" wall[row, col, :] = tile\n",
" return True"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def remove(wall, row, col):\n",
" wall[row, col, :] = -1"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def solve(wall, tiles, row=1, col=1):\n",
" # carry on the next row\n",
" if col == wall.shape[1] - 1:\n",
" row += 1\n",
" col = 1\n",
"\n",
" # solution found\n",
" if row == wall.shape[0] - 1:\n",
" return True\n",
"\n",
" # try each tile\n",
" for i in range(len(tiles)):\n",
" tile = tiles.popleft()\n",
"\n",
" if add(wall, row, col, tile):\n",
" # backtrack\n",
" if solve(wall, tiles, row, col + 1):\n",
" return True\n",
" remove(wall, row, col)\n",
"\n",
" tiles.append(tile)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## wall"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def make_wall(rows, cols):\n",
" # create wall\n",
" wall = np.zeros((rows, cols, 4), dtype=int) - 1\n",
"\n",
" # randomize wall edges\n",
" wall[-1, :, 0] = np.random.randint(0, 2, cols)\n",
" wall[:, 0, 1] = np.random.randint(0, 2, rows)\n",
" wall[:, -1, 2] = np.random.randint(0, 2, rows)\n",
" wall[0, :, 3] = np.random.randint(0, 2, cols)\n",
"\n",
" return wall"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def print_wall(wall):\n",
" chars = np.array(list('01 '))\n",
" tile = lambda i, j: ''.join(chars[wall[i, j, :]])\n",
"\n",
" # print rows\n",
" for i in range(wall.shape[0]):\n",
" row = [tile(i, j)[:2] for j in range(wall.shape[1])]\n",
" print(' '.join(row))\n",
" row = [tile(i, j)[2:] for j in range(wall.shape[1])]\n",
" print(' '.join(row))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"wall = make_wall(7, 7)\n",
"tiles = deque(np.random.randint(0, 2, (30, 4)))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 0 \n",
" 1 0 0 0 0 1 00\n",
" 1 01 00 00 01 10 \n",
" 10 11 01 00 11 0 \n",
" 0 00 10 10 01 10 \n",
" 00 01 01 01 11 0 \n",
" 1 01 10 11 11 10 \n",
" 10 10 00 10 11 0 \n",
" 1 00 00 00 00 10 \n",
" 10 01 00 01 01 0 \n",
" 0 00 10 01 10 11 \n",
" 00 00 01 10 01 1 \n",
"00 0 0 1 0 1 1 \n",
" 0 \n"
]
}
],
"source": [
"solve(wall, tiles)\n",
"print_wall(wall)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 60 - bloom filter.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from collections import deque\n",
"from bitarray import bitarray"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def ihash(x):\n",
" h = 86813\n",
" while True:\n",
" for i in x:\n",
" h = ((h + i) * 127733) % (1 << 32)\n",
" yield h"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def bloom_filter(array_bytes, k):\n",
" array = bitarray(array_bytes * 8)\n",
" array.setall(0)\n",
"\n",
" def _hash(x):\n",
" for _, h in zip(range(k), ihash(x)):\n",
" yield h % len(array)\n",
" \n",
" def _add(x):\n",
" for h in _hash(x):\n",
" array[h] = 1\n",
"\n",
" def _contains(x):\n",
" return all(array[h] for h in _hash(x))\n",
"\n",
" return _add, _contains"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def measure_accuracy(A, B, array_bytes, k):\n",
" add, contains = bloom_filter(array_bytes, k)\n",
" \n",
" # store A\n",
" deque((add(x) for x in A), 0)\n",
"\n",
" # find false positives in B\n",
" fp = sum(contains(x) for x in B)\n",
"\n",
" # result\n",
" acc = 1 - fp / len(B)\n",
" print('{} hashes, {} false positives, {:.4f} accuracy'.format(k, fp, acc))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(999891, 999659)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n = 10 ** 6\n",
"A = set(map(tuple, np.random.randint(0, 256, (n, 4))))\n",
"B = set(map(tuple, np.random.randint(0, 256, (n, 4)))) - A\n",
"len(A), len(B)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 hashes, 117551 false positives, 0.8824 accuracy\n",
"2 hashes, 66855 false positives, 0.9331 accuracy\n",
"3 hashes, 40262 false positives, 0.9597 accuracy\n",
"4 hashes, 61965 false positives, 0.9380 accuracy\n"
]
}
],
"source": [
"for k in [1, 2, 3, 4]:\n",
" measure_accuracy(A, B, n, k)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 hashes, 30693 false positives, 0.9693 accuracy\n",
"2 hashes, 5479 false positives, 0.9945 accuracy\n",
"4 hashes, 937 false positives, 0.9991 accuracy\n",
"6 hashes, 326 false positives, 0.9997 accuracy\n",
"8 hashes, 454 false positives, 0.9995 accuracy\n"
]
}
],
"source": [
"for k in [1, 2, 4, 6, 8]:\n",
" measure_accuracy(A, B, n * 4, k)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 61 - hanoi tower II.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def get_rods(move, towers, left, middle, right):\n",
" if towers:\n",
" if (move << 1) & (1 << towers):\n",
" right.append(towers)\n",
" get_rods(move, towers - 1, middle, left, right)\n",
" else:\n",
" left.append(towers)\n",
" get_rods(move, towers - 1, left, right, middle)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def get_move(towers, left, middle, right):\n",
" if not towers:\n",
" return 0\n",
" if not left or right and left[0] < right[0]:\n",
" move = 1 << (towers - 1)\n",
" return move + get_move(towers - 1, middle, left, right[1:])\n",
" else:\n",
" return get_move(towers - 1, left[1:], right, middle)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def hanoi(towers):\n",
" for i in range(2 ** towers):\n",
" rods = [], [], []\n",
" get_rods(i, towers, *rods)\n",
" move = get_move(towers, *rods)\n",
" print('{:2} moves -- {} {} {}'.format(move, *rods))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 0 moves -- [2, 1] [] []\n",
" 1 moves -- [2] [1] []\n",
" 2 moves -- [] [1] [2]\n",
" 3 moves -- [] [] [2, 1]\n"
]
}
],
"source": [
"hanoi(2)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 0 moves -- [3, 2, 1] [] []\n",
" 1 moves -- [3, 2] [] [1]\n",
" 2 moves -- [3] [2] [1]\n",
" 3 moves -- [3] [2, 1] []\n",
" 4 moves -- [] [2, 1] [3]\n",
" 5 moves -- [1] [2] [3]\n",
" 6 moves -- [1] [] [3, 2]\n",
" 7 moves -- [] [] [3, 2, 1]\n"
]
}
],
"source": [
"hanoi(3)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 0 moves -- [4, 3, 2, 1] [] []\n",
" 1 moves -- [4, 3, 2] [1] []\n",
" 2 moves -- [4, 3] [1] [2]\n",
" 3 moves -- [4, 3] [] [2, 1]\n",
" 4 moves -- [4] [3] [2, 1]\n",
" 5 moves -- [4, 1] [3] [2]\n",
" 6 moves -- [4, 1] [3, 2] []\n",
" 7 moves -- [4] [3, 2, 1] []\n",
" 8 moves -- [] [3, 2, 1] [4]\n",
" 9 moves -- [] [3, 2] [4, 1]\n",
"10 moves -- [2] [3] [4, 1]\n",
"11 moves -- [2, 1] [3] [4]\n",
"12 moves -- [2, 1] [] [4, 3]\n",
"13 moves -- [2] [1] [4, 3]\n",
"14 moves -- [] [1] [4, 3, 2]\n",
"15 moves -- [] [] [4, 3, 2, 1]\n"
]
}
],
"source": [
"hanoi(4)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 0 moves -- [5, 4, 3, 2, 1] [] []\n",
" 1 moves -- [5, 4, 3, 2] [] [1]\n",
" 2 moves -- [5, 4, 3] [2] [1]\n",
" 3 moves -- [5, 4, 3] [2, 1] []\n",
" 4 moves -- [5, 4] [2, 1] [3]\n",
" 5 moves -- [5, 4, 1] [2] [3]\n",
" 6 moves -- [5, 4, 1] [] [3, 2]\n",
" 7 moves -- [5, 4] [] [3, 2, 1]\n",
" 8 moves -- [5] [4] [3, 2, 1]\n",
" 9 moves -- [5] [4, 1] [3, 2]\n",
"10 moves -- [5, 2] [4, 1] [3]\n",
"11 moves -- [5, 2, 1] [4] [3]\n",
"12 moves -- [5, 2, 1] [4, 3] []\n",
"13 moves -- [5, 2] [4, 3] [1]\n",
"14 moves -- [5] [4, 3, 2] [1]\n",
"15 moves -- [5] [4, 3, 2, 1] []\n",
"16 moves -- [] [4, 3, 2, 1] [5]\n",
"17 moves -- [1] [4, 3, 2] [5]\n",
"18 moves -- [1] [4, 3] [5, 2]\n",
"19 moves -- [] [4, 3] [5, 2, 1]\n",
"20 moves -- [3] [4] [5, 2, 1]\n",
"21 moves -- [3] [4, 1] [5, 2]\n",
"22 moves -- [3, 2] [4, 1] [5]\n",
"23 moves -- [3, 2, 1] [4] [5]\n",
"24 moves -- [3, 2, 1] [] [5, 4]\n",
"25 moves -- [3, 2] [] [5, 4, 1]\n",
"26 moves -- [3] [2] [5, 4, 1]\n",
"27 moves -- [3] [2, 1] [5, 4]\n",
"28 moves -- [] [2, 1] [5, 4, 3]\n",
"29 moves -- [1] [2] [5, 4, 3]\n",
"30 moves -- [1] [] [5, 4, 3, 2]\n",
"31 moves -- [] [] [5, 4, 3, 2, 1]\n"
]
}
],
"source": [
"hanoi(5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 62 - linked-list cycle.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def detect_cycle(head):\n",
" if not head:\n",
" return\n",
" \n",
" # cycle detection: 2k = k (mod C)\n",
" p, q = head, head.next\n",
" while q and p is not q:\n",
" p, q = p.next, q.next and q.next.next\n",
"\n",
" if p is q:\n",
" # cycle removal: k + T = 0 (mod C)\n",
" p = head\n",
" while p is not q.next:\n",
" p, q = p.next, q.next\n",
" \n",
" # fix the last link\n",
" q.next = None\n",
" return q"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## utilities"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class Node:\n",
" def __init__(self, **kwargs):\n",
" self.__dict__ = kwargs\n",
" \n",
" def __repr__(self):\n",
" return str(self.__dict__)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def linked_list_with_cycle(length, cycle):\n",
" head, tail = None, None\n",
" \n",
" for i in range(length):\n",
" # prepend head\n",
" head = Node(value=length - i, next=head)\n",
" tail = tail or head\n",
" \n",
" # make cycle of length C\n",
" if i + 1 == cycle:\n",
" tail.next = head\n",
" \n",
" return head"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'value': 1, 'next': {'value': 2, 'next': {'value': 3, 'next': {'value': 4, 'next': {'value': 5, 'next': {'value': 6, 'next': {'value': 7, 'next': {'value': 8, 'next': {'value': 9, 'next': {'value': 10, 'next': {...}}}}}}}}}}}\n"
]
},
{
"data": {
"text/plain": [
"{'value': 10, 'next': None}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"linked_list = linked_list_with_cycle(10, 4)\n",
"print(linked_list)\n",
"detect_cycle(linked_list)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'value': 5, 'next': None}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"linked_list = linked_list_with_cycle(5, 1)\n",
"detect_cycle(linked_list)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'value': 10, 'next': None}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"linked_list = linked_list_with_cycle(10, 5)\n",
"detect_cycle(linked_list)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'value': 25, 'next': None}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"linked_list = linked_list_with_cycle(25, 25)\n",
"detect_cycle(linked_list)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 63 - zig-zag.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def zig_zag_index(k, n):\n",
" # upper side of interval\n",
" if k >= n * (n + 1) // 2:\n",
" i, j = zig_zag_index(n * n - 1 - k, n)\n",
" return n - 1 - i, n - 1 - j\n",
"\n",
" # lower side of interval\n",
" i = int((np.sqrt(1 + 8 * k) - 1) / 2)\n",
" j = k - i * (i + 1) // 2\n",
" return (j, i - j) if i & 1 else (i - j, j)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def zig_zag_value(i, j, n):\n",
" # upper side of interval\n",
" if i + j >= n:\n",
" return n * n - 1 - zig_zag_value(n - 1 - i, n - 1 - j, n)\n",
"\n",
" # lower side of interval\n",
" k = (i + j) * (i + j + 1) // 2\n",
" return k + i if (i + j) & 1 else k + j"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"n = 10"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0, 1, 5, 6, 14, 15, 27, 28, 44, 45],\n",
" [ 2, 4, 7, 13, 16, 26, 29, 43, 46, 63],\n",
" [ 3, 8, 12, 17, 25, 30, 42, 47, 62, 64],\n",
" [ 9, 11, 18, 24, 31, 41, 48, 61, 65, 78],\n",
" [10, 19, 23, 32, 40, 49, 60, 66, 77, 79],\n",
" [20, 22, 33, 39, 50, 59, 67, 76, 80, 89],\n",
" [21, 34, 38, 51, 58, 68, 75, 81, 88, 90],\n",
" [35, 37, 52, 57, 69, 74, 82, 87, 91, 96],\n",
" [36, 53, 56, 70, 73, 83, 86, 92, 95, 97],\n",
" [54, 55, 71, 72, 84, 85, 93, 94, 98, 99]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"M = np.zeros((n, n), dtype=int)\n",
"for i in range(n):\n",
" for j in range(n):\n",
" M[i, j] = zig_zag_value(i, j, n)\n",
"M"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0, 1, 5, 6, 14, 15, 27, 28, 44, 45],\n",
" [ 2, 4, 7, 13, 16, 26, 29, 43, 46, 63],\n",
" [ 3, 8, 12, 17, 25, 30, 42, 47, 62, 64],\n",
" [ 9, 11, 18, 24, 31, 41, 48, 61, 65, 78],\n",
" [10, 19, 23, 32, 40, 49, 60, 66, 77, 79],\n",
" [20, 22, 33, 39, 50, 59, 67, 76, 80, 89],\n",
" [21, 34, 38, 51, 58, 68, 75, 81, 88, 90],\n",
" [35, 37, 52, 57, 69, 74, 82, 87, 91, 96],\n",
" [36, 53, 56, 70, 73, 83, 86, 92, 95, 97],\n",
" [54, 55, 71, 72, 84, 85, 93, 94, 98, 99]])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"M = np.zeros((n, n), dtype=int)\n",
"for k in range(n * n):\n",
" M[zig_zag_index(k, n)] = k\n",
"M"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 64 - k-clique.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from itertools import combinations\n",
"import numpy as np\n",
"import networkx as nx\n",
"import matplotlib.pyplot as plt\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def k_cliques(graph):\n",
" # 2-cliques\n",
" cliques = [{i, j} for i, j in graph.edges() if i != j]\n",
" k = 2\n",
" \n",
" while cliques:\n",
" # result\n",
" yield k, cliques\n",
" \n",
" # merge k-cliques into (k+1)-cliques\n",
" cliques_1 = set()\n",
" for u, v in combinations(cliques, 2):\n",
" w = u ^ v\n",
" if len(w) == 2 and graph.has_edge(*w):\n",
" cliques_1.add(tuple(u | w))\n",
"\n",
" # remove duplicates\n",
" cliques = list(map(set, cliques_1))\n",
" k += 1 "
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def print_cliques(graph):\n",
" for k, cliques in k_cliques(graph):\n",
" print('%d-cliques: #%d, %s ...' % (k, len(cliques), cliques[:3]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph #1"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"nodes = 6\n",
"graph = nx.Graph()\n",
"graph.add_nodes_from(range(nodes))\n",
"graph.add_edges_from(combinations(range(nodes), 2))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\nhpOTExMmTODzzz83+/9DX19fWrZsScuWLfP1edkQZtmSk5PZu3dv+nL13bt3sbe3p3///rz//vuU\nLl06089dvHiRkSNHsmrVKtzd3ZkzZ062ew9GjhzJybfeovjgwTBmTFpJTkX5r00kpCVkVU17xjx4\nsEXMmA0kOYtHGEp0enl5SYnOAjJnco6Li2P06NHMmDGDZ555hrVr1/Lqq6+a5d4ZnTx5kpCQkAI1\n4ZANYZbn7t27bN68mbCwMDZs2MCLL76Ig4MDixcv5vXXX8+2reiNGzcYM2YMc+fOxdnZmdOnT/Ps\ns89me7+DBw8yZ84cjh49ivL882lNLGJj00pyHj+eVmCkfPm041Jdu+p+81dmJDmLR8ybN4979+5J\niU4jMMcMUFVVli5dysCBA3n33Xf5888/6dOnD9HR0WZPzqqq0rt3b7y9vTNdrsytuLg4KlWqZMTI\nhClcuHAhfXa8b98+mjdvjr29PX5+frmqBHf//n0mTpzIxIkT+fLLL7M8ZfC4hw8f4ujoSFBQ0KO/\nzypW1HWt7LyS5CzSGUp0btmyRZoOGIGpZ86HDh3C1dWVhw8fsmzZMpo3bw78t2P7yy+/NNm9M7Ns\n2TJu3rxJr169CjSOPHPWp9TUVA4ePJj+/Pjy5cu0a9eOnj17snLlSsqUKZOrcR4+fMjMmTMZNWoU\n77//Pvv27aN27dq5jsPPz49atWoV+v0wkpxFOjc3NxwdHaVEpxGkpKSQmJiY5fO1grh69SpeXl6s\nX78ePz8/HB0dH1k2tLOzY8WKFUa/b3bu3r2Lp6cnP//8c4GPackzZ/2Ij49n27ZthIWFsW7dOsqX\nL4+DgwPTp0/nf//7X55+iE9JSWHx4sX4+PjQsGFDNm3alOe/aw4fPsysWbP4888/C/1+GEnOAoD1\n69cTHh5OSEiI1qEUCobZnzH/AklMTGTKlCmMGTOGLl26EBUVRdmyZZ+4zs7ODm9vb6PdNzeGDx/O\nxx9/zNtvv13gsWTmrK0rV66wbt06QkND2bVrF02aNMHe3p6BAwfmaYZroKoqa9asYejQoZQvX55F\nixbxzjvv5HmcxMREunbtyvjx46lSpUqeP29pJDkL7t27R69evaREpxEZO8Fs3LgRd3d3atasyZ49\ne6hXr16W19apU4e///6bhIQEk8zcH3f8+HEWL15MRESEUcaTDWHmpaoqx44dS1+ujomJ4eOPP6ZT\n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\nB8AeLjzcBUDuAAAAAElFTkSuQmCC\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(8, 8))\n",
"nx.draw_networkx(graph)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2-cliques: #15, [{0, 1}, {0, 2}, {0, 3}] ...\n",
"3-cliques: #20, [{2, 3, 5}, {3, 4, 5}, {0, 3, 4}] ...\n",
"4-cliques: #15, [{0, 1, 3, 5}, {0, 1, 2, 3}, {0, 1, 3, 4}] ...\n",
"5-cliques: #6, [{1, 2, 3, 4, 5}, {0, 1, 3, 4, 5}, {0, 1, 2, 3, 5}] ...\n",
"6-cliques: #1, [{0, 1, 2, 3, 4, 5}] ...\n"
]
}
],
"source": [
"print_cliques(graph)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph #2"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"nodes, edges = 10, 50\n",
"graph = nx.Graph()\n",
"graph.add_nodes_from(range(nodes))\n",
"graph.add_edges_from(np.random.randint(0, nodes, (edges, 2)))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAsYAAAKvCAYAAABpkwknAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3Xdc1WX/x/HX14nkzJkDV+JKBcU9IreZuUducwVoZtpQ\nf2V3S7NhOUDNlWCauTVnkitzICLO3KLeOVOTxIFcvz8wbytUhHP4Mt7Px+M8vDnn+l7fN49b6cP1\nvYZljEFEREREJK1LZ3cAEREREZHkQIWxiIiIiAgqjEVEREREABXGIiIiIiKACmMREREREUCFsYiI\niIgIoMJYRERERARQYSwiIiIiAqgwFhEREREBIINdN86TJ48pVqyYXbcXERERkTRi586dF40xeR/V\nzrbCuFixYoSEhNh1exERERFJIyzLOhmfdppKISIiIiKCCmMREREREUCFsYiIiIgIoMJYRERERARQ\nYSwiIiIiAqgwFhEREREBVBiLiIiIiAAqjEVEREREABXGIiIiIiKACmMREREREUCFsYiIiIgIoMJY\nRERERASIZ2FsWVZTy7J+tSzriGVZb8fx+RuWZYXdfe21LOuOZVlPOj6uiIiIiIhzPLIwtiwrPTAR\naAaUA16yLKvc/W2MMZ8aYzyMMR7AMGCDMeZ3ZwQWEREREXGG+IwYVwOOGGOOGWNuAXOBlg9p/xIw\nxxHhRERERESSSnwK40LAqfu+Pn33vX+xLMsVaAosSHw0EREREZGk4+jFdy2Anx80jcKyrH6WZYVY\nlhVy4cIFB99aRERERCTh4lMYnwGK3Pd14bvvxaUTD5lGYYyZYozxMsZ45c2bN/4pRUREREScLD6F\n8Q6glGVZxS3LykRs8bv0n40sy8oBPAsscWxEERERERHny/CoBsaYaMuyBgCrgfTAdGPMPsuyXrn7\n+aS7TVsDa4wxfzotrYiIiIiIk1jGGFtu7OXlZUJCQmy5t4iIiIikHZZl7TTGeD2qnU6+ExERERFB\nhbGIiIiICKDCWEREREQEUGEsIiIiIgKoMBYRERERAVQYi4iIiIgAKoxFRERERAAVxiIiIiIigApj\nERERERFAhbGIiIiICKDCWEREREQEUGEsIiIiIgKoMBYRERERAVQYi4iIiIgAKoxFRERERAAVxiIi\nIiIigApjERERERFAhbGIiIiICKDCWEREREQEUGEsIiIiIgKoMBYRERERAVQYi4iIiIgAKoxFRERE\n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\n863lEBEp1Fq3bk3lypUZP36801HypXfv3gBMnDjR4STZrLW899571KhRg4oVK7J169Z8F8VQOPqM\ndZZx4bJlyxYmTZrErFmzMMY4HUekwLRiLCJuY4xhypQp1K5dm0cffZTbb7/d6UguS0lJYc2aNfTv\n398nXln/008/0bNnT3bu3MmHH35I3bp1CzxmvXr1+P777/npp58oV66cG1K6n84yLjzOnDlDbGws\nEyZM4Oabb3Y6johbaMVYRNzq1ltvZciQIYXqbOOsrCzi4uIIDQ3l6aefdjSLtZa3336bGjVqUKVK\nFTZv3uyWohiyi87IyEiWLl3qlvE8QT3Ghcezzz5LlSpVaN++vdNRRNxGhbGIuF3fvn05cuQIb7/9\nttNRXPLWW2+xb98+XnvtNa6++mrHchw6dIjWrVvzwgsv8PHHHzN27FhCQ0PdOoevt1PoLOPC4bPP\nPiMpKYnp06erhUL8igpjEXG7kJAQZsyYwcCBAzl+/LjTca4oPT2dvn37UqlSJTp06OBIBmst8+bN\n45577uHuu+9m06ZN1KlTxyNztWjRgqVLl5KZmemR8QtKK8a+79SpU3Tq1Ilp06ZRtmxZp+OIuJUK\nYxHxiLp169KmTRuGDBnidJQrGjduHGlpacyePduRla+DBw/y8MMPM378eBYvXszo0aM9eiJDhQoV\nuP7669m8ebPH5igIbb7zfYMHD6ZBgwa0atXK6SgibqfCWEQ8ZsyYMXz88cd89tlnTkfJ0eHDh3np\npZeIjo7mvvvu8+rc1lrmzp3LPffcQ61atdi4cSO1a9f2yty+3E6hzXe+bdmyZfzrX//ymZNbRNxN\nhbGIeEzp0qV59dVX6d69u08WO/3798day5QpU7w67/79+/nzn//Mq6++ytKlSxk5cqRXz3329cJY\nK8a+6ZdffqFbt27MmjWLa665xuk4Ih6hwlhEPOqRRx6hQoUKTJgwwekov/PNN98wf/58+vfvT/ny\n5b0yp7WWWbNmce+991KvXj02bNjAPffc45W5L9awYUN27Njhk/3faqXwXX369OEvf/kLUVFRTkcR\n8RidYywiHmWMYerUqdSpU4f/+7//47bbbnM6EgBdu3bl6quvZvjw4V6Zb9++fTzxxBMcPXqUTz/9\nlBo1anhl3pyEhobSsGFDli9fziOPPOJYjpyolcI3ffDBB6xbt46tW7c6HUXEo7RiLCIeV6lSJZ56\n6il69uzpE2cbJycns2XLFqZMmULx4sU9Ope1ltdff53atWsTERHB+vXrHS2KL/DVdgq1Uview4cP\n07NnT+bOnevocYYi3qDCWES8YsCAARw4cID33nvP0RyZmZl069aNypUr065dO4/O9cMPP9C8eXNm\nzpzJypUreeaZZwgJCfHonK66UBj7wl9ULqZzjH2LtZb4+Hg6depUoNeRixQWKoxFxCsunG08YMAA\nfvnlF8dyJCQkcPjwYebNm+ex49mysrKYNm0a4eHhREZGsm7dOu6++26PzJVft99+O6GhoXz11VdO\nR/kdrRj7ln/84x/s2rWLkSNHOh1FxCvUYywiXnP//ffzl7/8haeffpqEhASvz3/y5EmGDh1KdHS0\nx45G27t3L926dePUqVOsWbOGatWqeWSegjLGEB0dzZIlS6hevbrTcX6jzXe+4z//+Q8DBw4kOTnZ\no2dri/gSrRiLiFeNHTuWhQsXsn79eq/PPWTIEDIyMpg5c6bbx87KymLKlCnUqVOHmJgYPvvsM58t\nii/wxT5jbb7zDdZaunXrRp8+fbj33nudjiPiNVoxFhGvCgsL45VXXqF79+5s3LjRaz23Bw4cYObM\nmQwcOJAbbrjBrWN///33dO3alXPnzrF27VruvPNOt47vKU2aNKFdu3b8+uuvlCxZ0uk4gFopfMX0\n6dM5ceKEz7+5UsTdtGIsIl73t7/9jRtuuIHXXnvNa3NeOJ5txIgRbhszKyuLiRMnUrduXVq2bElK\nSkqhKYoBSpQoQd26dVm5cqXTUX6jVgrn7d69m+HDhzN37lyKFNH6mQQW/RsvIl5njCEhIYG6devy\nyCOPULFiRY/Ot3HjRlasWEFiYqLbeiV37dpF165dAfj888+pUqWKW8b1tpiYGD755BMefvhhp6MA\naqVwWmZmJp07d2bYsGGF6i95Iu6iFWMRcUTlypUZMGAATz75pEePDLPW0r59e2677TYee+yxAo+X\nmZnJhAkTqF+/Po888girV68utEUx8NsGPF85tk2tFM6aMGECISEh9OnTx+koIo7QirGIOGbQoEG8\n+eabLFiwgL/+9a8emePdd9/l+++/58svvyzw8WzffvstXbp0ISQkhPXr13P77be7KaVz/vSnP3Hu\n3Dl27drlEwW+zjF2zldffcVLL73Ehg0bCArSupkEJv2bLyKOKVq0KNOnT6dfv36cOHHC7eOfO3eO\n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\niAgA/w9DTlaBjiYkyQAAAABJRU5ErkJggg==\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(12, 12))\n",
"nx.draw_networkx(graph)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2-cliques: #27, [{0, 6}, {0, 1}, {0, 4}] ...\n",
"3-cliques: #31, [{8, 2, 3}, {8, 1, 7}, {0, 8, 2}] ...\n",
"4-cliques: #13, [{0, 8, 4, 6}, {0, 1, 4, 8}, {8, 9, 2, 3}] ...\n",
"5-cliques: #2, [{0, 2, 4, 8, 9}, {0, 2, 4, 6, 8}] ...\n"
]
}
],
"source": [
"print_cliques(graph)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"## graph #3"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"nodes, edges = 100, 1000\n",
"graph = nx.Graph()\n",
"graph.add_nodes_from(range(nodes))\n",
"graph.add_edges_from(np.random.randint(0, nodes, (edges, 2)))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2-cliques: #914, [{0, 58}, {0, 1}, {0, 90}] ...\n",
"3-cliques: #1126, [{33, 26, 34}, {4, 31, 84}, {88, 64, 31}] ...\n",
"4-cliques: #142, [{18, 3, 76, 85}, {27, 28, 77, 55}, {44, 37, 60, 92}] ...\n"
]
}
],
"source": [
"print_cliques(graph)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 65 - floyd-warshall.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from itertools import product\n",
"import numpy as np\n",
"import networkx as nx\n",
"import matplotlib.pyplot as plt\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def floyd(graph):\n",
" # initialize matrix\n",
" distance = nx.adjacency_matrix(graph).todense().astype(float)\n",
" distance[distance == 0] = np.inf\n",
" np.fill_diagonal(distance, 0)\n",
" \n",
" # find shortest paths\n",
" for k, i, j in product(range(len(graph)), repeat=3):\n",
" distance[i, j] = min(distance[i, j], distance[i, k] + distance[k, j])\n",
" \n",
" # negative cycle detection\n",
" if i == j and distance[i, j] < 0:\n",
" return k, i, 'negative cycle detected'\n",
"\n",
" # shortest paths\n",
" return {\n",
" (i, j): distance[i, j]\n",
" for i, j in product(range(len(graph)), repeat=2)\n",
" if i != j and not np.isinf(distance[i, j])\n",
" }"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def generate_graph(n, edge_prob=.5, pos_weight_prob=.2):\n",
" graph = nx.DiGraph()\n",
" graph.add_nodes_from(range(n))\n",
" \n",
" for u, v in product(range(n), repeat=2):\n",
" if u != v and np.random.rand() < edge_prob:\n",
" weight = [-1, 1][np.random.rand() < pos_weight_prob]\n",
" graph.add_edge(u, v, weight=weight)\n",
" \n",
" return graph"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def draw_graph(graph):\n",
" cm = {-1: 'red', 1: 'black'}\n",
" colors = [cm[e['weight']] for (u, v, e) in graph.edges(data=True)]\n",
"\n",
" plt.figure(figsize=(8, 8))\n",
" plt.axis('off')\n",
"\n",
" layout = nx.spring_layout(graph)\n",
" nx.draw_networkx_nodes(graph, layout, node_color='steelblue', node_size=520)\n",
" nx.draw_networkx_edges(graph, layout, edge_color=colors)\n",
" nx.draw_networkx_labels(graph, layout, font_color='white')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"graph = generate_graph(5, edge_prob=.4, pos_weight_prob=.7)\n",
"draw_graph(graph)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{(0, 1): 0.0,\n",
" (0, 2): -1.0,\n",
" (0, 3): -2.0,\n",
" (0, 4): -1.0,\n",
" (1, 0): 1.0,\n",
" (1, 2): 0.0,\n",
" (1, 3): -1.0,\n",
" (1, 4): 0.0,\n",
" (2, 0): 2.0,\n",
" (2, 1): 1.0,\n",
" (2, 3): -1.0,\n",
" (2, 4): 0.0,\n",
" (3, 4): 1.0}"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"floyd(graph)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\nsnJ0xoqNZrLn3beyz/dues/5hz3/pVefsWBlx3N3pGwQmz3Ox36f0iK1pDe/Kkn9yHCWKtUu7U/Q\ncYb1po/eln1f9F8/mbnFo/8Y2JyxM+uxciUQy4i4Azhm0eue8T3g+ZSTycZr0TqfcouUO7EljWE4\nSx0iYgvgemC3x+56AHse9UpiZCNmjIxM5jGjmfnQ7f997s0/ueGz+43z/v8BJ2bmp7pRs6TBYzhL\nNRGx0za7z7lpj799xWYjG89c/w+sxehDD3LLJ9/Cz24f01V1FfDCzFy6oXVKGlyGs1Qz78yFT8qV\no7fGjJGpJ3Nl9KEHWfLuU1l+/9LllFux3utlFZLWxz5nqcO8MxfOAC6NGSP1HdVTEiMbsfdxr/vd\nyMab7JOZ5xrMkiaiK/8BkgbIIcDOwLhfMh8x64kc+hfbscNjt+B/v30Pb/nMzet82IyRETbfevuV\nc0+/bHvAm6QkTYgzZ2ms0yi7ssf1i9+u4JIv38Wim34y4QdGxOaseY63JK2V4SxVqgNGDmYd7VLX\nfWcpX7njPn6zfMI3QVI97+Dq+ZK0XoaztNqelANGemE5sEePni1pwBjO0mq7sq5DRjZMALv16NmS\nBozhLK22CWvZCNYFI9XzJWm9DGdptQeB0R49e7R6viStl+EsrXY7sM4+5BkRbDQygxkzYvWfx4RW\nwhO4rRtFShp89jlLq90CbLquv+C5Bz6JEw7e+eHXh+y1HRctvpOLr/3u+p69KXDrBlcoaSh4fKfU\nYd6ZCz8PzKW7G8MSuObqMxYc2sVnShpgLmtLY50NLOvyM5cBb+7yMyUNMMNZGusLwJ10b2PYaPW8\na7r0PElDwGVtqWbemQufBHwL2KwLj/sdsNfVZyz4XheeJWlIOHOWaq4+Y8FdwAls+Glhy4ETDGZJ\nk+XMWVqLeWcuPBK4CJjJ5A4nGQVWUIL58l7UJmmwGc7SOlRL3JdSrpHcnHXv4k7K5q87gb9zxixp\nqgxnaT3mnblwBqW96lWUW6uWU0J6hDJLTkof82LKruxrrj5jwcpmqpU0CAxnaRKqax/3oFxisQnl\nSM7bgFuvPmPBiiZrkzQ4DGdJklrG3dqSJLWM4SxJUssYzpIktYzhLElSyxjOkiS1jOEsSVLLGM6S\nJLWM4SxJUssYzpIktYzhLElSyxjOkiS1jOEsSVLLGM6SJLWM4SxJUssYzpIktYzhLElSyxjOkiS1\njOEsSVLLGM6SJLWM4SxJUssYzpIktYzhLElSyxjOkiS1jOEsSVLLGM6SJLWM4SxJUssYzpIktYzh\nLElSyxjOkiS1jOEsSVLLGM6SJLWM4SxJUssYzpIktYzhLElSyxjOkiS1jOEsSVLLGM6SJLWM4SxJ\nUssYzpIktYzhLElSyxjOkiS1jOEsSVLLGM6SJLWM4SxJUssYzpIktYzhLElSyxjOkiS1jOEsSVLL\n/D9/3FiSpU/+XwAAAABJRU5ErkJggg==\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"graph = generate_graph(5, edge_prob=.4, pos_weight_prob=.6)\n",
"draw_graph(graph)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(1, 4, 'negative cycle detected')"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"floyd(graph)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 66 - gram-schmidt.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def gram_schmidt(X):\n",
" O = np.zeros(X.shape)\n",
"\n",
" for i in range(X.shape[1]):\n",
" # orthogonalization\n",
" vector = X[:, i]\n",
" space = O[:, :i]\n",
" projection = vector @ space\n",
" vector = vector - np.sum(projection * space, axis=1)\n",
"\n",
" # normalization\n",
" norm = np.sqrt(vector @ vector)\n",
" vector /= abs(norm) < 1e-8 and 1 or norm\n",
" \n",
" O[:, i] = vector\n",
"\n",
" return O"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# 6 column vectors in 4D\n",
"vectors = np.array([\n",
" [1, 1, 2, 0, 1, 1],\n",
" [0, 0, 0, 1, 2, 1],\n",
" [1, 2, 3, 1, 3, 2],\n",
" [1, 0, 1, 0, 1, 1]\n",
"], dtype=float)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 3., 3., 6., 1., 5., 4.],\n",
" [ 3., 5., 8., 2., 7., 5.],\n",
" [ 6., 8., 14., 3., 12., 9.],\n",
" [ 1., 2., 3., 2., 5., 3.],\n",
" [ 5., 7., 12., 5., 15., 10.],\n",
" [ 4., 5., 9., 3., 10., 7.]])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# check orthogonality\n",
"vectors.T @ vectors"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0.57735, -0. , -0. , -0.30861, 0. , -0. ],\n",
" [ 0. , 0. , 0. , 0.92582, 0. , 0. ],\n",
" [ 0.57735, 0.70711, 0. , 0.1543 , 0. , 0. ],\n",
" [ 0.57735, -0.70711, -0. , 0.1543 , -0. , -0. ]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"orthonormal = gram_schmidt(vectors)\n",
"orthonormal.round(5)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 1., -0., -0., -0., 0., -0.],\n",
" [-0., 1., 0., 0., 0., 0.],\n",
" [-0., 0., 0., 0., 0., 0.],\n",
" [-0., 0., 0., 1., 0., 0.],\n",
" [ 0., 0., 0., 0., 0., 0.],\n",
" [-0., 0., 0., 0., 0., 0.]])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# check orthogonality\n",
"(orthonormal.T @ orthonormal).round(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## QR decomposition"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"matrix = np.array([\n",
" [1, 1, -1],\n",
" [1, 2, 1],\n",
" [1, 3, 0]\n",
"], dtype=float)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0.57735, -0.70711, -0.40825],\n",
" [ 0.57735, -0. , 0.8165 ],\n",
" [ 0.57735, 0.70711, -0.40825]])"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Q = gram_schmidt(matrix)\n",
"Q.round(5)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 1.73205, 3.4641 , 0. ],\n",
" [-0. , 1.41421, 0.70711],\n",
" [ 0. , 0. , 1.22474]])"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"R = Q.T @ matrix\n",
"R.round(5)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 1., 1., -1.],\n",
" [ 1., 2., 1.],\n",
" [ 1., 3., -0.]])"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(Q @ R).round(5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 67 - linked-list mergesort.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from types import SimpleNamespace\n",
"from random import randint"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def _merge(p, q):\n",
" r, s = [Node()] * 2\n",
" \n",
" while p or q:\n",
" if not q or p and p.value < q.value:\n",
" r.next = p\n",
" r, p = r.next, p.next\n",
" else:\n",
" r.next = q\n",
" r, q = r.next, q.next\n",
"\n",
" return s.next"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### recursive"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def mergesort_recursive(head):\n",
" # list is sorted\n",
" if not (head and head.next):\n",
" return head\n",
"\n",
" # make equal split\n",
" p, q, r = head, head.next, None\n",
" while q:\n",
" p, q, r = p.next, q.next and q.next.next, p\n",
" r.next = None\n",
"\n",
" # sort recursively\n",
" p = mergesort_recursive(p)\n",
" q = mergesort_recursive(head)\n",
"\n",
" # merge\n",
" return _merge(p, q)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### iterative"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def mergesort_iterative(head):\n",
" splits = []\n",
"\n",
" while head:\n",
" # sorted list of length 1\n",
" head, p = head.next, head\n",
" p.next = None\n",
" splits.append((1, p))\n",
"\n",
" while len(splits) > 1:\n",
" (i, p), (j, q) = splits[-2:]\n",
" if i != j and head:\n",
" break\n",
" \n",
" # merge\n",
" splits[-2:] = [(i + j, _merge(p, q))]\n",
"\n",
" return splits and splits[0][1] or None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## utilities"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"Node = SimpleNamespace"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def random_linked_list(size, r):\n",
" head = None\n",
" for i in range(size):\n",
" head = Node(value=randint(0, r), next=head)\n",
" return head"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def print_list(head):\n",
" def _iter(head):\n",
" while head:\n",
" yield head.value\n",
" head = head.next\n",
"\n",
" print(list(_iter(head)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[10, 9, 2, 9, 0, 6, 4, 9, 1, 7, 10, 9, 9, 3, 0, 5, 7, 5, 1, 0]\n"
]
}
],
"source": [
"head = random_linked_list(size=20, r=10)\n",
"print_list(head)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[]\n",
"[4, 5, 10]\n",
"[3, 4, 5, 5, 6, 9]\n",
"[0, 1, 3, 3, 4, 4, 8, 8, 8]\n",
"[1, 1, 4, 5, 6, 6, 6, 6, 6, 6, 9, 9]\n",
"[0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 9, 9, 10]\n",
"[0, 1, 2, 3, 4, 4, 4, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 10]\n",
"[0, 1, 1, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 7, 8, 8, 9, 9, 9, 9, 10]\n",
"[0, 1, 1, 2, 2, 2, 2, 3, 3, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 10, 10]\n",
"[0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10]\n"
]
}
],
"source": [
"for i in range(10):\n",
" head = random_linked_list(size=3 * i, r=10)\n",
" head = mergesort_recursive(head)\n",
" print_list(head)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[]\n",
"[4, 4, 5]\n",
"[1, 2, 4, 5, 6, 6]\n",
"[3, 4, 5, 5, 6, 9, 10, 10, 10]\n",
"[0, 2, 3, 3, 5, 6, 7, 8, 8, 8, 10, 10]\n",
"[0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 7, 8, 9, 9, 10]\n",
"[1, 1, 2, 2, 2, 3, 4, 5, 5, 5, 7, 7, 7, 7, 9, 9, 10, 10]\n",
"[0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 6, 6, 7, 9, 10, 10]\n",
"[0, 0, 0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 10, 10]\n",
"[0, 0, 1, 1, 1, 1, 2, 2, 2, 4, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10]\n"
]
}
],
"source": [
"for i in range(10):\n",
" head = random_linked_list(size=3 * i, r=10)\n",
" head = mergesort_iterative(head)\n",
" print_list(head)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 68 - gale-shapley.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from collections import deque, defaultdict"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def stable_match(men, women):\n",
" free_men = deque(men)\n",
" engaged = defaultdict(lambda: None)\n",
"\n",
" while free_men:\n",
" i = free_men.popleft()\n",
"\n",
" # man proposes women according his preferences\n",
" for j in men[i]:\n",
" preference = women[j].index\n",
" fiance = engaged[j]\n",
"\n",
" # woman accepts the better offer\n",
" if not fiance or preference(i) < preference(fiance):\n",
" engaged[j] = i\n",
" fiance and free_men.append(fiance)\n",
" break\n",
"\n",
" return [(m, w) for w, m in engaged.items()]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"men = {\n",
" 'adam': ['claire', 'diana'],\n",
" 'bob': ['diana', 'claire'],\n",
"}\n",
"women = {\n",
" 'claire': ['bob', 'adam'],\n",
" 'diana': ['adam', 'bob'],\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('adam', 'claire'), ('bob', 'diana')]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"stable_match(men, women)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"men = {\n",
" 'adam': ['betty', 'claire', 'diana'],\n",
" 'bob': ['betty', 'claire', 'diana'],\n",
" 'charlie': ['betty', 'claire', 'diana'],\n",
"}\n",
"women = {\n",
" 'betty': ['charlie', 'bob', 'adam'],\n",
" 'claire': ['charlie', 'bob', 'adam'],\n",
" 'diana': ['charlie', 'bob', 'adam'],\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('charlie', 'betty'), ('bob', 'claire'), ('adam', 'diana')]"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"stable_match(men, women)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"men = {\n",
" 'adam': ['diana', 'alice', 'betty', 'claire'],\n",
" 'bob': ['betty', 'claire', 'alice', 'diana'],\n",
" 'charlie': ['betty', 'diana', 'claire', 'alice'],\n",
" 'david': ['claire', 'alice', 'diana', 'betty'],\n",
"}\n",
"women = {\n",
" 'alice': ['david', 'adam', 'charlie', 'bob'],\n",
" 'betty': ['adam', 'charlie', 'bob', 'david'],\n",
" 'claire': ['adam', 'bob', 'charlie', 'david'],\n",
" 'diana': ['david', 'adam', 'charlie', 'bob'],\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"[('adam', 'diana'),\n",
" ('charlie', 'betty'),\n",
" ('bob', 'claire'),\n",
" ('david', 'alice')]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"stable_match(men, women)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 69 - rmsprop.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def gradient_descent(F, dF, x, steps=100, lr=0.001):\n",
" loss = []\n",
" \n",
" for _ in range(steps):\n",
" dx = dF(x)\n",
" x -= lr * dx\n",
" loss.append(F(x))\n",
"\n",
" return x, loss"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def rmsprop(F, dF, x, steps=100, lr=0.001, decay=.9, eps=1e-8):\n",
" loss = []\n",
" dx_mean_sqr = np.zeros(x.shape, dtype=float)\n",
"\n",
" for _ in range(steps):\n",
" dx = dF(x)\n",
" dx_mean_sqr = decay * dx_mean_sqr + (1 - decay) * dx ** 2\n",
" x -= lr * dx / (np.sqrt(dx_mean_sqr) + eps)\n",
" loss.append(F(x))\n",
" \n",
" return x, loss"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def rmsprop_momentum(F, dF, x, steps=100, lr=0.001, decay=.9, eps=1e-8, mu=.9):\n",
" loss = []\n",
" dx_mean_sqr = np.zeros(x.shape, dtype=float)\n",
" momentum = np.zeros(x.shape, dtype=float)\n",
"\n",
" for _ in range(steps):\n",
" dx = dF(x)\n",
" dx_mean_sqr = decay * dx_mean_sqr + (1 - decay) * dx ** 2\n",
" momentum = mu * momentum + lr * dx / (np.sqrt(dx_mean_sqr) + eps)\n",
" x -= momentum\n",
" loss.append(F(x))\n",
"\n",
" return x, loss"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## function"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def F(x):\n",
" residual = A @ x - np.eye(len(A), dtype=float)\n",
" return np.sum(residual ** 2)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def dF(x):\n",
" return 2 * A.T @ (A @ x - np.eye(len(A), dtype=float))"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"A = np.array([\n",
" [2, 5, 1, 4, 6],\n",
" [3, 5, 0, 0, 0],\n",
" [1, 1, 0, 3, 8],\n",
" [6, 6, 2, 2, 1],\n",
" [8, 3, 5, 1, 4],\n",
"], dtype=float)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## optimization"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(array([[ 0.79, -0.01, 0.18, 0.19, -0.08],\n",
" [-0.01, 0.8 , 0. , 0.2 , -0.07],\n",
" [ 0.18, 0. , 0.85, -0.15, 0.07],\n",
" [ 0.19, 0.2 , -0.15, 0.66, 0.13],\n",
" [-0.08, -0.07, 0.07, 0.13, 0.95]]), 0.54691984767143453)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X, loss1 = gradient_descent(F, dF, A * 0, steps=300)\n",
"(A @ X).round(2), loss1[-1]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(array([[ 0.84, -0.05, 0.1 , 0.1 , -0.06],\n",
" [-0.04, 0.82, 0.03, 0.19, -0.03],\n",
" [ 0.12, 0.03, 0.9 , -0.08, 0.03],\n",
" [ 0.15, 0.2 , -0.12, 0.75, 0.06],\n",
" [-0.08, -0.09, 0.04, 0.1 , 0.97]]), 0.32396954419819657)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X, loss2 = rmsprop(F, dF, A * 0, steps=300)\n",
"(A @ X).round(2), loss2[-1]"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(array([[ 0.99, 0.01, 0. , -0.01, 0. ],\n",
" [-0. , 1. , 0. , -0. , 0. ],\n",
" [-0. , 0.01, 1. , -0.01, 0. ],\n",
" [-0.01, 0.01, 0. , 0.99, 0. ],\n",
" [-0.01, 0.01, 0. , -0.01, 1. ]]), 0.00062303887772378397)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X, loss3 = rmsprop_momentum(F, dF, A * 0, steps=300)\n",
"(A @ X).round(2), loss3[-1]"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"d8b01c18-bc74-42bd-8485-9e68ed5f812a\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"d8b01c18-bc74-42bd-8485-9e68ed5f812a\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'd8b01c18-bc74-42bd-8485-9e68ed5f812a' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"d8b01c18-bc74-42bd-8485-9e68ed5f812a\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"d8b01c18-bc74-42bd-8485-9e68ed5f812a\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"plot = figure()\n",
"plot.line(x=range(len(loss1)), y=loss1, color='steelblue', legend='gd')\n",
"plot.line(x=range(len(loss2)), y=loss2, color='green', legend='rmsprop')\n",
"plot.line(x=range(len(loss3)), y=loss3, color='red', legend='rmsprop+momentum')\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 70 - deadlock.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from collections import defaultdict\n",
"from time import sleep\n",
"from threading import Thread, Lock"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class SharedState:\n",
"\n",
" def __init__(self, n):\n",
" self._lock = Lock()\n",
" self._state = defaultdict(int)\n",
" self._resources = [Lock() for _ in range(n)]\n",
"\n",
" def atomic(self, key, value=0):\n",
" with self._lock:\n",
" self._state[key] += value\n",
" return self._state[key]\n",
"\n",
" def resource(self, i):\n",
" return self._resources[i]\n",
"\n",
" def kill(self):\n",
" resources = self._resources\n",
" self._resources = None\n",
" for i in resources:\n",
" i.release()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def worker(pid, state):\n",
" try:\n",
" while True:\n",
" state.atomic('waiting', 1)\n",
" with state.resource(pid):\n",
" state.atomic('waiting', 1)\n",
" with state.resource(pid - 1):\n",
" state.atomic('waiting', -2)\n",
" state.atomic('tasks', 1)\n",
"\n",
" except RuntimeError:\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def deadlock(n):\n",
" state = SharedState(n)\n",
"\n",
" for i in range(n):\n",
" Thread(target=worker, args=(i, state)).start()\n",
"\n",
" while state.atomic('waiting') < 2 * n:\n",
" sleep(1)\n",
"\n",
" print(n, 'workers; deadlock after', state.atomic('tasks'), 'tasks')\n",
" state.kill()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10 workers; deadlock after 19935 tasks\n",
"20 workers; deadlock after 36262 tasks\n",
"30 workers; deadlock after 77059 tasks\n",
"40 workers; deadlock after 15107 tasks\n",
"50 workers; deadlock after 43405 tasks\n",
"60 workers; deadlock after 9465 tasks\n",
"70 workers; deadlock after 78949 tasks\n",
"80 workers; deadlock after 10051 tasks\n",
"90 workers; deadlock after 14169 tasks\n"
]
}
],
"source": [
"for i in range(1, 10):\n",
" deadlock(10 * i)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 71 - hashtable - chaining.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from types import SimpleNamespace\n",
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class HashTable:\n",
"\n",
" ratio_expand = .8\n",
" ratio_shrink = .2\n",
" min_size = 11\n",
"\n",
" def __init__(self, size=None):\n",
" self._size = size or self.min_size\n",
" self._buckets = [None] * self._size\n",
" self._list = None\n",
" self._count = 0\n",
"\n",
" def _entry(self, key):\n",
" # get hash and index\n",
" idx = hash(key) % self._size\n",
"\n",
" # find entry by key\n",
" p, q = self._buckets[idx], None\n",
" while p and p.key != key:\n",
" p, q = p.next, p\n",
"\n",
" # index, entry, previous entry\n",
" return idx, p, q\n",
"\n",
" def _ensure_capacity(self):\n",
" fill = self._count / self._size\n",
" \n",
" # expand or shrink?\n",
" if fill > self.ratio_expand:\n",
" self._size = self._size * 2 + 1\n",
" elif fill < self.ratio_shrink and self._size > self.min_size:\n",
" self._size = (self._size - 1) // 2\n",
" else:\n",
" return\n",
"\n",
" # reallocate buckets\n",
" self._buckets = [None] * self._size\n",
"\n",
" # store entries into new buckets\n",
" p = self._list\n",
" while p:\n",
" idx = hash(p.key) % self._size\n",
" p.next = self._buckets[idx]\n",
" self._buckets[idx] = p\n",
" p = p.entry_next\n",
"\n",
" def __len__(self):\n",
" return self._count\n",
"\n",
" def __contains__(self, key):\n",
" _, p, _ = self._entry(key)\n",
" return bool(p)\n",
"\n",
" def __getitem__(self, key):\n",
" _, p, _ = self._entry(key)\n",
" return p and p.value\n",
"\n",
" def __setitem__(self, key, value):\n",
" idx, p, _ = self._entry(key)\n",
"\n",
" # set entry if key was found\n",
" if p:\n",
" p.value = value\n",
" return\n",
"\n",
" # create new entry\n",
" p = SimpleNamespace(\n",
" key=key,\n",
" value=value,\n",
" next=self._buckets[idx],\n",
" entry_next=self._list,\n",
" entry_prev=None\n",
" )\n",
"\n",
" # store to bucket\n",
" self._buckets[idx] = p\n",
"\n",
" # store to list\n",
" if self._list:\n",
" self._list.entry_prev = p\n",
" self._list = p\n",
"\n",
" # expand\n",
" self._count += 1\n",
" self._ensure_capacity()\n",
"\n",
" def __delitem__(self, key):\n",
" idx, p, q = self._entry(key)\n",
"\n",
" # key not found\n",
" if not p:\n",
" return\n",
"\n",
" # remove from bucket\n",
" if q:\n",
" q.next = p.next\n",
" else:\n",
" self._buckets[idx] = p.next\n",
"\n",
" # remove from list\n",
" if p.entry_next:\n",
" p.entry_next.entry_prev = p.entry_prev\n",
" if p.entry_prev:\n",
" p.entry_prev.entry_next = p.entry_next\n",
" else:\n",
" self._list = p.entry_next\n",
"\n",
" # shrink\n",
" self._count -= 1\n",
" self._ensure_capacity()\n",
"\n",
" def __iter__(self):\n",
" p = self._list\n",
" while p:\n",
" yield p.key\n",
" p = p.entry_next\n",
" \n",
" def slots(self):\n",
" return ''.join(p and 'x' or '-' for p in self._buckets)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"table = HashTable()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# add random values\n",
"for _ in range(1000):\n",
" key, value = np.random.randint(1000), np.random.rand()\n",
" table[key] = value"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(625, 1535)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(table), table._size"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'-x-x-x-xx-xxxxx-xxx-xxxx-x-x-x---xx-xxx---xx-x--x-xx-x-xxxx-xx-xx--xxx--xx-x-x-xx-x-xxxx-x-xxxx--xxxxx-x--xxx-xxx-xxxxxx--xxxx-xxxxx---xxxxxx--xxxxxxx--xx-x-xx--xx-xxxxx-x-xxxxx-xx--x--xxx-xxxx-x-xx-x---x-xx---xx--xxxxx--xx-x---x--xx-----xxx--x--xx-xx--xxxx-xx---xxxx-x-xxx--x-xxx--xx-xx-xx-xx--xx-xx-xxxxxx-xxxxxx--xx-x-xx---xx--x-xxx--xx-xx-x-xx-x-x-xxxx--xx-xx-x-xx-x--xxx-xx---xx--xx--x-xxx---x-xxxxxxx--xxx-x---x-x-x--x-xxxxx---x-xxxxx----xx-xxxx-x-xxxxxxxxx-xxxxxx---xx-x--xx-xx-xxx--xxx-x---xxxxx--x-xx-xx---xxx-xx--xx-xx-xx-xx-xxxxx--xx-xxx-x----x-xxxx--xxxx-xxxxxx--xxxxxx-x-x-x--x-xxx--xx-x-x-x-xxxx-xx-x-x-x-xxx--xxxxxx-xxxxxxx-x-x-xxxxxxx-xxx-xxx-x-xxx-xxxxx---x-xxxx-x-x-x--x---xxx--xxxx-xxxx----xxxx-xxx-xxx-xx-x-x-xxx-x---xxx-xx-x--x-xx--xxx-x-xxxxxx-xxx--xx----x-xxxxxxx-------x-x-xxxxxxx-x-x-x-----xxxxx-xxxxx--xxx-xxx-----xxx-xxxx--xxxxxxx--xx-xxx-xx---xx-xxxxxxxxxxxxxx-xxxx--x--x--xxxx---xxxxxx-xxxx---xxx---x-xxxxxx-x--xxxx--xxxxx---xxx-x-x---xxx---xx-xx-x-xxxx--xxx-xxxxxx--xx-x-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"table.slots()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"918 0.46964946620099624\n",
"880 0.6586348500634474\n",
"633 0.06134674099379567\n",
"89 0.7216673947800543\n",
"53 0.7986314320745663\n"
]
}
],
"source": [
"# print some values\n",
"for key in list(table)[:5]:\n",
" print(key, table[key])"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# delete all the values\n",
"for key in list(table):\n",
" del table[key]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(0, 11)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(table), table._size"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 72 - hashtable - open addressing.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class HashTable:\n",
"\n",
" ratio_expand = .7\n",
" ratio_shrink = .2\n",
" min_size = 11\n",
" empty = (None,)\n",
"\n",
" def __init__(self, size=None):\n",
" self._size = size or self.min_size\n",
" self._buckets = [None] * self._size\n",
" self._count = 0\n",
"\n",
" def _entry(self, key):\n",
" # get hash\n",
" hash_ = hash(key)\n",
" idx1 = None\n",
"\n",
" for i in range(self._size):\n",
" # quadratic probing\n",
" idx = (hash_ + i) % self._size\n",
" entry = self._buckets[idx]\n",
"\n",
" # end of chain\n",
" if not entry:\n",
" break\n",
" # remember first empty bucket\n",
" elif entry is self.empty:\n",
" if idx1 is None:\n",
" idx1 = idx\n",
" # test key\n",
" elif entry[0] == key:\n",
" return idx, entry\n",
"\n",
" else:\n",
" # out of space\n",
" if idx1 is None:\n",
" raise IndexError()\n",
"\n",
" # return first empty bucket\n",
" return (idx, None) if idx1 is None else (idx1, None)\n",
"\n",
" def _ensure_capacity(self):\n",
" fill = self._count / self._size\n",
" \n",
" # expand or shrink?\n",
" if fill > self.ratio_expand:\n",
" self._size = self._size * 2 + 1\n",
" elif fill < self.ratio_shrink and self._size > self.min_size:\n",
" self._size = (self._size - 1) // 2\n",
" else:\n",
" return\n",
"\n",
" # reallocate buckets\n",
" entries = self._buckets\n",
" self._buckets = [None] * self._size\n",
"\n",
" # store entries into new buckets\n",
" for entry in entries:\n",
" if entry and entry is not self.empty:\n",
" idx, _ = self._entry(entry[0])\n",
" self._buckets[idx] = entry\n",
"\n",
" def __len__(self):\n",
" return self._count\n",
"\n",
" def __contains__(self, key):\n",
" _, entry = self._entry(key)\n",
" return bool(entry)\n",
"\n",
" def __getitem__(self, key):\n",
" _, entry = self._entry(key)\n",
" return entry and entry[1]\n",
"\n",
" def __setitem__(self, key, value):\n",
" idx, entry = self._entry(key)\n",
"\n",
" # set value\n",
" self._buckets[idx] = key, value\n",
"\n",
" # expand\n",
" self._count += bool(not entry or entry is self.empty)\n",
" self._ensure_capacity()\n",
"\n",
" def __delitem__(self, key):\n",
" idx, entry = self._entry(key)\n",
"\n",
" # delete key and value\n",
" if entry:\n",
" self._buckets[idx] = self.empty\n",
"\n",
" # shrink\n",
" self._count -= bool(entry and entry is not self.empty)\n",
" self._ensure_capacity()\n",
"\n",
" def __iter__(self):\n",
" for entry in self._buckets:\n",
" if entry and entry is not self.empty:\n",
" yield entry[0]\n",
"\n",
" def slots(self):\n",
" return ''.join('-' if not p else 'o' if p is self.empty else 'x' for p in self._buckets)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"table = HashTable()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# add random values\n",
"for _ in range(1000):\n",
" key, value = np.random.randint(1000), np.random.rand()\n",
" if np.random.rand() >= .5:\n",
" table[key] = value\n",
" else:\n",
" del table[key]"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(303, 767)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(table), table._size"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'-xxxxxxxx----xxxx--xxxx--xxxxxxxxx--x-xx------x-xx-xx---xxxxxxxx--------xxx-----x-x-x---x--xxxxxxx--xxxxo-x-x----xx-xxx-xo-xxx----xx---xxxx--xx-x-----xxxxxxx-xxxx-xxx-x---x-x----o---xx-----xx---xxxxx---xx-xxxxxo--xx----xxxxxxx--x--x-x-xxx-----x--------x-x---x--xx--------xx-x-------x--x-xx-x---x-x--xx-x--------x--x-x-xxx--x-----xx-----xx-xo------x-x--x-x----x-------xxx-x-x----x--x-x-----xx----xxxx----x-xx--------x---xx--x--x--x--xx--xx---x--x---x-----x--x--xx---x-x-x--x-x-ox--x-xx-x---xx--xox-xo-----x-x---xx-x---x-x----x--x-x-----x--xxx----x-----ox--xx-x--x-xx-x-xx-x---xx--xxo-ox--xx-x-x---xx-x--x-x---xx-x-ox-xxxx--x-----------x-------x-xx-xxx---xxx--x-----------xx-x-----x-------x---x------x--xxxx-------x-x---x--x-xxx---o-xx----o------x--x---xx-x---xx-xxx-x-'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"table.slots()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 0.09786699342385574\n",
"2 0.3816624750531391\n",
"768 0.17267164120539713\n",
"769 0.5484495883897172\n",
"772 0.2314199080141074\n"
]
}
],
"source": [
"# print some values\n",
"for key in list(table)[:5]:\n",
" print(key, table[key])"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# delete all the values\n",
"for key in list(table):\n",
" del table[key]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"(0, 11)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(table), table._size"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 73 - bresenhams line.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"75c75969-bea0-4564-af39-7e895b7c4445\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"75c75969-bea0-4564-af39-7e895b7c4445\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '75c75969-bea0-4564-af39-7e895b7c4445' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"75c75969-bea0-4564-af39-7e895b7c4445\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"75c75969-bea0-4564-af39-7e895b7c4445\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"from bokeh.palettes import Set3_12\n",
"\n",
"output_notebook()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def bresenham(x0, y0, x1, y1, color, canvas):\n",
" # swap major and minor axis\n",
" if abs(y0 - y1) > abs(x0 - x1):\n",
" x0, y0, x1, y1 = y0, x0, y1, x1\n",
" canvas = canvas.T\n",
" \n",
" # swap x-axis direction\n",
" if x0 > x1:\n",
" x0, y0, x1, y1 = x1, y1, x0, y0\n",
" \n",
" # initialize\n",
" dx, dy = abs(x0 - x1), abs(y0 - y1)\n",
" dz = 1 if y0 <= y1 else -1\n",
" p = 2 * dy - dx\n",
" \n",
" # draw\n",
" while x0 <= x1:\n",
" canvas[y0, x0] = color\n",
"\n",
" if p > 0:\n",
" p -= 2 * dx\n",
" y0 += dz\n",
" p += 2 * dy\n",
" x0 += 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## plot"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def plot(canvas):\n",
" w, h = canvas.shape\n",
" \n",
" plot = figure(x_range=(0, w - 1), y_range=(0, h - 1))\n",
" plot.grid.visible = False\n",
" plot.axis.visible = False\n",
" image = plot.image([canvas], \n",
" x=0, y=0, dw=w - 1, dh=h - 1,\n",
" palette=['#000000'] + Set3_12)\n",
" \n",
" show(plot)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"n = 50\n",
"canvas = np.zeros((n, n), dtype=int)\n",
"\n",
"# 8 basic lines\n",
"m = n // 2\n",
"bresenham(m, m, n-1, m+10, 1, canvas)\n",
"bresenham(m, m, n-1, m-10, 2, canvas)\n",
"bresenham(m, m, m+10, n-1, 3, canvas)\n",
"bresenham(m, m, m+10, 0, 4, canvas)\n",
"bresenham(0, m+10, m, m, 5, canvas)\n",
"bresenham(0, m-10, m, m, 6, canvas)\n",
"bresenham(m-10, n-1, m, m, 7, canvas)\n",
"bresenham(m-10, 0, m, m, 8, canvas)\n",
"bresenham(m, m, m, m, 0, canvas)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot(canvas)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"n = 100\n",
"canvas = np.zeros((n, n), dtype=int)\n",
"\n",
"for i in range(10):\n",
" x = np.random.randint(0, n, 4)\n",
" bresenham(*x, i + 1, canvas)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot(canvas)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"n = 200\n",
"canvas = np.zeros((n, n), dtype=int)\n",
"\n",
"for i in range(n):\n",
" bresenham(0, 0, i, n - 1, i % 10 + 1, canvas)\n",
" bresenham(0, 0, n - 1, i, i % 10 + 1, canvas)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot(canvas)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 74 - google interview question.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def solve_question(trials):\n",
" # range to search in\n",
" probability_range = np.array([0., 1.])\n",
"\n",
" while True:\n",
" # prob. to see car in 10 minutes\n",
" probability_10min = probability_range.mean()\n",
" \n",
" # simulate three 10-minute intervals\n",
" events = np.random.rand(trials, 3) < probability_10min\n",
" events = np.sum(events, axis=1) > 0\n",
"\n",
" # prob. to see car in 30 minutes\n",
" probability_30min = np.mean(events)\n",
" if abs(probability_30min - .95) < 1e-4:\n",
" return probability_10min\n",
"\n",
" # bisection\n",
" i = 0 if probability_30min < .95 else 1\n",
" probability_range[i] = probability_10min"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0.6318359375"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solve_question(10**6)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0.6328125"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solve_question(10**6)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0.63142514228820801"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solve_question(10**6)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"expected result"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"0.6315968501359612"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1 - pow(0.05, 1/3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 75 - merkles puzzles.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from os import urandom\n",
"from hashlib import sha1\n",
"from random import shuffle, choice"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"puzzle_size = 2 ** 16"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def merkles_puzzle():\n",
" secrets = [None] * puzzle_size\n",
" puzzles = [None] * puzzle_size\n",
"\n",
" for i in range(puzzle_size):\n",
" # generate secret\n",
" secrets[i] = urandom(16)\n",
"\n",
" # pair := secret|index\n",
" pair = secrets[i] + int.to_bytes(i, 4, 'big')\n",
" # plaintext := pair|sha1(pair)\n",
" plaintext = pair + sha1(pair).digest()\n",
"\n",
" # cipthertext := ENCRYPT(plaintext, key)\n",
" key = urandom(10)\n",
" noise = sha1(key).digest()\n",
" noise += sha1(noise).digest()\n",
" ciphertext = bytes(i ^ j for i, j in zip(plaintext, noise))\n",
"\n",
" # puzzle := ciphertext|key\n",
" puzzles[i] = ciphertext + key[2:]\n",
"\n",
" # randomize order\n",
" shuffle(puzzles)\n",
"\n",
" # return\n",
" return secrets, puzzles"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def solve_puzzle(puzzle):\n",
" ciphertext = puzzle[:40]\n",
" key = puzzle[40:]\n",
"\n",
" for i in range(puzzle_size):\n",
" # guess key\n",
" noise = sha1(int.to_bytes(i, 2, 'big') + key).digest()\n",
" noise += sha1(noise).digest()\n",
"\n",
" # plaintext := DECRYPT(ciphertext, key)\n",
" plaintext = bytes(i ^ j for i, j in zip(ciphertext, noise))\n",
"\n",
" # pair|digest := key|index|sha1(pair)\n",
" pair = plaintext[:20]\n",
" digest = plaintext[20:]\n",
"\n",
" # on match: time, key, index\n",
" if sha1(pair).digest() == digest:\n",
" return i, pair[:16], int.from_bytes(pair[16:], 'big')"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"## (I) alice"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"alice_secrets, public_puzzles = merkles_puzzle()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## (II) bob"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Bob has secret and publishes index\n",
"key: b'\\xe7.T\\xbd|\\x94\\xa1\\r\\xa3\\xffy\\xab\\x98ZJ\\xb3'\n",
"index: 52188\n",
"steps executed: 53788\n"
]
}
],
"source": [
"bob_time, bob_secret, public_index = solve_puzzle(choice(public_puzzles))\n",
"\n",
"print('Bob has secret and publishes index')\n",
"print('key:', bob_secret)\n",
"print('index:', public_index)\n",
"print('steps executed:', bob_time)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## (III) alice"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Alice has secret\n",
"key: b'\\xe7.T\\xbd|\\x94\\xa1\\r\\xa3\\xffy\\xab\\x98ZJ\\xb3'\n"
]
}
],
"source": [
"print('Alice has secret')\n",
"print('key:', alice_secrets[public_index])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## (IV) adversary"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"adversary failed to find secret\n",
"searched puzzles: 168 steps executed: 5404667\n"
]
}
],
"source": [
"total_time, total_puzzles = 0, 0\n",
"\n",
"for puzzle in public_puzzles:\n",
" adv_time, adv_key, adv_index = solve_puzzle(puzzle)\n",
" total_time += adv_time\n",
" total_puzzles += 1\n",
"\n",
" if adv_index == public_index:\n",
" print('very unlikely! adversary found secret:', adv_key)\n",
" break\n",
"\n",
" if total_time > bob_time * 100:\n",
" print('adversary failed to find secret')\n",
" break\n",
"\n",
"print('searched puzzles:', total_puzzles, 'steps executed:', total_time)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 76 - 2-3 tree.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"from bokeh.io import push_notebook\n",
"from time import sleep"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class Node:\n",
" def __init__(self, value, left=None, right=None, red=False):\n",
" self.value = value\n",
" self.left = left\n",
" self.right = right\n",
" self.red = red\n",
"\n",
" @staticmethod\n",
" def red(node):\n",
" return node and node.red\n",
"\n",
" def rotate_left(self):\n",
" if not Node.red(self.left) and Node.red(self.right):\n",
" child = self.right\n",
" self.right, child.left = child.left, self\n",
" self.red, child.red = True, self.red\n",
" return child\n",
" else:\n",
" return self\n",
"\n",
" def rotate_right(self):\n",
" if Node.red(self.left) and Node.red(self.left.left):\n",
" child = self.left\n",
" self.left, child.right = child.right, self\n",
" self.red, child.red = True, self.red\n",
" return child\n",
" else:\n",
" return self\n",
" \n",
" def flip_colors(self):\n",
" if Node.red(self.left) and Node.red(self.right):\n",
" self.red, self.left.red, self.right.red = True, False, False"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def insert(node, value, root=True):\n",
" if not node:\n",
" return Node(value, red=not root)\n",
" \n",
" # insert value\n",
" if value == node.value:\n",
" pass\n",
" elif value < node.value:\n",
" node.left = insert(node.left, value, root=False)\n",
" else:\n",
" node.right = insert(node.right, value, root=False)\n",
" \n",
" # update tree w.r.t invariants\n",
" node = node.rotate_left()\n",
" node = node.rotate_right()\n",
" node.flip_colors()\n",
"\n",
" # keep root black\n",
" if root:\n",
" node.red = False\n",
"\n",
" return node"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def draw(node, red, black, xlo=0, xhi=1, x=.5, y=0):\n",
" if node:\n",
" x_, y_ = (xlo + xhi) / 2, y - 1\n",
" (red if node.red else black).append([x, y, x_, y_])\n",
" draw(node.left, red, black, xlo, x_, x_, y_)\n",
" draw(node.right, red, black, x_, xhi, x_, y_)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# insert few values\n",
"root = None\n",
"for i in range(5):\n",
" root = insert(root, i)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"a9a47e6b-6413-432b-b807-c046ca5ab7d7\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"a9a47e6b-6413-432b-b807-c046ca5ab7d7\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'a9a47e6b-6413-432b-b807-c046ca5ab7d7' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"a9a47e6b-6413-432b-b807-c046ca5ab7d7\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"a9a47e6b-6413-432b-b807-c046ca5ab7d7\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"# get plot data\n",
"red, black = [], []\n",
"draw(root, red, black)\n",
"\n",
"# plot\n",
"plot = figure(plot_height=400, plot_width=800)\n",
"plot.axis.visible = False\n",
"plot.grid.visible = False\n",
"\n",
"red_segment = plot.segment(*zip(*red), color='#ff0000')\n",
"black_segment = plot.segment(*zip(*black), color='#000000')\n",
"\n",
"handle = show(plot, notebook_handle=True)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"for _ in range(100):\n",
" for i in np.random.randint(0, 1000, 5):\n",
" root = insert(root, i)\n",
"\n",
" red, black = [], []\n",
" draw(root, red, black)\n",
"\n",
" x0, y0, x1, y1 = red and zip(*red) or [tuple()] * 4\n",
" red_segment.data_source.data.update({\n",
" 'x0': x0, 'y0': y0, 'x1': x1, 'y1': y1\n",
" })\n",
" x0, y0, x1, y1 = black and zip(*black) or [tuple()] * 4\n",
" black_segment.data_source.data.update({\n",
" 'x0': x0, 'y0': y0, 'x1': x1, 'y1': y1\n",
" })\n",
"\n",
" push_notebook()\n",
" sleep(.1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 77 - unification.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class Unify:\n",
" \n",
" def __init__(self):\n",
" self.reference = {} # variable bindings\n",
" self.checkpoint = [] # unification checkpoints\n",
" self.var_ctx = 0 # unique variable id\n",
"\n",
" def variable(self, *args):\n",
" self.var_ctx += 1\n",
" return ['%s_%d' % (var, self.var_ctx) for var in args]\n",
"\n",
" def __call__(self, var_x, var_y):\n",
" # resolve variable X\n",
" while isinstance(var_x, str) and var_x in self.reference:\n",
" var_x = self.reference[var_x]\n",
"\n",
" # resolve variable Y\n",
" while isinstance(var_y, str) and var_y in self.reference:\n",
" var_y = self.reference[var_y]\n",
"\n",
" # unified to self?\n",
" if isinstance(var_x, str) and isinstance(var_y, str):\n",
" if var_x == var_y:\n",
" return True\n",
"\n",
" # unify free variable X\n",
" if isinstance(var_x, str):\n",
" self.reference[var_x] = var_y\n",
" self.checkpoint[-1].append(var_x)\n",
" return True\n",
"\n",
" # unify free variable Y\n",
" if isinstance(var_y, str):\n",
" self.reference[var_y] = var_x\n",
" self.checkpoint[-1].append(var_y)\n",
" return True\n",
"\n",
" # tuple is unified element-wise\n",
" if isinstance(var_x, tuple) and isinstance(var_y, tuple):\n",
" if len(var_x) == len(var_y):\n",
" return all(self(i, j) for i, j in zip(var_x, var_y))\n",
"\n",
" # atom is unified on equality\n",
" if isinstance(var_x, int) and isinstance(var_y, int):\n",
" if var_x == var_y:\n",
" return True\n",
"\n",
" # not unifiable\n",
" raise KeyError()\n",
"\n",
" def __getitem__(self, var):\n",
" # resolve tuple by members\n",
" if isinstance(var, tuple):\n",
" return tuple(self[i] for i in var)\n",
"\n",
" # resolve variable recursively\n",
" if isinstance(var, str):\n",
" if var in self.reference:\n",
" return self[self.reference[var]]\n",
" return var\n",
"\n",
" # atomic value\n",
" if isinstance(var, int):\n",
" return var\n",
"\n",
" # invalid object\n",
" raise TypeError()\n",
"\n",
" def __enter__(self):\n",
" # store unification checkpoint\n",
" self.checkpoint.append([])\n",
"\n",
" def __exit__(self, exc_type, *args):\n",
" # remove checkpoint and unbind variables\n",
" for var in self.checkpoint.pop():\n",
" if var in self.reference:\n",
" del self.reference[var]\n",
"\n",
" # suppress exception\n",
" if exc_type is not GeneratorExit:\n",
" return True"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def conc(PREFIX, SUFFIX, RESULT):\n",
" HEAD, TAIL, CONC = unify.variable('HEAD', 'TAIL', 'CONC')\n",
"\n",
" with unify:\n",
" unify(PREFIX, EMPTY) and unify(SUFFIX, RESULT)\n",
" yield\n",
"\n",
" with unify:\n",
" unify(PREFIX, (HEAD, TAIL)) and unify(RESULT, (HEAD, CONC))\n",
" yield from conc(TAIL, SUFFIX, CONC)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"unify = Unify()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"EMPTY = tuple()\n",
"prefix = (1, (2, EMPTY))\n",
"suffix = (3, (4, EMPTY))\n",
"result = (1, (2, (3, (4, EMPTY))))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### concatenate PREFIX and SUFFIX"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(1, (2, (3, (4, ()))))\n"
]
}
],
"source": [
"for _ in conc(prefix, suffix, 'RESULT'):\n",
" print(unify['RESULT'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### what was concatenated to PREFIX if RESULT is this?"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(3, (4, ()))\n"
]
}
],
"source": [
"for _ in conc(prefix, 'SUFFIX', result):\n",
" print(unify['SUFFIX'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### this is super cool! if RESULT is this, what was PREFIX and SUFFIX?"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"possible answer is () and (1, (2, (3, (4, ()))))\n",
"possible answer is (1, ()) and (2, (3, (4, ())))\n",
"possible answer is (1, (2, ())) and (3, (4, ()))\n",
"possible answer is (1, (2, (3, ()))) and (4, ())\n",
"possible answer is (1, (2, (3, (4, ())))) and ()\n"
]
}
],
"source": [
"for _ in conc('PREFIX', 'SUFFIX', result):\n",
" print('possible answer is', unify['PREFIX'], 'and', unify['SUFFIX'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### if PREFIX and SUFFIX are concatenated, would this be RESULT?"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"yes\n"
]
}
],
"source": [
"for _ in conc(prefix, suffix, result):\n",
" print('yes')\n",
" break\n",
"else:\n",
" print('no')"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"yes\n"
]
}
],
"source": [
"for _ in conc(prefix, suffix, result):\n",
" print('yes')\n",
" break\n",
"else:\n",
" print('no')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## most people won't solve this one"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"PREFIX = ()\n",
"SUFFIX = RESULT\n",
"RESULT = RESULT\n",
"\n",
"PREFIX = ('HEAD_18', ())\n",
"SUFFIX = CONC_18\n",
"RESULT = ('HEAD_18', 'CONC_18')\n",
"\n",
"PREFIX = ('HEAD_18', ('HEAD_19', ()))\n",
"SUFFIX = CONC_19\n",
"RESULT = ('HEAD_18', ('HEAD_19', 'CONC_19'))\n",
"\n",
"PREFIX = ('HEAD_18', ('HEAD_19', ('HEAD_20', ())))\n",
"SUFFIX = CONC_20\n",
"RESULT = ('HEAD_18', ('HEAD_19', ('HEAD_20', 'CONC_20')))\n",
"\n",
"PREFIX = ('HEAD_18', ('HEAD_19', ('HEAD_20', ('HEAD_21', ()))))\n",
"SUFFIX = CONC_21\n",
"RESULT = ('HEAD_18', ('HEAD_19', ('HEAD_20', ('HEAD_21', 'CONC_21'))))\n",
"\n"
]
}
],
"source": [
"for _ in zip(range(5), conc('PREFIX', 'SUFFIX', 'RESULT')):\n",
" print('PREFIX =', unify['PREFIX'])\n",
" print('SUFFIX =', unify['SUFFIX'])\n",
" print('RESULT =', unify['RESULT'])\n",
" print()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 78 - horn-satifiability.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from collections import defaultdict, deque"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def knowledge_base(formulas):\n",
" rules, variable, dependency = [], defaultdict(bool), defaultdict(list)\n",
"\n",
" def _clause(formula):\n",
" # A, B, C => P\n",
" neg, pos = formula.replace(' ', '').split('=>')\n",
" neg, pos = set(neg.split('&')) - {''}, pos or None\n",
"\n",
" # add rule\n",
" rules.append((neg, pos))\n",
" \n",
" # set variable and track dependencies\n",
" for i in neg:\n",
" dependency[i].append((neg, pos))\n",
"\n",
" # parse formulas and build knowledge base\n",
" deque((_clause(i) for i in formulas.split('\\n') if i), 0)\n",
" \n",
" return rules, variable, dependency"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def resolution(rules, variable, dependency):\n",
" # initial variables that have to be satisfied\n",
" to_satisfy = [(neg, pos) for neg, pos in rules if not neg]\n",
"\n",
" while to_satisfy:\n",
" neg, pos = to_satisfy.pop()\n",
"\n",
" # contradiction: true => false\n",
" if not pos:\n",
" return False\n",
"\n",
" # satisfy variable\n",
" variable[pos] = True\n",
"\n",
" # update dependent rules\n",
" for d_neg, d_pos in dependency[pos]:\n",
" d_neg.remove(pos)\n",
" \n",
" # next variable to be satisfied\n",
" if not d_neg and d_pos not in variable:\n",
" to_satisfy.append((d_neg, d_pos))\n",
"\n",
" return True"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def hornsat(formulas):\n",
" rules, variable, dependency = knowledge_base(formulas)\n",
" satisfiable = resolution(rules, variable, dependency)\n",
"\n",
" print(['CONTRADICTION', 'SATISFIABLE'][satisfiable])\n",
" print(', '.join('%s=%s' % i for i in variable.items()))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"SATISFIABLE\n",
"X=True, Y=True, Z=True\n"
]
}
],
"source": [
"hornsat(\"\"\"\n",
"X => Y\n",
"Y => Z\n",
"=> X\n",
"\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CONTRADICTION\n",
"X=True, Y=True\n"
]
}
],
"source": [
"hornsat(\"\"\"\n",
"X => Y\n",
"Y => X\n",
"=> X\n",
"Y =>\n",
"\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"SATISFIABLE\n",
"R=True, S=True, P=True\n"
]
}
],
"source": [
"hornsat(\"\"\"\n",
"P & Q & R & S => X\n",
"P & Q => R\n",
"R => S\n",
"X =>\n",
"=> P\n",
"=> R\n",
"\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CONTRADICTION\n",
"Q=True, P=True, R=True, S=True, X=True\n"
]
}
],
"source": [
"hornsat(\"\"\"\n",
"P & Q & R & S => X\n",
"P & Q => R\n",
"R => S\n",
"X =>\n",
"=> P\n",
"=> Q\n",
"\"\"\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 79 - logistic regression.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## logistic regression"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def logistic_regression(X, Y, W, lr=0.001, steps=1000):\n",
" m = len(Y)\n",
" sigmoid = lambda z: 1 / (1 + np.exp(-z))\n",
" \n",
" for _ in range(steps):\n",
" # prediction\n",
" hypothesis = sigmoid(X @ W)\n",
" \n",
" # fix overflow & underflow\n",
" hypothesis = np.clip(hypothesis, 1e-5, 1 - 1e-5)\n",
" \n",
" # loss function, gradient, training\n",
" loss = -1 / m * (Y @ np.log(hypothesis) + (1 - Y) @ np.log(1 - hypothesis))\n",
" gradient = 1 / m * (X.T @ (hypothesis - Y))\n",
" W -= lr * gradient\n",
" \n",
" # current loss & prediction\n",
" return loss, sigmoid(X @ W)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## data"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"n = 10000\n",
"# random values\n",
"x_, y_ = np.random.rand(2, n)\n",
"# polynomial coefficients\n",
"X = np.c_[np.ones(n), x_, y_, x_ ** 2, y_ ** 2]\n",
"# Y: target values\n",
"Y = (x_ - .5 <= (y_ - .5) ** 2) * 1\n",
"# weights\n",
"W = np.zeros(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## train classification model"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.0939072483492\n",
"0.0743423477901\n",
"0.0639064636228\n",
"0.057261603557\n",
"0.0525680284948\n",
"0.0490253057578\n",
"0.0462262933158\n",
"0.0439401890239\n",
"0.0420253995815\n",
"0.0403896777157\n"
]
}
],
"source": [
"for _ in range(10):\n",
" loss, H = logistic_regression(X, Y, W, lr=5., steps=1000)\n",
" print(loss)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"accuracy 0.9912\n",
"weights [ 24.48175955 -17.75464348 -38.46985456 -22.38533121 38.41771001]\n"
]
}
],
"source": [
"print('accuracy', np.mean(Y == H.round()))\n",
"print('weights', W)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## plot"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"e563c626-944e-44f4-a90e-450c3f52e6b6\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"e563c626-944e-44f4-a90e-450c3f52e6b6\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'e563c626-944e-44f4-a90e-450c3f52e6b6' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"e563c626-944e-44f4-a90e-450c3f52e6b6\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"e563c626-944e-44f4-a90e-450c3f52e6b6\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"palette = ['steelblue', 'red', 'lightgreen']\n",
"color = [palette[i] for i in (Y + H.round()).astype(int)]\n",
"\n",
"plot = figure()\n",
"plot.circle(x_, y_, line_color='#c0c0c0', fill_color=color, alpha=.6, size=8)\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 80 - hopfield net.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def hopfield_net(n):\n",
" weights = np.zeros((n, n), dtype=int)\n",
"\n",
" def _store(data):\n",
" nonlocal weights\n",
"\n",
" vector = np.array(data, dtype=int) * 2 - 1\n",
" weights += np.outer(vector, vector) - np.eye(len(data), dtype=int)\n",
"\n",
" def _reconstruct(data):\n",
" visible = np.array(data, dtype=int)\n",
"\n",
" while True:\n",
" for i, v in np.ndenumerate(visible):\n",
" visible[i] = weights[i] @ visible >= 0\n",
"\n",
" hidden = weights @ visible >= 0\n",
" if np.all(hidden == visible):\n",
" return visible\n",
"\n",
" return _store, _reconstruct"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"store, reconstruct = hopfield_net(25)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### memories"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"store([\n",
" 1,0,0,0,1,\n",
" 0,1,0,1,0,\n",
" 0,0,1,0,0,\n",
" 0,1,0,1,0,\n",
" 1,0,0,0,1,\n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"store([\n",
" 1,1,1,1,1,\n",
" 1,0,0,0,1,\n",
" 1,0,0,0,1,\n",
" 1,0,0,0,1,\n",
" 1,1,1,1,1,\n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"store([\n",
" 0,0,1,0,0,\n",
" 0,0,1,0,0,\n",
" 1,1,1,1,1,\n",
" 0,0,1,0,0,\n",
" 0,0,1,0,0,\n",
"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### reconstruction"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1, 1, 1, 1],\n",
" [1, 0, 0, 0, 1],\n",
" [1, 0, 0, 0, 1],\n",
" [1, 0, 0, 0, 1],\n",
" [1, 1, 1, 1, 1]])"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reconstruct([\n",
" 1,1,1,1,1,\n",
" 0,0,0,0,0,\n",
" 0,0,0,0,0,\n",
" 0,0,0,0,0,\n",
" 0,0,0,0,0,\n",
"]).reshape(5, 5)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1, 1, 1, 1],\n",
" [1, 0, 0, 0, 1],\n",
" [1, 0, 0, 0, 1],\n",
" [1, 0, 0, 0, 1],\n",
" [1, 1, 1, 1, 1]])"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reconstruct([\n",
" 0,0,0,0,0,\n",
" 0,0,0,0,0,\n",
" 0,0,0,0,0,\n",
" 0,0,0,0,1,\n",
" 1,1,0,0,1,\n",
"]).reshape(5, 5)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0],\n",
" [0, 0, 1, 0, 0],\n",
" [0, 1, 0, 1, 0],\n",
" [1, 0, 0, 0, 1]])"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reconstruct([\n",
" 1,0,0,0,0,\n",
" 0,1,0,0,0,\n",
" 0,0,1,0,0,\n",
" 0,0,0,1,0,\n",
" 0,0,0,0,1,\n",
"]).reshape(5, 5)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[0, 0, 1, 0, 0],\n",
" [0, 0, 1, 0, 0],\n",
" [1, 1, 1, 1, 1],\n",
" [0, 0, 1, 0, 0],\n",
" [0, 0, 1, 0, 0]])"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reconstruct([\n",
" 0,0,0,0,0,\n",
" 0,0,1,0,0,\n",
" 0,1,0,1,0,\n",
" 0,0,1,0,0,\n",
" 0,0,0,0,0,\n",
"]).reshape(5, 5)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[0, 0, 1, 0, 0],\n",
" [0, 0, 1, 0, 0],\n",
" [1, 1, 1, 1, 1],\n",
" [0, 0, 1, 0, 0],\n",
" [0, 0, 1, 0, 0]])"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reconstruct([\n",
" 0,0,1,0,0,\n",
" 0,0,1,0,0,\n",
" 0,0,1,0,0,\n",
" 0,0,1,0,0,\n",
" 0,0,1,0,0,\n",
"]).reshape(5, 5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 81 - topological sort.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import networkx as nx\n",
"from collections import deque\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def topological_sort(graph):\n",
" topology = []\n",
" degree = {n: graph.in_degree(n) for n in graph.nodes()}\n",
"\n",
" # nodes without incoming edges\n",
" queue = deque(n for n, d in degree.items() if not d)\n",
" \n",
" while queue:\n",
" n = queue.popleft()\n",
" topology.append(n)\n",
"\n",
" # remove node's edges\n",
" for m in list(graph[n]):\n",
" degree[m] -= 1\n",
"\n",
" # enqueue nodes with no incoming edges\n",
" if not degree[m]:\n",
" queue.append(m)\n",
"\n",
" if len(topology) < len(graph):\n",
" raise ValueError('graph contains cycle')\n",
"\n",
" return topology"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"graph = nx.DiGraph([\n",
" ('A', 'B'),\n",
" ('A', 'D'),\n",
" ('B', 'C'),\n",
" ('B', 'E'),\n",
" ('C', 'D'),\n",
" ('C', 'E'),\n",
" ('D', 'E'),\n",
" ('F', 'G'),\n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"['A', 'F', 'B', 'G', 'C', 'D', 'E']"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"topology = topological_sort(graph)\n",
"topology"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## plot"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"n = len(topology)\n",
"x = range(n)\n",
"q = []\n",
"\n",
"for u, v in graph.edges():\n",
" x0 = topology.index(u)\n",
" x1 = topology.index(v)\n",
" yc = 0 if abs(x0 - x1) == 1 else -.7 if x0 & 1 else .7\n",
" q.append([x0, 0, x1, 0, (x0 + x1) / 2, yc])"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"69b50d06-2af0-443b-9604-e06bec647c92\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"69b50d06-2af0-443b-9604-e06bec647c92\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '69b50d06-2af0-443b-9604-e06bec647c92' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"69b50d06-2af0-443b-9604-e06bec647c92\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"69b50d06-2af0-443b-9604-e06bec647c92\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"plot = figure(x_range = (-1, n), y_range = (-1, 1), plot_width=600, plot_height=300)\n",
"plot.grid.visible = False\n",
"plot.axis.visible = False\n",
"\n",
"plot.quadratic(*zip(*q), color='red')\n",
"plot.circle(x, 0, size=30)\n",
"plot.text(x, 0, text=topology, text_color='yellow', text_align='center', text_baseline='middle')\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 82 - flood fill.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"3a3f6afb-9f16-4413-9aab-5c60408ce5d4\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"3a3f6afb-9f16-4413-9aab-5c60408ce5d4\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '3a3f6afb-9f16-4413-9aab-5c60408ce5d4' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"3a3f6afb-9f16-4413-9aab-5c60408ce5d4\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"3a3f6afb-9f16-4413-9aab-5c60408ce5d4\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"from bokeh.palettes import RdYlGn\n",
"\n",
"output_notebook()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def flood_fill(x, y, canvas, color, bg=None):\n",
" h, w = canvas.shape\n",
" bg = canvas[y, x] if bg is None else bg\n",
" stack = [(y, x)]\n",
" \n",
" while stack:\n",
" i, j = stack.pop()\n",
" if canvas[i, j] != bg:\n",
" continue\n",
"\n",
" # find left boundary\n",
" while j > 0 and canvas[i, j - 1] == bg:\n",
" j -= 1\n",
"\n",
" # scan to right\n",
" up, down = True, True\n",
" while j < w and canvas[i, j] == bg:\n",
" canvas[i, j] = color\n",
"\n",
" # detect color change above\n",
" if i + 1 < h:\n",
" if up and canvas[i + 1, j] == bg:\n",
" stack.append((i + 1, j))\n",
" up = canvas[i + 1, j] != bg\n",
"\n",
" # detect color change below\n",
" if i > 0:\n",
" if down and canvas[i - 1, j] == bg:\n",
" stack.append((i - 1, j))\n",
" down = canvas[i - 1, j] != bg\n",
"\n",
" j += 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"n = 300\n",
"canvas = np.zeros((n, n), dtype=np.int8)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# draw random lines\n",
"for i in range(10):\n",
" x, y = np.random.randint(0, n, 2)\n",
" canvas[i & 1 and range(0, y) or range(y, n), x] = 1\n",
" x, y = np.random.randint(0, n, 2)\n",
" canvas[y, i & 1 and range(0, x) or range(x, n)] = 1"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot = figure(x_range=(0, 1), y_range=(0, 1))\n",
"plot.image([canvas], x=0, y=0, dw=1, dh=1)\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"palette = RdYlGn[11]\n",
"\n",
"# flood fill\n",
"for i in range(100):\n",
" x, y = np.random.randint(0, n, 2)\n",
" flood_fill(x, y, canvas, color=(i % len(palette)) + 2, bg=0)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot = figure(x_range=(0, 1), y_range=(0, 1))\n",
"plot.image([canvas], x=0, y=0, dw=1, dh=1, palette=['#000000', '#ffffff'] + palette)\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 83 - breaking AES.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from Crypto import Random\n",
"from Crypto.Cipher import AES"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## AES-CBC"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def encrypt(plaintext):\n",
" # initialize AES\n",
" random = Random.new()\n",
" iv = random.read(16)\n",
" key = random.read(16)\n",
" aes = AES.new(key, AES.MODE_CBC, iv)\n",
"\n",
" # add PKCS#7 padding\n",
" pad = 16 - len(plaintext) % 16\n",
" plaintext += bytes([pad] * pad)\n",
" \n",
" # encrypt\n",
" ciphertext = iv + aes.encrypt(plaintext)\n",
"\n",
" return key, ciphertext"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def decrypt(ciphertext, key):\n",
" # initialize AES\n",
" iv = ciphertext[:16]\n",
" aes = AES.new(key, AES.MODE_CBC, iv)\n",
"\n",
" # decrypt\n",
" plaintext = aes.decrypt(ciphertext[16:])\n",
" \n",
" # check PKCS#7 padding\n",
" pad = plaintext[-1]\n",
" if pad not in range(1, 17):\n",
" raise Exception()\n",
" if plaintext[-pad:] != bytes([pad] * pad):\n",
" raise Exception()\n",
"\n",
" # remove padding\n",
" return plaintext[:-pad]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## secure service"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def secure_service(message):\n",
" secret_key = b'\\xed\\xcc\\xb5\\x8a\\xf4\\x8f\\xd9\\x1e\\x1bS\\xce~p\\xa2s\\xcc'\n",
"\n",
" # decrypt message\n",
" plaintext = decrypt(message, secret_key)\n",
"\n",
" # process message\n",
" try:\n",
" from json import loads\n",
" print('ACK', loads(plaintext))\n",
" except Exception:\n",
" raise ValueError()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## adversarial client"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def attack(message):\n",
" reconstructed = b''\n",
"\n",
" while len(message) >= 32:\n",
" # retrieved block\n",
" block = [0] * 16\n",
"\n",
" # byte in block\n",
" for i in range(1, 17):\n",
" # PKCS#7 padding\n",
" pad = [0] * (16 - i) + [i] * i\n",
"\n",
" for x in range(256):\n",
" # tested byte\n",
" block[-i] = x\n",
" if x == i:\n",
" continue\n",
" \n",
" # alter message\n",
" test = bytearray(message)\n",
" for j in range(16):\n",
" test[-32 + j] ^= block[j] ^ pad[j]\n",
" test = bytes(test)\n",
"\n",
" try:\n",
" # call service\n",
" secure_service(test)\n",
" except ValueError as e:\n",
" break # incorrect content\n",
" except Exception as e:\n",
" pass # incorrect padding\n",
" else:\n",
" block[-i] = i\n",
"\n",
" # store retrieved block and continue\n",
" reconstructed = bytes(block) + reconstructed\n",
" message = message[:-16]\n",
"\n",
" return reconstructed"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"intercepted_message = b'\\xd97\\xea\\xc8\\xfe\\xdf\\x06\\xf7b3\\x16UG\\xd5#>\\xa8\\x1c.l\\xf1+\\xc9H\\xbd\\xb1\\x91\\x90\\xc0\\xac?\\x92\\x1c\\xa0\\x08\\xc7d/\\x10\\xe6\\xae\\xe0 F\\x1a\\x13\\xc1\\xb0\\xf0,\\xd7\\xb9\\xca\\xfb\\xde\\x13\\xa5\\xfd92\\xff*\\x17\\xbc\\x8f\\xd3Z\\xe81\\x8f\\x1c\\xb4\\x17@\\xeb5\\t\\xa4\\x16\\xb2\\x07\\x06\\xd6\\x83x\\xac\\xf3\\xc9\\xb2\\xb7\\xf6Q3\\xc0\\x7f\\x92\\xd4p\\xfeV\\xad{\\xc7(}\\x8f[L>\\x08\\xab\\xfe'"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"b'{\"user\":\"John Doe\",\"message\":\"and what is your favorite way to screw your security up?\"}\\x08\\x08\\x08\\x08\\x08\\x08\\x08\\x08'"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"attack(intercepted_message)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 84 - maze generation.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def generate_maze(n, m):\n",
" # maze skeleton\n",
" maze = np.tile([[1, 2], [2, 0]], (n // 2 + 1, m // 2 + 1))\n",
" maze = maze[:-1, :-1]\n",
"\n",
" cells = {(i, j): (i, j) for i, j in np.argwhere(maze == 1)}\n",
" walls = np.argwhere(maze == 2)\n",
"\n",
" # union-find\n",
" def find(p, q):\n",
" if p != cells[p] or q != cells[q]:\n",
" cells[p], cells[q] = find(cells[p], cells[q])\n",
" return cells[p], cells[q]\n",
"\n",
" # find spanning tree\n",
" np.random.shuffle(walls)\n",
" for wi, wj in walls:\n",
" if wi % 2:\n",
" p, q = find((wi - 1, wj), (wi + 1, wj))\n",
" else:\n",
" p, q = find((wi, wj - 1), (wi, wj + 1))\n",
"\n",
" maze[wi, wj] = p != q\n",
" if p != q:\n",
" cells[p] = q\n",
"\n",
" return maze"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"maze = generate_maze(40, 80)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"ef46fb0b-ffaf-4ce1-b0b6-ac8c16c5aa2a\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"ef46fb0b-ffaf-4ce1-b0b6-ac8c16c5aa2a\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'ef46fb0b-ffaf-4ce1-b0b6-ac8c16c5aa2a' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"ef46fb0b-ffaf-4ce1-b0b6-ac8c16c5aa2a\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"ef46fb0b-ffaf-4ce1-b0b6-ac8c16c5aa2a\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()\n",
"\n",
"plot = figure(x_range=(0, 1), y_range=(0, 1),\n",
" plot_height=410, plot_width=810)\n",
"plot.axis.visible = False\n",
"plot.outline_line_color = '#ffffff'\n",
"plot.image([maze], x=0, y=0, dw=1, dh=1, palette=['#228B22', '#ffffff'])\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 85 - coin success runs.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def probability(n, k, p, all_probs=False):\n",
" F = np.zeros(n + 1)\n",
" U = np.zeros(n + 1)\n",
" R = p ** np.arange(k + 1)\n",
"\n",
" for i in range(k, n + 1):\n",
" U[i] = R[k] - R[1:k] @ U[i-k+1:i][::-1]\n",
" F[i] = U[i] - F[1:i] @ U[1:i][::-1]\n",
"\n",
" S = F.cumsum()\n",
"\n",
" return S if all_probs else S[-1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def print_chance(n):\n",
" print(n, 'tosses; probability to see at least ...')\n",
" for k in range(1, n + 1):\n",
" p = probability(n, k, .5)\n",
" print('%dx HEADS in row = %.4f' % (k, p))\n",
" if p < 1e-4:\n",
" break"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4 tosses; probability to see at least ...\n",
"1x HEADS in row = 0.9375\n",
"2x HEADS in row = 0.5000\n",
"3x HEADS in row = 0.1875\n",
"4x HEADS in row = 0.0625\n"
]
}
],
"source": [
"print_chance(4)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10 tosses; probability to see at least ...\n",
"1x HEADS in row = 0.9990\n",
"2x HEADS in row = 0.8594\n",
"3x HEADS in row = 0.5078\n",
"4x HEADS in row = 0.2451\n",
"5x HEADS in row = 0.1094\n",
"6x HEADS in row = 0.0469\n",
"7x HEADS in row = 0.0195\n",
"8x HEADS in row = 0.0078\n",
"9x HEADS in row = 0.0029\n",
"10x HEADS in row = 0.0010\n"
]
}
],
"source": [
"print_chance(10)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"50 tosses; probability to see at least ...\n",
"1x HEADS in row = 1.0000\n",
"2x HEADS in row = 1.0000\n",
"3x HEADS in row = 0.9827\n",
"4x HEADS in row = 0.8274\n",
"5x HEADS in row = 0.5519\n",
"6x HEADS in row = 0.3146\n",
"7x HEADS in row = 0.1653\n",
"8x HEADS in row = 0.0836\n",
"9x HEADS in row = 0.0415\n",
"10x HEADS in row = 0.0204\n",
"11x HEADS in row = 0.0100\n",
"12x HEADS in row = 0.0049\n",
"13x HEADS in row = 0.0024\n",
"14x HEADS in row = 0.0012\n",
"15x HEADS in row = 0.0006\n",
"16x HEADS in row = 0.0003\n",
"17x HEADS in row = 0.0001\n",
"18x HEADS in row = 0.0001\n"
]
}
],
"source": [
"print_chance(50)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1000 tosses; probability to see at least ...\n",
"1x HEADS in row = 1.0000\n",
"2x HEADS in row = 1.0000\n",
"3x HEADS in row = 1.0000\n",
"4x HEADS in row = 1.0000\n",
"5x HEADS in row = 1.0000\n",
"6x HEADS in row = 0.9997\n",
"7x HEADS in row = 0.9818\n",
"8x HEADS in row = 0.8611\n",
"9x HEADS in row = 0.6242\n",
"10x HEADS in row = 0.3854\n",
"11x HEADS in row = 0.2154\n",
"12x HEADS in row = 0.1140\n",
"13x HEADS in row = 0.0586\n",
"14x HEADS in row = 0.0297\n",
"15x HEADS in row = 0.0150\n",
"16x HEADS in row = 0.0075\n",
"17x HEADS in row = 0.0038\n",
"18x HEADS in row = 0.0019\n",
"19x HEADS in row = 0.0009\n",
"20x HEADS in row = 0.0005\n",
"21x HEADS in row = 0.0002\n",
"22x HEADS in row = 0.0001\n",
"23x HEADS in row = 0.0001\n"
]
}
],
"source": [
"print_chance(1000)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 86 - binary heap.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"identity = lambda i: i"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def heapify(heap, key=identity):\n",
" n = len(heap)\n",
" for i in reversed(range(n // 2)):\n",
" sift_down(heap, i, n, key=key)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def heap_push(heap, value, key=identity):\n",
" i = len(heap)\n",
" heap.append(value)\n",
" sift_up(heap, i, key=key)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def heap_pop(heap, key=identity):\n",
" item, heap[0] = heap[0], heap[-1]\n",
" del heap[-1]\n",
" heap and sift_down(heap, 0, len(heap), key=key)\n",
" return item"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def sift_down(heap, i, n, key=identity):\n",
" # item to be sifted\n",
" item = heap[i]\n",
" item_key = key(item)\n",
"\n",
" while True:\n",
" smallest, k = item_key, i\n",
" j = 2 * i + 1\n",
"\n",
" # left child\n",
" if j < n:\n",
" left_key = key(heap[j])\n",
" if left_key < smallest:\n",
" smallest, k = left_key, j\n",
"\n",
" # right child\n",
" if j + 1 < n and key(heap[j + 1]) < smallest:\n",
" k = j + 1\n",
"\n",
" # swap or finish\n",
" if k != i:\n",
" heap[i] = heap[k]\n",
" i = k\n",
" else:\n",
" break\n",
"\n",
" heap[i] = item"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def sift_up(heap, i, key=identity):\n",
" # item to be sifted\n",
" item = heap[i]\n",
" item_key = key(item)\n",
"\n",
" while i:\n",
" j = i // 2\n",
"\n",
" if item_key < key(heap[j]):\n",
" heap[i] = heap[j]\n",
" i = j\n",
" else:\n",
" break\n",
"\n",
" heap[i] = item"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def heap_sort(data, key=identity):\n",
" heapify(data, key=key)\n",
" \n",
" for i in reversed(range(len(data))):\n",
" data[0], data[i] = data[i], data[0]\n",
" sift_down(data, 0, i, key=key)\n",
" \n",
" data.reverse()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### priority queues"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"data1, data2 = [9, 7, 5, 3, 1], [8, 6, 4, 2, 0]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
]
}
],
"source": [
"heapify(data1)\n",
"heapify(data2)\n",
"\n",
"while data1 or data2:\n",
" data2 and heap_push(data1, heap_pop(data2))\n",
" print(heap_pop(data1), end=', ')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### string array"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"data = ['hello', 'bye', 'good-bye', 'hi', 'hey!', 'good night']"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"['bye', 'good night', 'good-bye', 'hello', 'hey!', 'hi']"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"heap_sort(data)\n",
"data"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"['hi', 'bye', 'hey!', 'hello', 'good-bye', 'good night']"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"heap_sort(data, key=len)\n",
"data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### string array treated as hexadecimal values"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"data = ['ff', '100', 'ac', '5', '99cc', '393', '000152']"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"['000152', '100', '393', '5', '99cc', 'ac', 'ff']"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"heap_sort(data)\n",
"data"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"['5', 'ac', 'ff', '100', '000152', '393', '99cc']"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"heap_sort(data, key=lambda i: int(i, 16))\n",
"data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 87 - gray code.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def gray_code(n):\n",
" code = [0] * n\n",
" i, parity = 0, 0\n",
"\n",
" while i < n:\n",
" yield code[::-1]\n",
"\n",
" i = parity\n",
" parity ^= 1\n",
" while i and not code[i - 1]:\n",
" i += 1\n",
"\n",
" if i < n:\n",
" code[i] ^= 1 "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0, 0, 0, 0]\n",
"[0, 0, 0, 1]\n",
"[0, 0, 1, 1]\n",
"[0, 0, 1, 0]\n",
"[0, 1, 1, 0]\n",
"[0, 1, 1, 1]\n",
"[0, 1, 0, 1]\n",
"[0, 1, 0, 0]\n",
"[1, 1, 0, 0]\n",
"[1, 1, 0, 1]\n",
"[1, 1, 1, 1]\n",
"[1, 1, 1, 0]\n",
"[1, 0, 1, 0]\n",
"[1, 0, 1, 1]\n",
"[1, 0, 0, 1]\n",
"[1, 0, 0, 0]\n"
]
}
],
"source": [
"for code in gray_code(4):\n",
" print(code)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def plot_code(n):\n",
" code = np.array(list(gray_code(n))).T\n",
"\n",
" plot = figure(x_range=(0, 1), y_range=(0, 1), plot_width=800, plot_height=200)\n",
" plot.axis.visible = False\n",
" plot.image([code], x=0, y=0, dw=1, dh=1, palette=['#ffffff', '#000000'])\n",
"\n",
" show(plot)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"93b8c138-2760-40f7-ab09-6acd018a2bd4\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"93b8c138-2760-40f7-ab09-6acd018a2bd4\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '93b8c138-2760-40f7-ab09-6acd018a2bd4' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"93b8c138-2760-40f7-ab09-6acd018a2bd4\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"93b8c138-2760-40f7-ab09-6acd018a2bd4\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"output_notebook()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot_code(5)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot_code(10)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 88 - perlin noise.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"c7ba96e5-5e4a-4fd3-96c3-3fa654c5c5ff\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"c7ba96e5-5e4a-4fd3-96c3-3fa654c5c5ff\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'c7ba96e5-5e4a-4fd3-96c3-3fa654c5c5ff' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"c7ba96e5-5e4a-4fd3-96c3-3fa654c5c5ff\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"c7ba96e5-5e4a-4fd3-96c3-3fa654c5c5ff\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"from bokeh.palettes import gray\n",
"from bokeh.layouts import layout\n",
"\n",
"output_notebook()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def generate_gradient(seed=None):\n",
" global gradient\n",
" \n",
" seed and np.random.seed(seed)\n",
" gradient = np.random.rand(512, 512, 2) * 2 - 1"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def perlin_noise(size_x, size_y, frequency):\n",
" global gradient\n",
" \n",
" # linear space by frequency\n",
" x = np.tile(np.linspace(0, frequency, size_x, endpoint=False), size_y)\n",
" y = np.repeat(np.linspace(0, frequency, size_y, endpoint=False), size_x)\n",
"\n",
" # gradient coordinates\n",
" x0 = x.astype(int)\n",
" y0 = y.astype(int)\n",
"\n",
" # local coordinate\n",
" x -= x0\n",
" y -= y0\n",
"\n",
" # gradient projections\n",
" g00 = gradient[x0, y0]\n",
" g10 = gradient[x0 + 1, y0]\n",
" g01 = gradient[x0, y0 + 1]\n",
" g11 = gradient[x0 + 1, y0 + 1]\n",
"\n",
" # fade\n",
" t = (3 - 2 * x) * x * x\n",
"\n",
" # linear interpolation\n",
" r = g00[:, 0] * x + g00[:, 1] * y\n",
" s = g10[:, 0] * (x - 1) + g10[:, 1] * y\n",
" g0 = r + t * (s - r)\n",
"\n",
" # linear interpolation\n",
" r = g01[:, 0] * x + g01[:, 1] * (y - 1)\n",
" s = g11[:, 0] * (x - 1) + g11[:, 1] * (y - 1)\n",
" g1 = r + t * (s - r)\n",
"\n",
" # fade\n",
" t = (3 - 2 * y) * y * y\n",
"\n",
" # (bi)linear interpolation\n",
" g = g0 + t * (g1 - g0)\n",
"\n",
" # reshape\n",
" return g.reshape(size_y, size_x)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def banded_perlin_noise(size_x, size_y, frequencies, amplitudes):\n",
" image = np.zeros((size_y, size_x))\n",
"\n",
" for f, a in zip(frequencies, amplitudes):\n",
" image += perlin_noise(size_x, size_y, f) * a\n",
"\n",
" image -= image.min()\n",
" image /= image.max()\n",
"\n",
" return image"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## perlin noise"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"generate_gradient()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plots = []\n",
"\n",
"for frequency in [1, 2, 4, 8, 16, 32, 64, 128]:\n",
" image = perlin_noise(200, 200, frequency)\n",
" \n",
" plot = figure(x_range=(0, 1), y_range=(0, 1), plot_width=200, plot_height=200)\n",
" plot.axis.visible = False\n",
" plot.toolbar_location = None\n",
" plot.min_border = 0\n",
" plot.image([image], x=0, y=0, dw=1, dh=1, palette=gray(256))\n",
" \n",
" plots.append(plot)\n",
"\n",
"show(layout([plots[:4], plots[4:]]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## perlin noise"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"generate_gradient(39320)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"image = banded_perlin_noise(512, 512, [2, 4, 8, 16, 32, 64], [32, 16, 8, 4, 2, 1])"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"start = np.array([0x42, 0x92, 0xc6])\n",
"end = np.array([0xf7, 0xfb, 0xff])\n",
"palette = [\n",
" \"#%02x%02x%02x\" % tuple(((1 - i) * start + i * end).astype(int))\n",
" for i in np.linspace(0, 1, num=256)\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot = figure(x_range=(0, 1), y_range=(0, 1), plot_width=512, plot_height=512)\n",
"\n",
"plot.axis.visible = False\n",
"plot.toolbar_location = None\n",
"plot.min_border = 0\n",
"plot.image([image], x=0, y=0, dw=1, dh=1, palette=palette)\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 89 - bipartiteness.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import networkx as nx\n",
"import matplotlib.pyplot as plt\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def test_bipartiteness(graph):\n",
" partition = {}\n",
" nodes = [(i, None) for i in graph]\n",
"\n",
" while nodes:\n",
" node, color = nodes.pop()\n",
"\n",
" # assign default color\n",
" if color is None:\n",
" color = partition.get(node, 0)\n",
"\n",
" # test bipartiteness\n",
" if node in partition:\n",
" if partition[node] != color:\n",
" raise ValueError('graph is not bipartite')\n",
" continue\n",
"\n",
" # assign partition, DFS\n",
" partition[node] = color\n",
" nodes.extend((i, 1 - color) for i in graph[node])\n",
"\n",
" return partition"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph #1"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"graph = nx.complete_multipartite_graph(5, 5)\n",
"partition = test_bipartiteness(graph)\n",
"partition"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAl8AAAHVCAYAAADPfJ4+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsnXe0JVXxtp93yBnJIEEkZyRLkhyGDILkPANKjpJFogIi\nEkQZkmQRyQyIoCgfoAKKkgyAIAYw/UQlCrzfH7Uvc7lzwwl9wp1bz1pnwdzTvXed0326q3dVvSXb\nJEmSJEmSJO1hVKcNSJIkSZIkGUmk85UkSZIkSdJG0vlKkiRJkiRpI+l8JUmSJEmStJF0vpIkSZIk\nSdpIOl9JkiRJkiRtJJ2vJEmSJEmSNpLOV5IkSZIkSRtJ5ytJkiRJkqSNpPOVJEmSJEnSRtL5SpIk\nSZIkaSPpfCVJkiRJkrSRdL6SJEmSJEnaSDpfSZIkSZIkbSSdryRJkiRJkjaSzleSJEmSJEkbSecr\nSZIkSZKkjaTzlSRJkiRJ0kbS+UqSJEmSJGkj6XwlSZIkSZK0kXS+kiRJkiRJ2kg6X0mSJEmSJG0k\nna8kSZIkSZI2ks5XkiRJkiRJG0nnK0mSJEmSpI2k85UkSZIkSdJG0vlKkiRJkiRpI+l8JUmSJEmS\ntJF0vpIkSZIkSdpIOl9JkiRJkiRtJJ2vJEmSJEmSNpLOV5IkSZIkSRtJ5ytJkiRJkqSNpPOVJEmS\nJEnSRtL5SpIkSZIkaSPpfCVJkiRJkrSRdL6SJEmSJEnayOSdNiBJkoqQpgW2AOYDpgFeA34OPITt\nTpqWdDnSssBawMzA/4BXgTuw/9lRu5JkEkV5TU6SYY60KHAIsCfwHjA18WD1NvAu8HfgLOAa7P90\nyMqk25CmBLYDjgEWBgRMRZxDbxPn0G3AV7Af7ZSZSTIpks5XkgxnpP2Bc4kb5RSDbPk68F9gHexf\nt8O0pIuR5gR+AMwPTD/Ilj2O2DeAo7Dfb4N1STLJk85XkgxXpMOBU4Fpa9zjfcIBWzUdsBGMNDvw\nC2AOBnfYe/M68G1g3wxhJ0nzpPOVJMMRaWPgZmp3vHp4H/grsDD265XblXQ3koDHgaWAKevc+3Xg\nOOzzK7crSUYYWe2YJMOTM+nH8doVmAuYEVgUuHTi/UYRYaadW2pd0q2sDSxCH8frbWAfYAFgBmB5\n4O6J950O+AJSFmolSZOk85Ukww1paWCx/t46BngB+DdwO3ACsczRh+mBz5dVkGRkcTThRH2Id4kS\n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\nGcA1WNRzAdYdeRrfTH2JJ87fh2f+Mg6pqGK1YWNabjYT+HPMpjpOu8d1vhynDVJdVz8R2IXiHqCm\nA7s31NY0RGuVU+5U19WvCrxOKxpxOXinobamf4QmOU6HxCNfjtM2OZXicm9+Af4BPBytOU5boKG2\n5j3gWuB/Rew+AzgkWoscp2PizpfjtEHCTXR7zAHLN3w9A/gXsHNDbY030O64HI053/8tYJ8ZwIEN\ntTVPx2OS43Qs3PlynDZKQ23N88Bg4FPgZ7I7YbOwiNdkYHBDbU0hN12nndFQWzMX2BXLHfyFHMUb\n2Hn1A+aw356AeY7TIfCcL8dp41TX1VcA2wAnA0OwpGhlfmXbVcDlDbU176djoVOuVNfVLw2Mw6Jh\nXbHOBwJ0Ad4CzgfuaaityZik7zhOcbjz5TjtiOq6+iWAZbCE6h+Bjxtqa2ama5VT7lTX1VcBfYGe\nWKT0q4bami/Stcpx2i/ufDmO4ziO4ySI53w5juM4juMkiDtfjuM4juM4CeLOl+M4juM4ToK48+U4\njuM4jpMg7nw5juM4juMkiDtfjuM4juM4CeLOl+M4juM4ToK48+U4juM4jpMg7nw5juM4juMkiDtf\njuM4juM4CeLOl+M4juM4ToK48+U4juM4jpMg7nw5juM4juMkiDtfjuM4juM4CeLOl+M4juM4ToK4\n8+U4juM4jpMg7nw5juM4juMkiDtfjuM4juM4CeLOl+M4juM4ToK48+U4juM4jpMg7nw5juM4juMk\niDtfjuM4juM4CeLOl+M4juM4ToK48+U4juM4jpMg7nw5juM4juMkiDtfjuM4juM4CeLOl+M4juM4\nToK48+U4juM4jpMg7nw5juM4juMkiDtfjuM4juM4CeLOl+M4juM4ToK48+U4juM4jpMg7nw5juM4\njuMkiDtfjuM4juM4CeLOl+M4juM4ToK48+U4juM4jpMg7nw5juM4juMkiDtfjuM4juM4CeLOl+M4\njuM4ToK48+U4juM4jpMg7nw5juM4juMkiDtfjuM4juM4CeLOl+M4juM4ToK48+U4juM4jpMg7nw5\njuM4juMkiDtfjuM4juM4CeLOl+M4juM4ToK48+U4juM4jpMg7nw5juM4juMkiDtfjuM4juM4CeLO\nl+M4juM4ToL8P7sP5hoodhmZAAAAAElFTkSuQmCC\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(10, 8))\n",
"plt.axis('off')\n",
"\n",
"colors = ['steelblue', 'red']\n",
"node_color = [colors[partition[i]] for i in graph]\n",
"pos = nx.circular_layout(graph)\n",
"\n",
"nx.draw_networkx(graph, pos=pos, node_color=node_color)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph #2"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{0: 1, 1: 0, 2: 1, 3: 0, 4: 1, 5: 0, 6: 1, 7: 0, 8: 1, 9: 0}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"graph = nx.circulant_graph(10, [1])\n",
"partition = test_bipartiteness(graph)\n",
"partition"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAl8AAAHVCAYAAADPfJ4+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XeU5VWVt/Fnd4YmJwHJOUkQMDAKKmKJZc5ZEZrxVcac\nx1Jnyhkj6MjoKD2IAUVHMGGhhYJEQZAcBSQLgkjubjpU7fePc51puqtDdde954bns1YvsepXd30X\nVNfddfY5+0RmIkmSpNaYVDuAJElSL7H4kiRJaiGLL0mSpBay+JIkSWohiy9JkqQWsviSJElqIYsv\nSZKkFrL4kiRJaiGLL0mSpBay+JIkSWohiy9JkqQWsviSJElqIYsvSZKkFrL4kiRJaiGLL0mSpBay\n+JIkSWohiy9JkqQWsviSJElqIYsvSZKkFrL4kiRJaiGLL0mSpBay+JIkSWohiy9JkqQWsviSJElq\nIYsvSZKkFrL4kiRJaiGLL0mSpBay+JIkSWohiy9JkqQWsviSJElqIYsvSZKkFrL4kiRJaiGLL0mS\npBay+JIkSWohiy9JkqQWsviSJElqIYsvSZKkFppSO4CkidE3OLQm8CJgS2AN4CHgUuD84YH+rJlN\nbS5iT+CZwHrAQuAe4FQy76+aS+pSkenPZKmT9Q0O7QS8G3grMALMoPxiNR9YBNwHfB44cXig/5FK\nMdVuIqYBrwA+AuwABDCd8j00n/I99DPgaDIvrhVT6kYWX1IH6xscejtwDOWNcupyHp0DPAo8a3ig\n//pWZFMbi3gCcCawFbDWcp78eyH2deCDZI62IJ3U9Sy+pA7VNzj0PmAQWHMlv2SUUoA91QKsh0Vs\nDFwGbMLyC/bFzQF+CByBbxrSarP4kjpQ3+BQH/BjVr7w+rtR4F5gh+GB/jkTHkztLSKAS4DdgWnj\n/Oo5wMfI/MqE55J6jBvupc70GcYovK46+Yv87ebLGVk4n+lrrc82z3gFW+zbt/gjkyhtptcDs1sT\nVW3kQGBHlii85gPvAH4D3A9sT/kGO/TxXzsT+CQRXyNzUQuySl3LlS+pw/QNDu0B/J4xiq9H7rmV\nNdfflMnTZjDnr3dw8Qkf5clv/BTrbL7Dko/+CdjRU5A9JmKIUlPF4h+eA3yBcmJjK+A04HXAVcA2\nj3+FR4C3kPmTZkeVuplzvqTO826W0TJa+wnbMHnajPJ/IoBg7v13j/XopsDTmpRP7ShiU+Bglii8\n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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(10, 8))\n",
"plt.axis('off')\n",
"\n",
"colors = ['steelblue', 'red']\n",
"node_color = [colors[partition[i]] for i in graph]\n",
"pos = nx.circular_layout(graph)\n",
"\n",
"nx.draw_networkx(graph, pos=pos, node_color=node_color)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph #3"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"graph is not bipartite\n"
]
}
],
"source": [
"try:\n",
" graph = nx.complete_multipartite_graph(2, 2, 2)\n",
" test_bipartiteness(graph)\n",
"except ValueError as e:\n",
" print(e)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\nIqIznnJdQsmwvgM8CjwOnEOZWL7RzI+R6zJaJy0xIsZSdl/+ltL6aKYOAG5n5i0xGo8Ci2H/tbPB\nRQypUa7LvwJvBG6mNItdFjgYeN3Mj5LrMlonSVnErIzRp2w2pB9SRDfkuowBlKQsYlZKR/9LmUlH\n/9nwMLAe9tUdjSli2OW6jAGUmrKIWbGvAnah3MjH42HKUPLc+CM6LddlDKCslEXMrmb4MaWOZawH\nmico9Sq7YJ/ai9AihlauyxggScoixqMMMd6TUk9s/reB5SOUHfgXAYflSTyiR3JdxoBIUhYxEWVm\n3ocoHcEXouzG/yXw2ezmiqgk12X0uSRlERERES2QQv+IiIiIFkhSFhEREdECScoiIiIiWiBJWURE\nREQLJCmLiIiIaIEkZREREREtkKQsIiIiogWSlEVERES0QJKyiIiIiBZIUhYRERHRAknKIiIiIlog\nSVlERERECyQpi4iIiGiBJGURERERLZCkLCIiIqIFkpRFREREtECSsoiIiIgWSFIWERER0QJJyiIi\nIiJaIElZRERERAskKYuIiIhogSRlERERES2QpCwiIiKiBZKURURERLRAkrKIiIiIFkhSFhEREdEC\nScoiIiIiWiBJWUREREQLJCmLiIiIaIEkZREREREtkKQsIiIiogWSlEVERES0QJKyiIiIiBZIUhYR\nERHRAknKIiIiIlogSVlERERECyQpi4iIiGiBJGURERERLZCkLCIiIqIFkpRFREREtECSsoiIiIgW\nSFIWERER0QJJyiIiIiJaIElZRERERAskKYuIiIhogSRlERERES2QpCwiIiKiBZKURURERLRAkrKI\niIiIFkhSFhEREdECScoiIiIiWuD/A2XRwWlhoKp9AAAAAElFTkSuQmCC\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(10, 8))\n",
"plt.axis('off')\n",
"\n",
"nx.draw_networkx(graph, pos=nx.circular_layout(graph))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 90 - simple nim - AI.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"from bokeh.plotting import figure, show, output_notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## player: AI"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class Player:\n",
"\n",
" def __init__(self, heap):\n",
" self.history = {}\n",
" self.distribution = np.ones((heap + 1, 3), dtype=int)\n",
" self.cutoff = 1000\n",
"\n",
" def __call__(self, heap):\n",
" # randomize move based on previous games\n",
" dist = self.distribution[heap].cumsum()\n",
" rnd = np.random.randint(dist[2])\n",
" move = 1 if rnd < dist[0] else 2 if rnd < dist[1] else 3\n",
" \n",
" # store move in history\n",
" self.history[heap] = min(heap, move)\n",
" \n",
" return self.history[heap]\n",
"\n",
" def learn(self, winner):\n",
" # update move distribution\n",
" for heap, move in self.history.items():\n",
" if winner is self:\n",
" self.distribution[heap][move - 1] += 1\n",
" else:\n",
" self.distribution[heap][move - 1] -= 1\n",
" self.distribution[heap] += 1\n",
"\n",
" # normalize distribution to speed learning up\n",
" normalize = np.argwhere(self.distribution.sum(axis=1) > self.cutoff)\n",
" for heap in normalize:\n",
" self.distribution[heap] -= self.distribution[heap].min() - 1\n",
"\n",
" # reset game history\n",
" self.history = {}\n",
" \n",
" def strategy(self):\n",
" distribution = self.distribution[1:]\n",
" return distribution.T / distribution.sum(axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## opponents"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def expert_opponent(heap):\n",
" return heap % 4 or min(heap, np.random.randint(1, 4))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def random_opponent(heap):\n",
" return min(heap, np.random.randint(1, 4))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def take_n_opponent(take):\n",
" return lambda heap: min(heap, take)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## training"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def play(heap, player, opponent):\n",
" players = player, opponent\n",
" wins = 0\n",
"\n",
" for game in range(100001):\n",
" # update plot periodically\n",
" if game % 10000 == 0:\n",
" print(game, 'games, W/L ratio', wins / 10000)\n",
" wins = 0\n",
"\n",
" # a single game\n",
" h = heap\n",
" while h:\n",
" h -= players[0](h)\n",
" players = players[1], players[0]\n",
"\n",
" winner = players[1]\n",
" wins += winner is player\n",
" \n",
" # let player learn\n",
" player.learn(winner)\n",
" \n",
" # plot distribution\n",
" plot_strategy(heap, player)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def plot_strategy(heap, player):\n",
" output_notebook()\n",
"\n",
" # data\n",
" take_1, take_2, take_3 = player.strategy()\n",
" take_2 += take_1\n",
" take_3 += take_2\n",
" kwargs = {'x': range(1, heap + 1), 'width': .8}\n",
"\n",
" # plot\n",
" plot = figure(plot_width=600, plot_height=400)\n",
" plot.vbar(**kwargs, bottom=0, top=take_1, legend='take 1', color='#a44444')\n",
" plot.vbar(**kwargs, bottom=take_1, top=take_2, legend='take 2', color='#88a888')\n",
" plot.vbar(**kwargs, bottom=take_2, top=take_3, legend='take 3', color='#ccccac')\n",
" show(plot)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## learning"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"HEAP = 21"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 games, W/L ratio 0.0\n",
"10000 games, W/L ratio 0.0082\n",
"20000 games, W/L ratio 0.0129\n",
"30000 games, W/L ratio 0.0146\n",
"40000 games, W/L ratio 0.0321\n",
"50000 games, W/L ratio 0.0325\n",
"60000 games, W/L ratio 0.1141\n",
"70000 games, W/L ratio 0.4532\n",
"80000 games, W/L ratio 0.4985\n",
"90000 games, W/L ratio 0.4992\n",
"100000 games, W/L ratio 0.4994\n"
]
},
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"bbada34c-95a7-465e-b99f-62634cc35fd5\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"bbada34c-95a7-465e-b99f-62634cc35fd5\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'bbada34c-95a7-465e-b99f-62634cc35fd5' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"bbada34c-95a7-465e-b99f-62634cc35fd5\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"bbada34c-95a7-465e-b99f-62634cc35fd5\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"play(HEAP, Player(HEAP), expert_opponent)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 games, W/L ratio 0.0\n",
"10000 games, W/L ratio 0.8589\n",
"20000 games, W/L ratio 0.949\n",
"30000 games, W/L ratio 0.9633\n",
"40000 games, W/L ratio 0.9634\n",
"50000 games, W/L ratio 0.9642\n",
"60000 games, W/L ratio 0.9674\n",
"70000 games, W/L ratio 0.9684\n",
"80000 games, W/L ratio 0.9675\n",
"90000 games, W/L ratio 0.9691\n",
"100000 games, W/L ratio 0.9716\n"
]
},
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"6c94c40c-3b4b-48f1-afe4-63cc61dd795c\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"6c94c40c-3b4b-48f1-afe4-63cc61dd795c\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '6c94c40c-3b4b-48f1-afe4-63cc61dd795c' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"6c94c40c-3b4b-48f1-afe4-63cc61dd795c\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"6c94c40c-3b4b-48f1-afe4-63cc61dd795c\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"play(HEAP, Player(HEAP), random_opponent)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 games, W/L ratio 0.0\n",
"10000 games, W/L ratio 0.9976\n",
"20000 games, W/L ratio 0.9995\n",
"30000 games, W/L ratio 0.9996\n",
"40000 games, W/L ratio 0.9998\n",
"50000 games, W/L ratio 0.9998\n",
"60000 games, W/L ratio 1.0\n",
"70000 games, W/L ratio 1.0\n",
"80000 games, W/L ratio 0.9999\n",
"90000 games, W/L ratio 1.0\n",
"100000 games, W/L ratio 1.0\n"
]
},
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"c456d954-9ff7-4b1c-a810-4961ac2a2376\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"c456d954-9ff7-4b1c-a810-4961ac2a2376\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'c456d954-9ff7-4b1c-a810-4961ac2a2376' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"c456d954-9ff7-4b1c-a810-4961ac2a2376\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"c456d954-9ff7-4b1c-a810-4961ac2a2376\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"play(HEAP, Player(HEAP), take_n_opponent(1))"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 games, W/L ratio 0.0\n",
"10000 games, W/L ratio 0.9706\n",
"20000 games, W/L ratio 0.9971\n",
"30000 games, W/L ratio 0.9969\n",
"40000 games, W/L ratio 0.9989\n",
"50000 games, W/L ratio 0.9997\n",
"60000 games, W/L ratio 0.9998\n",
"70000 games, W/L ratio 1.0\n",
"80000 games, W/L ratio 1.0\n",
"90000 games, W/L ratio 0.9999\n",
"100000 games, W/L ratio 1.0\n"
]
},
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"cc313b98-f7c0-4a7f-bd9f-01fbfa937930\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"cc313b98-f7c0-4a7f-bd9f-01fbfa937930\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'cc313b98-f7c0-4a7f-bd9f-01fbfa937930' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"cc313b98-f7c0-4a7f-bd9f-01fbfa937930\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"cc313b98-f7c0-4a7f-bd9f-01fbfa937930\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"play(HEAP, Player(HEAP), take_n_opponent(3))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 91 - variations.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from random import randrange"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def ffact(n, k, _cache={}):\n",
" if (n, k) not in _cache:\n",
" f = 1\n",
" for i in range(k):\n",
" f *= n - i\n",
" \n",
" _cache[n, k] = f\n",
" \n",
" return _cache[n, k]"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def variation_to_order(variation):\n",
" alphabet = list('0123456789')\n",
" n = len(variation)\n",
"\n",
" order = 1\n",
" order -= ffact(9, n - 1)\n",
" for i in range(1, n):\n",
" order += ffact(10, i) - ffact(9, i - 1)\n",
"\n",
" for i in range(n):\n",
" index = alphabet.index(variation[i])\n",
" order += index * ffact(9 - i, n - i - 1)\n",
" del alphabet[index]\n",
"\n",
" return order"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def order_to_variation(order):\n",
" for n in range(1, 11):\n",
" k = ffact(10, n) - ffact(9, n - 1)\n",
" if k >= order:\n",
" break\n",
" order -= k\n",
"\n",
" order -= (n != 1)\n",
" alphabet = list('0123456789')\n",
" variation = ''\n",
"\n",
" for i in range(n):\n",
" k = ffact(9 - i, n - i - 1)\n",
" index = order // k + (i == 0) - (n == 1)\n",
" order %= k\n",
" variation += alphabet[index]\n",
" del alphabet[index]\n",
"\n",
" return variation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"8877690"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"variation_to_order('9876543210')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" variation order\n",
" 598604732 ## 4158906\n",
" 594216380 ## 4141709\n",
" 63270914 ## 1687099\n",
"2486703591 ## 6129220\n",
" 948073165 ## 5446104\n",
" 72149586 ## 1845426\n",
"5647981230 ## 7288636\n",
"3249076158 ## 6432655\n",
"2768310954 ## 6245640\n",
"9312078654 ## 8641626\n"
]
}
],
"source": [
"print(' variation order')\n",
"for _ in range(10):\n",
" i = randrange(8877691)\n",
" variation = order_to_variation(i)\n",
" order = variation_to_order(variation)\n",
" assert i == order\n",
" print('%10s ## %d' % (variation, order))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 92 - PCA.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"f981697c-83d6-4fdb-91b7-84e9dde6b213\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"f981697c-83d6-4fdb-91b7-84e9dde6b213\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'f981697c-83d6-4fdb-91b7-84e9dde6b213' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"f981697c-83d6-4fdb-91b7-84e9dde6b213\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"f981697c-83d6-4fdb-91b7-84e9dde6b213\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import numpy as np\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"\n",
"output_notebook()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def PCA(X, n_components):\n",
" # normalize to zero mean\n",
" mu = X.mean(axis=0)\n",
" X = X - mu\n",
" \n",
" # eigenvectors of covariance matrix\n",
" sigma = X.T @ X\n",
" eigvals, eigvecs = np.linalg.eig(sigma)\n",
" \n",
" # principal components\n",
" order = np.argsort(eigvals)[::-1]\n",
" components = eigvecs[:, order[:n_components]]\n",
" \n",
" # projection\n",
" Z = X @ components\n",
" \n",
" # result\n",
" return Z, components"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2D data & principal components"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# generate points\n",
"x = np.linspace(0, 13, num=100)\n",
"y = x + np.sin(x) - np.cos(x)\n",
"\n",
"# 2D data\n",
"X = np.c_[x, y]"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0.72299657, -0.69085162],\n",
" [ 0.69085162, 0.72299657]])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# PCA\n",
"projection, components = PCA(X, n_components=2)\n",
"\n",
"# principal components\n",
"components"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 2705.081, 0. ],\n",
" [ 0. , 47.716]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# convariance matrix of projected data\n",
"(projection.T @ projection).round(3)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# prepare plot data\n",
"mean = np.mean(X, axis=0)\n",
"extent = projection.min(), projection.max()\n",
"angle = np.arctan(components[1] / components[0]) + np.pi * (components[0] < 0)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# plot original data & principal components\n",
"plot = figure()\n",
"\n",
"plot.scatter(x, y)\n",
"plot.ray(*mean, length=0, angle=angle[0], line_width=2, line_color='red')\n",
"plot.ray(*mean, length=0, angle=angle[1], line_width=2, line_color='green')\n",
"\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# plot projected data\n",
"plot = figure(x_range=extent, y_range=extent)\n",
"plot.scatter(projection[:, 0], projection[:, 1])\n",
"show(plot)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## binary vectors & dimensionality reduction"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# generate binary vectors\n",
"X = np.random.rand(90, 30)\n",
"X[:30, :] = X[:30, :] < ([.4] * 10 + [.1] * 10 + [.1] * 10)\n",
"X[30:60, :] = X[30:60, :] < ([.1] * 10 + [.4] * 10 + [.1] * 10)\n",
"X[60:, :] = X[60:, :] < ([.1] * 10 + [.1] * 10 + [.4] * 10)\n",
"\n",
"# define 3 classes\n",
"Y = ['red'] * 30 + ['green'] * 30 + ['blue'] * 30"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# PCA\n",
"projection, _ = PCA(X, n_components=2)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# plot projected data: 30D -> 2D\n",
"plot = figure()\n",
"plot.scatter(projection[:, 0], projection[:, 1], color=Y)\n",
"show(plot)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 93 - first and follow.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def first_and_follow(grammar):\n",
" # first & follow sets, epsilon-productions\n",
" first = {i: set() for i in grammar.nonterminals}\n",
" first.update((i, {i}) for i in grammar.terminals)\n",
" follow = {i: set() for i in grammar.nonterminals}\n",
" epsilon = set()\n",
"\n",
" while True:\n",
" updated = False\n",
" \n",
" for nt, expression in grammar.rules:\n",
" # FIRST set w.r.t epsilon-productions\n",
" for symbol in expression:\n",
" updated |= union(first[nt], first[symbol])\n",
" if symbol not in epsilon:\n",
" break\n",
" else:\n",
" updated |= union(epsilon, {nt})\n",
" \n",
" # FOLLOW set w.r.t epsilon-productions\n",
" aux = follow[nt]\n",
" for symbol in reversed(expression):\n",
" if symbol in follow:\n",
" updated |= union(follow[symbol], aux)\n",
" if symbol in epsilon:\n",
" aux = aux.union(first[symbol])\n",
" else:\n",
" aux = first[symbol]\n",
" \n",
" if not updated:\n",
" return first, follow, epsilon"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def union(first, begins):\n",
" n = len(first)\n",
" first |= begins\n",
" return len(first) != n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class Grammar:\n",
" \n",
" def __init__(self, *rules):\n",
" self.rules = tuple(self._parse(rule) for rule in rules)\n",
"\n",
" def _parse(self, rule):\n",
" return tuple(rule.replace(' ', '').split('::='))\n",
" \n",
" def __getitem__(self, nonterminal):\n",
" yield from [rule for rule in self.rules if rule[0] == nonterminal]\n",
" \n",
" @staticmethod\n",
" def is_nonterminal(symbol):\n",
" return symbol.isalpha() and symbol.isupper()\n",
" \n",
" @property\n",
" def nonterminals(self):\n",
" return set(nt for nt, _ in self.rules)\n",
" \n",
" @property\n",
" def terminals(self):\n",
" return set(\n",
" symbol\n",
" for _, expression in self.rules\n",
" for symbol in expression\n",
" if not self.is_nonterminal(symbol)\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## left-recursive grammar w/ epsilon-production"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"first, follow, epsilon = first_and_follow(Grammar(\n",
" '^ ::= A $',\n",
" 'A ::= ABBC',\n",
" 'A ::= B',\n",
" 'A ::= 1',\n",
" 'B ::= C',\n",
" 'B ::= 2',\n",
" 'C ::= 3',\n",
" 'C ::= ',\n",
"))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'$': {'$'},\n",
" '1': {'1'},\n",
" '2': {'2'},\n",
" '3': {'3'},\n",
" 'A': {'1', '2', '3'},\n",
" 'B': {'2', '3'},\n",
" 'C': {'3'},\n",
" '^': {'$', '1', '2', '3'}}"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"first"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'A': {'$', '2', '3'}, 'B': {'$', '2', '3'}, 'C': {'$', '2', '3'}, '^': set()}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"follow"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'A', 'B', 'C'}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"epsilon"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## arithmetic expressions"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"first, follow, epsilon = first_and_follow(Grammar(\n",
" '^ ::= E $',\n",
" 'E ::= E + T',\n",
" 'E ::= T',\n",
" 'T ::= T * F',\n",
" 'T ::= F',\n",
" 'F ::= ( E )',\n",
" 'F ::= x',\n",
"))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'$': {'$'},\n",
" '(': {'('},\n",
" ')': {')'},\n",
" '*': {'*'},\n",
" '+': {'+'},\n",
" 'E': {'(', 'x'},\n",
" 'F': {'(', 'x'},\n",
" 'T': {'(', 'x'},\n",
" '^': {'(', 'x'},\n",
" 'x': {'x'}}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"first"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{'E': {'$', ')', '+'},\n",
" 'F': {'$', ')', '*', '+'},\n",
" 'T': {'$', ')', '*', '+'},\n",
" '^': set()}"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"follow"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"set()"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"epsilon"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 94 - earley parser.ipynb
================================================
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class EarleyParser:\n",
"\n",
" def __init__(self, grammar):\n",
" self.grammar = grammar\n",
" self.states = []\n",
"\n",
" def parse(self, text):\n",
" self.states = [set() for _ in range(len(text) + 1)]\n",
" self.states[0].add(State(*grammar.start))\n",
"\n",
" for k, token in enumerate(text + '\\u0000'):\n",
" extension = list(self.states[k])\n",
" self.states[k].clear()\n",
"\n",
" while extension:\n",
" state = extension.pop()\n",
" if state in self.states[k]:\n",
" continue\n",
"\n",
" self.states[k].add(state)\n",
"\n",
" if state.finished:\n",
" self._completer(state, extension)\n",
" elif state.symbol_is_nonterminal:\n",
" self._predictor(state, k, extension)\n",
" else:\n",
" self._scanner(state, k, token)\n",
"\n",
" self._print(text)\n",
"\n",
" def _predictor(self, state, origin, extension):\n",
" for rule in self.grammar[state.symbol]:\n",
" extension.append(State(*rule, origin=origin))\n",
"\n",
" def _scanner(self, state, origin, token):\n",
" if state.symbol == token:\n",
" self.states[origin + 1].add(state.shift)\n",
"\n",
" def _completer(self, state, extension):\n",
" for reduce in self.states[state.origin]:\n",
" if state.nonterminal == reduce.symbol:\n",
" extension.append(reduce.shift)\n",
"\n",
" def _print(self, text):\n",
" for k, state in enumerate(self.states):\n",
" accepts = any(s.nonterminal == '^' and s.finished for s in state)\n",
"\n",
" print('(%d)' % k, end=' ')\n",
" print('\"%s.%s\"' % (text[:k], text[k:]), end=' ')\n",
" print(accepts and 'ACCEPTS' or '')\n",
"\n",
" for i in state:\n",
" print('\\t', i)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class State:\n",
"\n",
" def __init__(self, nonterminal, expression, dot=0, origin=0):\n",
" self.nonterminal = nonterminal\n",
" self.expression = expression\n",
" self.dot = dot\n",
" self.origin = origin\n",
"\n",
" @property\n",
" def finished(self):\n",
" return self.dot >= len(self.expression)\n",
"\n",
" @property\n",
" def symbol(self):\n",
" return None if self.finished else self.expression[self.dot]\n",
"\n",
" @property\n",
" def symbol_is_nonterminal(self):\n",
" return self.symbol and self.symbol.isalpha() and self.symbol.isupper()\n",
"\n",
" @property\n",
" def shift(self):\n",
" return State(self.nonterminal, self.expression, self.dot + 1, self.origin)\n",
"\n",
" @property\n",
" def tuple(self):\n",
" return self.nonterminal, self.expression, self.dot, self.origin\n",
"\n",
" def __hash__(self):\n",
" return hash(self.tuple)\n",
"\n",
" def __eq__(self, other):\n",
" return self.tuple == other.tuple\n",
"\n",
" def __str__(self):\n",
" n, e, d, o = self.tuple\n",
" return '[%d] %s -> %s.%s' % (o, n, e[:d], e[d:])"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class Grammar:\n",
"\n",
" def __init__(self, *rules):\n",
" self.rules = tuple(self._parse(rule) for rule in rules)\n",
"\n",
" def _parse(self, rule):\n",
" return tuple(rule.replace(' ', '').split('::='))\n",
" \n",
" @property\n",
" def start(self):\n",
" return next(self['^'])\n",
"\n",
" def __getitem__(self, nonterminal):\n",
" yield from [rule for rule in self.rules if rule[0] == nonterminal]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## grammar: arithmetic expression"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"grammar = Grammar(\n",
" '^ ::= E',\n",
" 'E ::= E + T',\n",
" 'E ::= E - T',\n",
" 'E ::= T',\n",
" 'T ::= T * F',\n",
" 'T ::= T / F',\n",
" 'T ::= F',\n",
" 'F ::= ( E )',\n",
" 'F ::= - F',\n",
" 'F ::= x',\n",
" 'F ::= y',\n",
" 'F ::= z',\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(0) \".x-x*(y+z)\" \n",
"\t [0] E -> .T\n",
"\t [0] F -> .(E)\n",
"\t [0] E -> .E-T\n",
"\t [0] E -> .E+T\n",
"\t [0] F -> .x\n",
"\t [0] F -> .z\n",
"\t [0] F -> .-F\n",
"\t [0] T -> .T*F\n",
"\t [0] ^ -> .E\n",
"\t [0] T -> .F\n",
"\t [0] T -> .T/F\n",
"\t [0] F -> .y\n",
"(1) \"x.-x*(y+z)\" ACCEPTS\n",
"\t [0] E -> T.\n",
"\t [0] E -> E.+T\n",
"\t [0] F -> x.\n",
"\t [0] T -> F.\n",
"\t [0] T -> T./F\n",
"\t [0] T -> T.*F\n",
"\t [0] ^ -> E.\n",
"\t [0] E -> E.-T\n",
"(2) \"x-.x*(y+z)\" \n",
"\t [2] F -> .-F\n",
"\t [2] T -> .T*F\n",
"\t [2] T -> .F\n",
"\t [2] T -> .T/F\n",
"\t [2] F -> .y\n",
"\t [2] F -> .(E)\n",
"\t [0] E -> E-.T\n",
"\t [2] F -> .x\n",
"\t [2] F -> .z\n",
"(3) \"x-x.*(y+z)\" ACCEPTS\n",
"\t [2] T -> F.\n",
"\t [2] T -> T.*F\n",
"\t [2] T -> T./F\n",
"\t [0] E -> E.+T\n",
"\t [0] E -> E-T.\n",
"\t [0] ^ -> E.\n",
"\t [0] E -> E.-T\n",
"\t [2] F -> x.\n",
"(4) \"x-x*.(y+z)\" \n",
"\t [4] F -> .y\n",
"\t [4] F -> .x\n",
"\t [4] F -> .-F\n",
"\t [4] F -> .z\n",
"\t [2] T -> T*.F\n",
"\t [4] F -> .(E)\n",
"(5) \"x-x*(.y+z)\" \n",
"\t [5] E -> .T\n",
"\t [5] F -> .(E)\n",
"\t [5] F -> .y\n",
"\t [5] E -> .E+T\n",
"\t [4] F -> (.E)\n",
"\t [5] T -> .T/F\n",
"\t [5] F -> .x\n",
"\t [5] T -> .F\n",
"\t [5] E -> .E-T\n",
"\t [5] F -> .-F\n",
"\t [5] F -> .z\n",
"\t [5] T -> .T*F\n",
"(6) \"x-x*(y.+z)\" \n",
"\t [5] T -> T./F\n",
"\t [5] E -> T.\n",
"\t [5] E -> E.+T\n",
"\t [5] E -> E.-T\n",
"\t [5] T -> T.*F\n",
"\t [5] F -> y.\n",
"\t [5] T -> F.\n",
"\t [4] F -> (E.)\n",
"(7) \"x-x*(y+.z)\" \n",
"\t [7] T -> .F\n",
"\t [7] F -> .-F\n",
"\t [7] T -> .T*F\n",
"\t [7] F -> .z\n",
"\t [7] F -> .(E)\n",
"\t [7] F -> .y\n",
"\t [5] E -> E+.T\n",
"\t [7] T -> .T/F\n",
"\t [7] F -> .x\n",
"(8) \"x-x*(y+z.)\" \n",
"\t [7] T -> T.*F\n",
"\t [5] E -> E.+T\n",
"\t [7] T -> F.\n",
"\t [5] E -> E.-T\n",
"\t [7] T -> T./F\n",
"\t [7] F -> z.\n",
"\t [4] F -> (E.)\n",
"\t [5] E -> E+T.\n",
"(9) \"x-x*(y+z).\" ACCEPTS\n",
"\t [4] F -> (E).\n",
"\t [2] T -> T.*F\n",
"\t [2] T -> T./F\n",
"\t [0] E -> E.+T\n",
"\t [0] E -> E-T.\n",
"\t [0] ^ -> E.\n",
"\t [0] E -> E.-T\n",
"\t [2] T -> T*F.\n"
]
}
],
"source": [
"EarleyParser(grammar).parse('x-x*(y+z)')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(0) \".x-(y/x+y/z)/y*-z\" \n",
"\t [0] E -> .T\n",
"\t [0] F -> .(E)\n",
"\t [0] E -> .E-T\n",
"\t [0] E -> .E+T\n",
"\t [0] F -> .x\n",
"\t [0] F -> .z\n",
"\t [0] F -> .-F\n",
"\t [0] T -> .T*F\n",
"\t [0] ^ -> .E\n",
"\t [0] T -> .F\n",
"\t [0] T -> .T/F\n",
"\t [0] F -> .y\n",
"(1) \"x.-(y/x+y/z)/y*-z\" ACCEPTS\n",
"\t [0] E -> T.\n",
"\t [0] E -> E.+T\n",
"\t [0] F -> x.\n",
"\t [0] T -> F.\n",
"\t [0] T -> T./F\n",
"\t [0] T -> T.*F\n",
"\t [0] ^ -> E.\n",
"\t [0] E -> E.-T\n",
"(2) \"x-.(y/x+y/z)/y*-z\" \n",
"\t [2] F -> .-F\n",
"\t [2] T -> .T*F\n",
"\t [2] T -> .F\n",
"\t [2] T -> .T/F\n",
"\t [2] F -> .y\n",
"\t [2] F -> .(E)\n",
"\t [0] E -> E-.T\n",
"\t [2] F -> .x\n",
"\t [2] F -> .z\n",
"(3) \"x-(.y/x+y/z)/y*-z\" \n",
"\t [3] T -> .T/F\n",
"\t [3] F -> .x\n",
"\t [3] F -> .z\n",
"\t [3] F -> .(E)\n",
"\t [3] E -> .E-T\n",
"\t [3] F -> .-F\n",
"\t [3] T -> .T*F\n",
"\t [3] E -> .E+T\n",
"\t [2] F -> (.E)\n",
"\t [3] F -> .y\n",
"\t [3] E -> .T\n",
"\t [3] T -> .F\n",
"(4) \"x-(y./x+y/z)/y*-z\" \n",
"\t [3] F -> y.\n",
"\t [3] T -> F.\n",
"\t [3] E -> T.\n",
"\t [3] E -> E.+T\n",
"\t [2] F -> (E.)\n",
"\t [3] T -> T./F\n",
"\t [3] E -> E.-T\n",
"\t [3] T -> T.*F\n",
"(5) \"x-(y/.x+y/z)/y*-z\" \n",
"\t [5] F -> .(E)\n",
"\t [3] T -> T/.F\n",
"\t [5] F -> .y\n",
"\t [5] F -> .x\n",
"\t [5] F -> .-F\n",
"\t [5] F -> .z\n",
"(6) \"x-(y/x.+y/z)/y*-z\" \n",
"\t [5] F -> x.\n",
"\t [2] F -> (E.)\n",
"\t [3] E -> T.\n",
"\t [3] E -> E.+T\n",
"\t [3] T -> T./F\n",
"\t [3] T -> T/F.\n",
"\t [3] E -> E.-T\n",
"\t [3] T -> T.*F\n",
"(7) \"x-(y/x+.y/z)/y*-z\" \n",
"\t [7] T -> .F\n",
"\t [7] F -> .-F\n",
"\t [7] T -> .T*F\n",
"\t [7] F -> .z\n",
"\t [3] E -> E+.T\n",
"\t [7] F -> .(E)\n",
"\t [7] F -> .y\n",
"\t [7] T -> .T/F\n",
"\t [7] F -> .x\n",
"(8) \"x-(y/x+y./z)/y*-z\" \n",
"\t [7] T -> T.*F\n",
"\t [3] E -> E+T.\n",
"\t [7] F -> y.\n",
"\t [7] T -> F.\n",
"\t [2] F -> (E.)\n",
"\t [3] E -> E.+T\n",
"\t [7] T -> T./F\n",
"\t [3] E -> E.-T\n",
"(9) \"x-(y/x+y/.z)/y*-z\" \n",
"\t [9] F -> .(E)\n",
"\t [7] T -> T/.F\n",
"\t [9] F -> .z\n",
"\t [9] F -> .-F\n",
"\t [9] F -> .x\n",
"\t [9] F -> .y\n",
"(10) \"x-(y/x+y/z.)/y*-z\" \n",
"\t [7] T -> T.*F\n",
"\t [3] E -> E+T.\n",
"\t [2] F -> (E.)\n",
"\t [3] E -> E.+T\n",
"\t [9] F -> z.\n",
"\t [7] T -> T./F\n",
"\t [3] E -> E.-T\n",
"\t [7] T -> T/F.\n",
"(11) \"x-(y/x+y/z)./y*-z\" ACCEPTS\n",
"\t [2] T -> F.\n",
"\t [2] T -> T.*F\n",
"\t [2] T -> T./F\n",
"\t [0] E -> E.+T\n",
"\t [2] F -> (E).\n",
"\t [0] E -> E-T.\n",
"\t [0] ^ -> E.\n",
"\t [0] E -> E.-T\n",
"(12) \"x-(y/x+y/z)/.y*-z\" \n",
"\t [12] F -> .-F\n",
"\t [12] F -> .z\n",
"\t [12] F -> .x\n",
"\t [2] T -> T/.F\n",
"\t [12] F -> .(E)\n",
"\t [12] F -> .y\n",
"(13) \"x-(y/x+y/z)/y.*-z\" ACCEPTS\n",
"\t [12] F -> y.\n",
"\t [2] T -> T./F\n",
"\t [0] E -> E.+T\n",
"\t [2] T -> T.*F\n",
"\t [2] T -> T/F.\n",
"\t [0] E -> E-T.\n",
"\t [0] ^ -> E.\n",
"\t [0] E -> E.-T\n",
"(14) \"x-(y/x+y/z)/y*.-z\" \n",
"\t [14] F -> .(E)\n",
"\t [14] F -> .y\n",
"\t [14] F -> .-F\n",
"\t [14] F -> .z\n",
"\t [2] T -> T*.F\n",
"\t [14] F -> .x\n",
"(15) \"x-(y/x+y/z)/y*-.z\" \n",
"\t [14] F -> -.F\n",
"\t [15] F -> .z\n",
"\t [15] F -> .x\n",
"\t [15] F -> .(E)\n",
"\t [15] F -> .y\n",
"\t [15] F -> .-F\n",
"(16) \"x-(y/x+y/z)/y*-z.\" ACCEPTS\n",
"\t [2] T -> T.*F\n",
"\t [2] T -> T./F\n",
"\t [0] E -> E.+T\n",
"\t [0] E -> E.-T\n",
"\t [0] E -> E-T.\n",
"\t [0] ^ -> E.\n",
"\t [15] F -> z.\n",
"\t [14] F -> -F.\n",
"\t [2] T -> T*F.\n"
]
}
],
"source": [
"EarleyParser(grammar).parse('x-(y/x+y/z)/y*-z')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## grammar: parentheses"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"grammar = Grammar(\n",
" '^ ::= P',\n",
" 'P ::= ( )',\n",
" 'P ::= ( P )',\n",
" 'P ::= P ( )',\n",
" 'P ::= P ( P )',\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(0) \".()(()())()\" \n",
"\t [0] ^ -> .P\n",
"\t [0] P -> .P(P)\n",
"\t [0] P -> .()\n",
"\t [0] P -> .P()\n",
"\t [0] P -> .(P)\n",
"(1) \"(.)(()())()\" \n",
"\t [0] P -> (.P)\n",
"\t [1] P -> .(P)\n",
"\t [1] P -> .()\n",
"\t [0] P -> (.)\n",
"\t [1] P -> .P()\n",
"\t [1] P -> .P(P)\n",
"(2) \"().(()())()\" ACCEPTS\n",
"\t [0] P -> P.(P)\n",
"\t [0] P -> ().\n",
"\t [0] P -> P.()\n",
"\t [0] ^ -> P.\n",
"(3) \"()(.()())()\" \n",
"\t [3] P -> .P()\n",
"\t [3] P -> .P(P)\n",
"\t [0] P -> P(.)\n",
"\t [3] P -> .(P)\n",
"\t [3] P -> .()\n",
"\t [0] P -> P(.P)\n",
"(4) \"()((.)())()\" \n",
"\t [4] P -> .(P)\n",
"\t [3] P -> (.)\n",
"\t [3] P -> (.P)\n",
"\t [4] P -> .P()\n",
"\t [4] P -> .P(P)\n",
"\t [4] P -> .()\n",
"(5) \"()(().())()\" \n",
"\t [3] P -> P.(P)\n",
"\t [0] P -> P(P.)\n",
"\t [3] P -> P.()\n",
"\t [3] P -> ().\n",
"(6) \"()(()(.))()\" \n",
"\t [6] P -> .P()\n",
"\t [6] P -> .()\n",
"\t [6] P -> .P(P)\n",
"\t [6] P -> .(P)\n",
"\t [3] P -> P(.P)\n",
"\t [3] P -> P(.)\n",
"(7) \"()(()().)()\" \n",
"\t [3] P -> P.(P)\n",
"\t [0] P -> P(P.)\n",
"\t [3] P -> P.()\n",
"\t [3] P -> P().\n",
"(8) \"()(()()).()\" ACCEPTS\n",
"\t [0] P -> P(P).\n",
"\t [0] P -> P.(P)\n",
"\t [0] P -> P.()\n",
"\t [0] ^ -> P.\n",
"(9) \"()(()())(.)\" \n",
"\t [9] P -> .P(P)\n",
"\t [9] P -> .()\n",
"\t [9] P -> .P()\n",
"\t [0] P -> P(.)\n",
"\t [9] P -> .(P)\n",
"\t [0] P -> P(.P)\n",
"(10) \"()(()())().\" ACCEPTS\n",
"\t [0] P -> P.(P)\n",
"\t [0] P -> P().\n",
"\t [0] P -> P.()\n",
"\t [0] ^ -> P.\n"
]
}
],
"source": [
"EarleyParser(grammar).parse('()(()())()')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 95 - strongly connected components.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import networkx as nx\n",
"import matplotlib.pyplot as plt\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def strongly_connected_components(graph):\n",
" times, _ = depth_first_search(graph.nodes(), graph)\n",
" _, components = depth_first_search(reversed(times), graph.reverse())\n",
"\n",
" return components"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def depth_first_search(nodes, graph):\n",
" times, components, explored = [], [], set()\n",
"\n",
" for node in nodes:\n",
" component = []\n",
" stack = [(False, node)]\n",
"\n",
" while stack:\n",
" complete, i = stack.pop()\n",
"\n",
" # check if already processed\n",
" if complete:\n",
" times.append(i)\n",
" continue\n",
" elif i in explored:\n",
" continue\n",
"\n",
" # mark the node\n",
" component.append(i)\n",
" explored.add(i)\n",
"\n",
" # search in depth\n",
" stack.append((True, i))\n",
" stack.extend((False, i) for i in graph[i])\n",
"\n",
" if component:\n",
" components.append(component)\n",
"\n",
" return times, components"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph #1"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"graph = nx.DiGraph()\n",
"graph.add_nodes_from(range(6))\n",
"graph.add_edges_from([\n",
" (0, 1), (1, 2), (2, 0), \n",
" (3, 4), (4, 5), (5, 3),\n",
" (0, 5), (2, 3),\n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\nRK5CYII=\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(8, 8))\n",
"plt.axis('off')\n",
"nx.draw_networkx(graph, pos=nx.circular_layout(graph), node_size=400, node_color='steelblue', font_color='white')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[0, 2, 1], [5, 4, 3]]"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"strongly_connected_components(graph)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph #2"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"graph = nx.DiGraph()\n",
"graph.add_nodes_from(range(7))\n",
"graph.add_edges_from([\n",
" (0, 1), (1, 2), (2, 0), \n",
" (0, 3), (3, 4), (4, 0),\n",
" (0, 5), (5, 6), (6, 0),\n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAecAAAHVCAYAAADLvzPyAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XeYnVW5/vHvM5OOBAgdKZJQBBKKiHQhtAChqigWygEU\nBESUI0ePxpbjUWyHoggqUi2UHz1AQgmdCEKQ0HvvnZA+c//+WG/Yk2FmMjO7vPvd+/5cl5fX7PLu\nJyQz96z1rvWskISZmZnVj5a8CzAzM7NFOZzNzMzqjMPZzMyszjiczczM6ozD2czMrM44nM3MzOqM\nw9nMzKzOOJzNzMzqjMPZzMyszjiczczM6ozD2czMrM44nM3MzOqMw9nMzKzOOJzNzMzqjMPZzMys\nzjiczczM6ozD2czMrM44nM3MzOqMw9nMzKzOOJzNzMzqjMPZzMyszjiczczM6ozD2czMrM44nM3M\nzOqMw9nMzKzOOJzNzMzqjMPZzMyszjiczczM6ozD2czMrM44nM3MzOqMw9nMzKzOOJzNzMzqjMPZ\nzMyszjiczczM6ozD2czMrM44nM3MzOqMw9nMzKzODMi7ADOzvIybOGkMcAQwFhgFDALmAU8AU4HT\nJk8YPyO/Cq1ZhaS8azAzq6lxEyeNBM4FNgYGA61dvGwBKainAwdOnjD+ydpVaM3O4WxmTWXcxEn7\nAWfRfSh31gbMBQ6ePGH8hVUszewDDmczaxpZMJ8NDO3H22cDBzmgrRYczmbWFLKp7BnAsDIuMwsY\nPXnC+KcqU5VZ17wgzMyaxbmkqewP+eUBW7DeqkvT1p4GK6+/N4fDTr2pq5cOzq6zTbWKNAOPnM2s\nCYybOGlD4A66GTX/8oAtuGHGC1xz73O9udwsYAuv4rZq8j5nM2sGh9PNqLkfBmXXM6saT2ubWTMY\ny2JWZv/HDutyyI4f5/k3ZnLW1Ee475k3u3vpgOx6ZlXjcDazZjCqpyfPuP5hnn39PRa0ie02WJmf\nfGEzjvzTLbz01qzu3rJW5Us0K/G0tpk1g0E9PfnIi28ze14b89vaue6+F3jwuTfZbK0VenrLwMqW\nZ7Yoh7OZNYN5fXmxgOj5JfPLqMVssRzOZtYMnujuiSUGD2DTkcsxsLWFlgjGjl6FMauP4F9PvNbT\n9R6vfIlmJb7nbGbNYCqwNl38zBvQ2sJBY9dltWU/QrvEc6/P5CcX3M0Lb77f3bUWZNczqxqHs5k1\n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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(8, 8))\n",
"plt.axis('off')\n",
"nx.draw_networkx(graph, node_size=400, node_color='steelblue', font_color='white')"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[0, 6, 5, 4, 3, 2, 1]]"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"strongly_connected_components(graph)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## graph #3"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"graph = nx.DiGraph()\n",
"graph.add_nodes_from(range(7))\n",
"graph.add_edges_from([\n",
" (0, 2), (2, 4), (4, 6), (6, 0),\n",
" (0, 1), (2, 3), (4, 5), (6, 7),\n",
"])"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAe8AAAHVCAYAAADYaHMGAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XfYXFXV/vHvnZCEjlKkSgnFFhDEgoqEkECA0Isg0kSa\ngAVUUF+jQtRX8afIG1oQpAQFpJcACSQQFEVURAGlI713SEh5sn5/7AOGydMzM3vOnPtzXV6XmTPP\nmQXk2Wv2PnuvpYjAzMzMymNA7gDMzMysb5y8zczMSsbJ28zMrGScvM3MzErGydvMzKxknLzNzMxK\nxsnbzMysZJy8zczMSsbJ28zMrGScvM3MzErGydvMzKxknLzNzMxKxsnbzMysZJy8zczMSsbJ28zM\nrGScvM3MzErGydvMzKxknLzNzMxKxsnbzMysZJy8zczMSsbJ28zMrGScvM3MzErGydvMzKxknLzN\nzMxKxsnbzMysZJy8zczMSsbJ28zMrGScvM3MzErGydvMzKxknLzNzMxKxsnbzMysZJy8zczMSsbJ\n28zMrGScvM3MzErGydvMzKxknLzNzMxKxsnbzMysZBbJHYCZ5Td63KT1gUOBEcDawGBgNvAgcCNw\n2uSxY+7MF6GZzU8RkTsGM8tk9LhJQ4GJwIbAEGBgJ2+bS0rkfwf2nTx2zEPNi9DMOuPkbVZRo8dN\n2h04m66Tdq0OYBaw/+SxYy5qYGhm1gMnb7MKKhL3OcBi/fjxmcB+TuBm+Th5m1VMsVR+J7D4Qtxm\nBjBs8tgxD9cnKjPrC29YM6ueiaSl8ncYNHAAR2wzjI3WWo6lFhvMUy+9wa+n3ctfH3yus3sMKe6z\naYNjNbNO+KiYWYWMHjdpA9LmtAWecQ8YIJ57dSbfPPdWdjl+MufcdB//s+tHWHGZTlfWBwIbFbvU\nzazJnLzNquUQOpl1A8ya08F5N9/PM6/MJIA/3/8sT788g3VXXqarew0u7mdmTebkbVYtI+jdznLe\ntcRgVltuCR557rWu3rJIcT8zazInb7NqWbs3bxo4QHxrp424/h+P89gLb3T31nXqE5aZ9YWTt1m1\nDO7pDQKO3mlD5nTM4+Tr7u7p7YPqEpWZ9YmTt1m1zO7pDUdtvwHvXmII4y7+Gx3zejxKOqc+YZlZ\nXzh5m1XIvLlzHuvu+le2HcZ7l1+S713wF2bPndebWz5Qn8jMrC98ztuszUkaDOwEHPLBHb68yqof\n2XKeBgxY4Iv7e5ZZjDEbr8HsuR1ccNSot18/cdKd3HjXk53dei6paYmZNZmTt1mbkjQUOAj4AvBv\n4LTl1934aA0YcDOdVFd79pWZjB43qS8fMRuYUI9YzaxvnLzN2oikQcB2pPPXGwPnAsMj4t633jN6\n3KQ7IuITknp1ZKwz8+Z1hDTgjinf285tQs0y8DNvszYgaXVJxwH/AY4CzgPeGxFfnz9xAzx661VH\nz5s7e6F+92PuHN166leel+QJgFkG/sUzK6li5rwNcCjwSeA3wFYR0eX5LklrARM75rx57lqf2f2z\n9KOrWMfsWdx12Qm89vTDOwAXSNorInrcxW5m9eOZt1nJSFpV0veAh4HvAheTZtlf6SFxrwPcBPz8\nviln7w/sR+oO1tGbz42Ijo45s+Kuy37BM3ff8tbLuwIXSeq05KqZNYaTt1kJSBogaWtJl5Haea4E\nbB8Rm0TE2RExo4effz9pZ/iPIuJkgKIf9/rAraQkPreLH58LzJB066N/vnqzZ+6+pbYN6A7AZZL6\n0xvczPrB/bzNWpiklUi7xQ8GXiDt7j4/Il7vwz2GAVOA70TE2Z29p+gOdgipVvk6pMppc0jnuG8E\nJkweO+bO4n7vBaYC69bc5gZgx56+SJjZwnPyNmsxkgaQkuihwCjSsviEiPhrP+71YeA64OsR8ds6\nxrgyMA14f82l6cB2fflyYWZ95+Rt1iIkrQDsT5plzwBOA34TEa/2834bA9cAh0fExfWKc777r0ia\nbQ+rufRHYJv+xm1mPXPyNstIkoDNSEvW2wKXkZbG/xwL8cspaRPgCuDgiLiiHrF28TnLA9cDG9Zc\nug3YOiJeatRnm1WZk7dZBpKWJe32PhiYR0rYE+uR7CRtClwK7B8R1yzs/XrxecsCk4GP1lz6O7Bl\nRLzQ6BjMqsbJ26xJiln2p0iz7B2Aq0lL47cszCy75jNGAL8D9oqI6+txz15+7jLAtaTz5vO7ExgV\nEc82KxazKnDyNmuwIrHtQ0rag0mz7HPqPSOVtBWpstpnI+Kmet67l5+/FOkLyWY1l/4NjIyIp5od\nk1m7cvI2a4Bilv0xUsLehbSsPAG4qV6z7JrPGwOcBewcEbf09P5GkbQEcCWwRc2l+4EtIuLx5kdl\n1n6cvM3qqJh97kU65rU0cDpwViOXjSXtRPpisENE/LlRn9OHeBYjbbwbXXPpIVICf6T5UZm1Fydv\nszqQ9BHSLHt3UlGT04CpETGvwZ+7OzAeGBMRf2vkZ/WFpEWBi0gdzub3KDAiIh5qflRm7cPJ26yf\niiXiPUlJe0XSLPvXzXq2K+nzwP8jHcn6RzM+sy8kDQbOJz02mN8TpBn4fc2Pyqw9OHmb9ZGkt0qJ\nfg74A2nJenJE9KrBR51i2B/4ET10Ecut6C8+Edij5tLTpAT+7+ZHZVZ+Tt5mvVA8x92d9Cx7deAM\n4MyIeCxDLAcDY0lHsO7t6f25FT2/f03acT+/50i70O9sflRm5ebkbdYNSR8gzbL3JlUNmwBMioiu\nOnA1Op4jgG+Skt4DOWLoj6L3+OnAATWXXiAVcvl786MyKy+3BDWrIWmIpL0kTSc133gd+GhEbBsR\nV2RM3F8HjgKGlylxQ+oFDhwEnFpzaTlgmqSPNz8qs/LyzNusIGldUrnS/YA7SLPsKyNiTtbAAEnf\nITUtGZljqb5eivPvJwBfrbn0Gmnj3R+bH5VZ+XjmbZUmabCkz0qaStp8Ng/4VERsFRGX5E7cSn5A\nel48vMyJG6AoUHMk8LOaS0sBUyTVVmczs0545m2VJGkoaRn3C6TynacBl0fErKyBzaeYpf4I2J60\nOe2ZzCHVTfHPdhzw3ZpLM4HtI2Jq86MyKw/PvK0yJA2StLOk64A/k+qMD4+IERFxYQsm7v8HbEMq\natI2iRvSDDwixgLfq7m0GHC1pK0zhGVWGp55W9uTtDpwIPBFUonOCcDFEfFm1sC6IGkAcCKwCTA6\nIl7MHFJDSToa+GnNy7OB3SLiqgwhmbU8z7ytLUkaKGk7SVeT+kq/i1TQ5DMRcV6LJ+7TgI1JS+Vt\nnbgBIuJ40nPw+Q0GLpW0a4aQzFqeZ97WViStSpphHwg8SUqEv4uIGVkD64XiLPQZwNqkWuWvZQ6p\nqSQdBpxc83IHsHdEXJAhJLOWtUjuAMwWVjFb3YpUTGU4cAFp01PL1fvuSlGF7BxgJWCbiHgjc0hN\nFxGnSJpNKuai4uWBwG8kDY6Ic/NFZ9ZaPPO20pK0Emm3+MGkSl0TgPMj4vWsgfVRUf/7N6QWojtH\nxMzMIWUlaV9Sb/L5H+sFcFBEnJknKrPW4pm3lUoxyx5BqjE+CrgY2D0i/po1sH6SNAS4kDTD3KlV\nn8U3U0ScW8zAzyP9e4E0Ez+jmIHXVmkzqxzPvK0UJK1AqjB2MDCDNMv+TUS8kjOuhVH0vL4EeBP4\nXETMzhxSSyk2q13AgpOMr0XEiRlCMmsZTt7WsoqzzpuRnmVvC1xGStp/jpL/xZW0OHA58CKwT+5K\nbq1K0vak1ZXBNZeOKXapm1WSk7dlJ2k5Uj3xv0bEzZKWBfYlJe15pIQ9MSJeyhhm3UhaErgKeAw4\nIFejk7IoCrZcDgypufS9iBiXISSz7Jy8LYtiVv1pUoLenTQw/wF4mFQOdBIpaf+h7LPs+UlaGrgG\nuBc4uOi2ZT2QNJL0hWexmks/JCXxtvk7YtYbTt7WVJLeRWqycQjwoU7echzwfxHxQlMDa4Lin/06\n4HbgiIiYlzmkUpE0nPSlbomaS8cD33ICtypxhTVruKIz1icknUUqnPJ/dJ64IZW9bsfEvSwwFbgV\nONyJu+8iYjrpPH9t8ZqjgROK1RyzSvDM2xqmWCL+PGmW/eEe3v4wqTjHWe3WhKPYKX8DMJm00cq/\ndAtB0sdJ/y7fVXPpVLyiYRXh5F1ho8dNWp90XnoEqSTnYFJDiAeBG4HTJo8dc2df7ytpY1LC3osF\nlzjn1wFcSXq2fX07DrpFIZmpwKX42WzdSPoIcD2wbM2lM0l7Cdru71IzNGpMsPpz8q6g0eMmDQUm\nAhuSNooN7ORtc0m/tH8H9p08dsxD3d2z2EG9J+kXf+MeQngM+BVwZkQ82bfoy6Oosz6VdB7du6Lr\nTNL6pH+/K8z38v3AJ9vx0UsjNWJMsMZy8q6Y0eMm7Q6cTde/oLU6gFnA/pPHjrmo9qKkDUiz7H2A\npbq5zzz+u4P8unbfZV20IZ0GnO7zyI0j6YOkBL4SKbGcDnzFKxy9V+8xwZrDybtCil/Sc1jwuE1v\nzAT2mzx2zEVFgZHPkpL2Jj383JOkTllnRMRj/fjc0pG0FilxnxgRv8wdT7uTtB7wa+AIUk30qcA3\nncB7Vq8xob5RWW84eVdEsSx2J7B4f+8RMe/NP0/4+gWvPnn/Tiy4WegdbyVtKJoAXF2lIiSS1iUl\nj59ExCm546kKSYqIKHb1Twb+BHzVCbxr9RgTSKWKh00eO+bh+kRlveWjYtUxkQUrVPVJzItF37fN\nQfvTdeJ+BvgxsHZEbBMRl1cscX+AtKnnOCfu5norSUfEi6SGNR8DTisa2VjnFnpMKH5+Yh1isT7y\nzLsCRo+btAFpJtLpN+yjd9qQjdZajiGDBvLS67O46I8Pcd0dna9wd8x+kz+f/nVef/aR+V++gTTL\nvrKqzTUkDQOmkIqFuO90ZpKWIu2xeBA4sN33WPRVT2PCDh9dgy0/vBprvmcpbrr7SX5+5T+7u90M\nYBPvQm8ufyuthkPo5hv2hbc8wP7jb2SX46fwgwv/yn4j1mOdlZbu9L0auAirfWwbgOeBnwHrRsSW\nEXFxhRP3hqQvMEc5cbeGiHgN2AZYHThXktsfv1O3Y8ILr8/it394gCl3PN6bew0u7mdN5ORdDSPo\nZhfpI8+9zqy56VhsABGwyrKdH88eMHARVt5g8yeA1SLi6Ih4oAHxloakj5KesR4eERfkjsf+KyLe\nALYDlgd+K2lQ5pBaSbdjwi33PM2f7n2GV2f26vv4IsX9rIn8bbQa1u7pDUdsM4wtP7waiw4ayP1P\nvcJt9z/b5XsHLbbkChExq64RlpCkTwJXkJZlr8wdjy0oImZK2pHUVvR3kvb0312gF2NCH61T5/tZ\nD5y8q6G2F/ICTrr2Lk657i4+sNq72WCN5ZjT0W2BqsrPYCRtRkoI+0bEdbnjsa5FxJuSdgEuAC6V\ntGtEvJk7rsx6HBP6qPJjQrN52bwaerX2NS/g7sdeYoWlF2W7jdfo7q1z6hNWORXtKS8BPufEXQ7F\nfow9SE1NrixqFVRZvfenVHpMyMHJuxoe7MubBwwQK7+727Gtss+5JY0mzeB2i4ipueOx3ouIOcDe\npCONk4qSvlXVpzGhFyo7JuTi5F0NN5LqEi9gmcUHM/xDK7PooIEMEGw8dHlGfGgV7vjP813da25x\nv8qRtB3pTOtORXtKK5mi7sD+pC521xad76qoyzEBYIDEoIEDGDBA//3/XXdcreyYkJOfeVfDBNKA\n1el/7+02XoOvbLs+Ejz7ykxOm/Ivbr2vyw1rs4v7VYqknYHTgO0i4rbc8Vj/RUSHpAOBk4EpkraO\niJdzx9Vk3Y4Je31mHfYZvt7bfx61wWpMnH4f5918f2dvr+SYkJuLtFTE6HGTbgE+Qe8aD3SlA7h1\n8tgxm9YnqnKQtAdwIrBtRNyeOx6rD0kCfgl8GtiqqM5WGR4Tys3L5tWxD6kT0MKYVdynMiTtDZwA\nbOnE3V6KkqpfIzWRmSZphR5+pN14TCgxJ++KKHrv7k/qBNQfM0ktACvTgEDSAcBPgVER4dKPbahI\n4McAVwM3Slopc0hN4zGh3LxsXjHu3ds7kg4FvkNK3PfljscaT9JY4PPAyIh4Inc8zdLXMWFeRwca\nMGCGpEqNCa3GybuCilaA5wIbkYo1dLZpZS5pI8rfgX2q9O1a0leAo0iDeL2P1FgLk3QMcBCwRUQ8\nmjueZunNmDCvYy7RMZdXn3qIR/90xUFP3/2HM5odp/2Xk3eFjR43aX1SQ4ERpPKGg0jFFh4gHf2Y\nULVOQZK+CRxKGrwf6en91n4kHQl8hfR3oDJfWqHrMeHNV56f+ew9ty7z+F+ufauj4JSIGJ0x1Mpz\n8jYrSPofYF/SjLtX7ZSsPUk6jPQsfGTVm+8ASNoY+GvNy++PiHtzxGPesGaGkmNJzzs3d+K2iDgF\nGAfcJOn9uePJLSL+Rur/Pb8jcsRiiZO3VVpx1vd/gZ1JifupzCFZi4iIM4D/IR0jG5Y7nhZwUs2f\n969whbrsnLytsorE/QtgK2BERHTdB9UqKSLOAb4OXC9pw9zxZHYx8PR8f14S2C9TLJXn5G2VJGkA\naSbxKdJzzRcyh2QtKiLOJy0RT5b00dzx5FJ0Zqstg3pE8btkTeYNa1Y5xWAzAfggqeTpK5lDshKQ\ntANwBrBjRNQ+/60ESSsDj/LOo2SjI2JKppAqy9+YrFIkDQR+DawHbO3Ebb0VEVeSlomvkPSZ3PHk\nUOwJqS3M8uUcsVSdZ95WGZIWIRWieA9p9vRG5pCshCSNAs4H9oiIabnjaTZJnwT+ON9LAawTEQ9l\nCqmSPPO2SpA0GLgAeDewvRO39VdE3ADsBlwgqYqFSm4F5m/SI+DwTLFUlpO3tT1JQ0hLfYOAnSKi\nv40YzACIiOmk44UTJW2XO55mKpq5jK95+QBJS+SIp6qcvK2tSVoMuIxU9nX3iFjYFohmAETELcB2\nwJmSds4dT5NdADw/35/fBeydKZZKcvK2tiVpceBK4BVgz+Koi1ndRMRtwDbAqZI+mzueZomIN4Ff\n1bz85aJ2gjWBk7e1JUlLAtcATwJ7R8TczCFZm4qI20mFfk6UVKXZ56nAvPn+/CFgeKZYKsfJ29qO\npGWAycD9wBcioiNzSNbmIuKfwEjgp5IOyB1PM0TEY8DlNS/72FiT+KiYtRVJ7wauI3VA+nJEzOvh\nR8zqRtJ6wA3AjyKithpZ25G0Oal98FvmAWtVqRd6Lp55W9uQtBwwlXQG9Qgnbmu2iLiP1Av725Kq\nMAudDtw1358HAF/KFEulOHlbW5D0HtIMYApwVHhJyTKJiAdJz36/JukbueNppC6OjR1UnPKwBnLy\nttIr6i3fRDoS9m0nbsstIh4hJfCDJf1P7nga7DfAy/P9eTlgz0yxVIaTt5WapNVIS3e/iYjvO3Fb\nq4iIx0kJ/POSjm3XY1RFtcIza172sbEG84Y1Ky1Ja5CecU+IiJ/ljsesM8UjnRuAScB32vELpqSh\nwAOkUqlv+XRE/LGLH7GF5Jm3lVIxWEwHxjtxWyuLiGeBLYCtgZ+344y0aEoyab6XppGqGlqDeOZt\npTPfcZwfR8RpueMx643iGONk4DbgK+12GkLSCGAXYA1gekT8PHNIbc3J20pF0geB64GxEfHr3PGY\n9UVRQOga4F/AIe2WwAEkbQL8FljXBZIax8vmVhqS1ifNuI9x4rYyiohXSMvn6wG/ljQwc0iN8Gfg\nBWDb3IG0MydvKwVJG5Fm3EdGxHm54zHrr4h4jZTYVgPOlbRI5pDqar6z31UoUpONl82t5Un6OHAV\n8KWIuDR3PGb1UBQyuRR4HdgrItpmg5ekIcCjwPCIuCd3PO3IM29raZI+BVwNfNGJ29pJRMwEdgKG\nABcXCa8tRMQsUsvQw3PH0q4887aWJWk4cBGwT0RMzh2PWSNIGkza4LU4sGuR1EuvKKD0T2DNiHg1\ndzztxjNva0mSRgIXA3s6cVs7i4jZpHKirwBXSlo8c0h1UVSYmwrslzuWduTkbS1H0tbA+aRZyLTc\n8Zg1WkTMBfYGngImSVoyc0j1Mh44QpJzTZ35X6i1FEnbA+cCO0bEzbnjMWuW4kz0F4AHgeskLZ05\npHr4PfAmsGXuQNqNk7e1DEm7AmcAYyLiT7njMWu2IoEfTHpWfH1Rla20fGyscbxhzVqCpD2BE4Bt\nIuKO3PGY5VTUP/8FsBmwVUS8kDmkfiue4T8CbFL0Orc68MzbspO0L2mg2tKJ2+ztGetRpIqC04rO\nZKUUETOAs4DDcsfSTjzztqwkfRE4lpS4/507HrNWUszAjwV2BUZFxFOZQ+oXSWsCfwNWL/p/20Ly\nzNuykXQY8H1ghBO32YIi+R7p9MVNklbNHVN/RMR/gD+QdtRbHTh5WxaSvgZ8k1Q+8f7c8Zi1soj4\nIWkz53RJa+SOp5/GA19ux37mOTh5W9NJOho4gpS4H84dj1kZRMTPSAnwJklDc8fTD1NJOWfzzHG0\nBSdvaypJY4EvkhL3o7njMSuTiDgROJ6UwNfNHU9fFJvwTsLHxurCG9asKYqlsuOAXYCREfF05pDM\nSqusGz2LynGPABv5y/vC8czbGq5I3D8FdgA2d+I2WzgRcSbwbWCqpPVzx9NbEfE6MBH4Uu5Yys4z\nb2uoInGfAHyGkhebMGs1kvYATiQVN/p77nh6Q9J6pJ3na7RLB7UcPPO2himaEZwMbEJaKnfiNquj\niLiQVPzkOkkfyx1Pb0TEfaQz33vmjqXMnLytISQNBE4HNiDNuF/OHJJZW4qIS4EDSd3IPpU7nl7y\nsbGF5ORtdSdpEVI5xLWBrSPi1cwhmbW1iLgK2Be4XNJmuePpheuApYCyfNloOU7eVleSBgHnASuR\nuoO9njkks0qIiOuAzwGXSBqZO57uRMQ80iO1I3LHUlbesGZ1I2kwqYzjosCuEfFm5pDMKqeYeV8M\n7Fsk9JYkaRngYWBYRDyZO56y8czb6kLSENKAMRDYxYnbLI+IuBnYCThX0va54+lKRLwCXAAckjuW\nMvLM2xaapMWAy4DXgL0iYk7mkMwqr9h9fjXwpWJTW8uR9CFS29M1ImJ27njKxDNvWyiSliANEC8A\nn3PiNmsNEfEXYGvgFEkteSwrIu4G/gXsnjuWsnHytn6TtBRwLfAo6fna3Mwhmdl8isItWwK/kLRv\n7ni6MB7XO+8zJ2/rl2KzyWTgHuCLEdGROSQz60RE3AmMBH5c1ERvNVcBK5WlyEyrcPK2PpP0buB6\n4Hbg0OLYh5m1qKJ5yRbA9yW1VF3x4ov/KfjYWJ94w5r1iaTlSYl7GvCN8F8gs9Io+oBPBX5ZtBdt\nCZKWAx4A3hcRz+aOpww887Zek7QicCPpObcTt1nJRMRDwOak0qRHZw7nbUXfg0uAg3LHUhaeeVuv\nSFqZNNu+ADjOidusvCStSvp9nhgRP8wdD4CkDUknV9byqZWeeeZtPZK0GjCd9It+rBO3WblFxBPA\ncOBzko5rhQYhEXEHqeLaTrljKQMnb+uWpDVJiXtCRPw4bzRmVi8R8TQwgpQsf9IKCRwfG+s1L5tb\nlyStTdrc8vOIGJ87HjOrv2Kz2BTg98CROVfWisZGD5OaGv0jVxxl4Jm3dUrS+4CbgB87cZu1r2Kz\n2Ejgk8DJkrLlheJZ92n42FiPPPO2BUj6IOk42Hcj4qzc8ZhZ40laGriGVHjp4Fz1GyS9B7gXWDsi\nXswRQxl45m3vIGkD0lL50U7cZtUREa+SaqGvA5wlaWCmOJ4l7TpvxWpwLcMzb3ubpI+Qvnl/JSJ+\nlzseM2s+SYsDVwDPk3oWNP3YlqSPAxcC67j0cuc88zYAJH2CVHzlUCdus+qKiBnA9sAywAWSBmeI\n4TbgWWC7Zn92WTh5G5I2JTUHOCAiLs8dj5nlFRFvAjsDA4GLJQ3JEIaPjXXDy+YVJ2lz4CLg8xEx\nJXM4ZtZCiqNbvwWWBHaJiJlN/OwhwCPAiKKxis3HM+8KkzSKlLj3cOI2s1rF8+7PAS8BV0laoomf\nPQs4HR8b65Rn3hUlaVvgbGDXiPh95nDMrIUVO8/PBNYCtouI15r0uasAdwNrRsQrzfjMsvDMu4Ik\n7UhK3Ds4cZtZT4od3weQzl9PlrRMkz73SVL1t/2b8Xll4pl3xUjaDTiJVH7wb7njMbPyKKqv/R/w\ncWB0RLzUhM/cFDiL1Os7S+GYVuTkXQGSxgKLkpaffg5s7brBZtYfRQOTn5P6gm8VEc834fNuB74T\nEdc28rPKxMm7jRV/6Y8Dvlu89BrwqYi4K19UZlZ2xdjyY2AMMKqoitbIzzuAtD9nTCM/p0z8zLtN\nFb9cP+W/iRtgKWB0nojMrF0Unce+A1wK3CRp5QZ/5PnAxySt0+DPKQ0n7zZUJO4TgG/WXHoNuLX5\nEZlZu4nkB8B5wHRJqzXws2YCvwYOb9RnlI2XzdtMsaHkJOBLNZdeIW0w+XPzozKzdibp68BhwMiI\n+E+DPmMN0rPvNSLi9UZ8Rpl45t1GirOYp7Ng4n4R2MKJ28waISJ+DvyStIS+doM+4xHgZmCfRty/\nbJy824SkRUjHKWrb6D1HKi94e/OjMrOqiIjxwP+SEvj7GvQx44EjikeDlebk3QaK+sPnseA30meA\nzSPin82PysyqJiImAN8Dpkn6YAM+4kYggC0acO9ScfIuuaJd3wXAHjWXngSGR8S/mh+VmVVVRJwF\nHANMlbRBne8dpD09la937g1rJVZ03bmI1Ht3fo+SnnE/2PyozMxA0mdJ1di2redjO0lLkrqNfaR4\nDl5JnnmXlKTFgCtYMHE/DGzmxG1mOUXE70ibZ6+V9PE63vd14BzS7vbK8sy7hIq2fFey4HOf+0kz\n7sebH5WZ2YIkbUc6o71zRNxSp3uuA/wJWL2ZPcZbiWfeJSNpKeBaFkzc95CecTtxm1nLiIirgb2B\nyyRtXqd7PgDcBuxVj/uVkZN3iRRt+CYDn6m5dBdpV/lTzY/KzKx7ETEF2BO4SNKoOt12PPDlqh4b\nc/IuCUnvBq4HPllz6Q7SOe5nmh+VmVnvRMQ0YBfgt5K2qcMtpwCLA5+uw71Kx8m7BCQtD0wDPlZz\n6S+kZ9wtxv4EAAAgAElEQVQNbclnZlYPEfF7YEfgHEk7LOS95pGOjX25HrGVjTestThJKwI3AMNq\nLv0J2CYiXml+VGZm/Sfpo8Ak4PCIuHgh7rM08B9g/Yh4ok7hlYJn3i2saLN3Ewsm7ptJTUacuM2s\ndCLir6T2xOMl9XvTWUS8CvwWOLResZWFZ94tqmivNw1Yt+bSNGCHiHij+VGZmdWPpGGkZ9ffjohz\n+nmPD5DKpq4REbPqGV8r88y7BUlakzS7rk3c1wHbOXGbWTuIiLtIx15/JOnAft7j38CdwGfrGVur\nc/JuMUU7venAWjWXrgJ2qmpBAjNrTxFxDzACGCvp8H7eZjwV27jm5N1CijZ6NwOr11y6FNitSktC\nZlYdEXE/sDnwdUlH9uMWk4AV6lmGtdU5ebeIon3edGCVmksXAHtGxOzmR2Vm1hwR8TApgR8m6Zg+\n/mwHcDIVmn17w1oLKNrm3QCsUHNpIvCF4i+mmVnbk7QqMBX4TUSM68PPLQs8CLy/CkWrPPPOTNJH\nSDslaxP3mThxm1nFFOe1Nwf2lPTD3pY/jYgXgYuBgxsYXsvwzDsjSZ8g1SpfpubSqcARRQUhM7PK\nkbQCqST09cDR0YtkVaxiXgusGRFzGhxiVp55ZyLp06S/lLWJ+0RS1SEnbjOrrIh4jnSMbATwy97M\nwCPin8ADpBrqbc3JO4OiLd5kYKmaS8cDR/bmG6aZWbsrlsJHAR8HTpHUm5w1HjiioYG1ACfvJiva\n4V0DLFFzaRzwLSduM7P/ioiXSaVUhwG/kjSwhx+5HFhT0oYNDy4jJ+8mkrQtcDWwWM2lsRHxPSdu\nM7MFFTXMtyEVrzpb0iLdvHcuad9QWx8b84a1JpG0I3ARMKjm0jERcXyGkMzMSkXS4qSZ9UvA3l1t\nSis2u90HrBMRLzQxxKbxzLsJJO1GOsJQm7iPdOI2M+udiJgB7EB67HihpMFdvO854EqgX/XSy8Az\n7wYr2t2dC9Q+pzksIk7NEJKZWakVSftC0oRot4h4s5P3fBS4BFi7WEpvK555N5Ck/YDzeGfiDuBA\nJ24zs/4pykV/FpgBXCGpdh/RWz3DnwS2a3J4TeHk3SCSDgLOAuY/mzgP2C8izswTlZlZeyied+8F\nPAdMklR7ggfauNuYl80boGhrd1LNyx2kDRYXZAjJzKwtFUfHzgDWBsZExGvzXRsMPAKMioi7M4XY\nEE7evTB63KT1gUNJlX7WBgYDs0lF8G8ETps8dsydAEU7u1/U3GIuqTPYJU0L2sysIoriLacAHwa2\njohX5rv2g2WHfvgDH93/R8/TizG8LJy8uzF63KShpM5eGwJDWHDTGaTEPBv4+19+/a0/vvSfu75Z\nc302aUPFVQ0N1sysworyqScCnwRGR8SLo8dNGtoxd/aFRHx0wCKDO7oo8PL2GA7sO3nsmIeaGHa/\nOXl3YfS4SbsDZ9N10n6HmDdv3ry5swfcddkJPHP3LW+9/Cawc0Rc17BAzcwMeDuB/wwYuemRZ4xf\n/N0rjaeXYzjp0eYsYP/JY8dc1MAw68Ib1jpRJO5zgMXp3X90NGDAgIGDF2XYzkex4oc+DTAT2M6J\n28ysOYoqld8cOnzPh4YsuewZ9GEML963OHBOkQNammfeNYql8jtJ/xH7pWP2m/x70ml7PnH79RfW\nLzIzM+vJ6HGThkbEnUU1tv6aAQybPHbMw/WKq948817QRNIyS78NGDS4Y9jOX2vL4wlmZi1uoqSF\nGsNJOWBiPYJpFM+85zN63KQNgD/Rw6x7lWUXZ8Ihm/H7fz/N8Zff0dXbZgCblG0Ho5lZWfU0hi+1\n6CCO3H4DNh66PK/MmM1ZN97LjXc92dXtWnoM98z7nQ6hF7PuI7Yexn1PvtLT2wYX9zMzs+bodgw/\nfJsPMbdjHnv84gZ+evkdfHmbYayxwpJdvb2lx3An73caQQ+bG4Z/aGXemDWHvz/8fE/3WqS4n5mZ\nNUeXY/iQQQPZ9AMrc85N9/HmnA7ufuwl/nTfM4xcf9Wu7tXSY7iT9zut3d3FxQcvwr7D12PClH/3\n9n7rLHxIZmbWS12O4asttwQd84InXnzj7dceeuZV1lhhqe7u17JjuJP3O3XaXu4t+26+HpPveIzn\nX1uggU1XaluAmplZ43Q5hi82aCAzZr2z/feMWXNZbPAi3d2vZcdwJ+93mt3VhaErLs1Hhi7Ppbf2\n6eRAp43izcysIbocw2fO6WDxIe/MxUsMGcTM2d12C23ZMbzbrxwV9CDwgc4ufHiNZVlxmcWY+NUt\nAFhs8CIMkFj9wE054ow/dHW/BxoTppmZdaLLMfzxF95g4ACxyrKL8+SLMwBYa8WleOS51zp7+1ta\ndgx38n6nG4F16eTfyzW3P8pNdz/19p93++RQVnzXYoy/5q6u7jW3uJ+ZmTVHl2P4rDkd3HLP0+w7\nfD1OuPpO1llpaT653oocefYfu7pXS4/hXjZ/pwl0sewya+48Xnpj1tv/mzl7LrPnzuOVGV2u0swu\n7mdmZs3R5RgOcNI1dzFk0EB+d9QovrXzRoy/9i4eee71rt7e0mO4i7TUGD1u0i3AJ+h9PdzOdAC3\nTh47ZtP6RGVmZr2x5fcvv40BAz46YMBALcRtWn4M98x7QfuQOsssjFnFfczMrEkkrfzHU45YNubO\nWZjEDSUYw528axS9XPcndQXrs445s+LRW686q5UL2puZtRtJqwHTZzz/xNp3XXYCHbP7PQebSWoL\n2tJjuJN3J4pervuRatt29PLHOoAZrz75wJH3XDNhR0kHNSxAMzN7m6Q1gZtJm9V45u5buOuyX9Ax\nd3ZHRPRpDAf2cz/vEiv+460P3Er6D9rpYcCIefM65s6eV7xv2G1nHH0iqaTedyUd3qx4zcyqSNLa\nwHRgrflff+buW6568+Vnh0nqdgwvXp9BMYaXIXGDN6z1yuhxk9YnFagfQSqXN4h0eP+BeR0d0/88\n4cjdXnv6oS0i4u1zY5LWAqYC4yPihBxxm5m1M0nvA6YBq9RcugTYKyJmQ/djOOk42IRW7R7WFSfv\nOpD0A2CliDi05vXVSQn8zIj4SY7YzMzakaQPkhL3ijWXLgD2iYhuS6eVnZN3HUhaGfgXMDQiXqq5\ntgrpL9hvgXHhf+FmZgtF0gbADcAKNZcmAl/ow3Pu0vIz7zqIiKeAa0m71GuvPQkMBz4LjJO0sEcY\nzMwqS9JHSEvdtYn7TCqSuMEz77qR9EnSt771ImJeJ9dXAK4v/ne0Z+BmZn0j6RPAZGCZmkunAkd0\nNva2K8+86+dW4GVgm84uRsRzwBakDRO/9AzczKz3JH2aNPmpTdy/BA6vUuIGJ++6KWbS44Evd/Oe\nF4FRwMeBUyX537+ZWQ8kbU6acS9Vc+l44KgqrmR62byOJC0KPAp8JiLu7eZ9SwHXAPcDB1XlGY2Z\nWV9JGgVcCSxWc2kc8P0qJm7wzLuuIuJN4Ayg2+IsEfEasDWwJnCOJLdmNTOrIWlb4GoWTNxjI+J7\nVU3c4Jl33Ul6L/APYI0iSXf33sWBy4GXgL0jYk4TQjQza3mSdgQuIhVUmd/REfGzDCG1FM+86ywi\nHiOd6963F++dAewALAFcKGlwg8MzM2t5knYDLmbBxP01J+7EybsxxgNH9GZHebHUvgsg4NLiubmZ\nWSVJ2otUJa32ceJhEXFihpBakpN3Y9xMqps7qjdvLurvfhZ4A7iiWE43M6sUSfsB5wED53s5gAMj\n4tQ8UbUmJ+8G6M2xsU5+Zg7weeA54GpJSzQoPDOzllO0UT6LtAr5lnnAfhFxZp6oWpc3rDVIkXwf\nAT4eEQ/14ecGknasrw2M6WnTm5lZ2RXtk0+qebkD+HxEXJghpJbnmXeDRMQbwNnAYX38uQ7gi6RG\nJ1Mkvav+0ZmZtQZJR7Jg4p4LfNaJu2ueeTeQpKHAbcDqxc7yvvysgBOBTwFbFdXZzMzahqRvAf9b\n8/JsYLeIuCpDSKXhmXcDFcvlfyQ9y+7rzwbwVeAmYJqk5esbnZlZHkq+x4KJ+01gRyfunjl5N954\n4Mv9aURSJPBvApOAmyTVNp03MyuVYiz8IXBszaWZwHYRcV3zoyofJ+/GuwEYDGzWnx8uEvh3gd+R\nEvgqdYzNzKxpisR9PPCdmktvAFtHxNTmR1VOTt4NViTfk+jDsbHO7hERxwHnANOLEqxmZqVRJO5f\nAt+oufQqaV/Pzc2Pqry8Ya0Jii5ijwAfLsqnLsy9jiR9ERgZEQ/XIz4zs0Yq2h+fAhxSc+llUuL+\nS/OjKjfPvJugOKt9HvClOtzrBODnpCX0dRb2fmZmjTRf7YraxP0CsIUTd/945t0kktYD/kA6NvZm\nHe53MDAW2DIi7lnY+5mZ1VvR7vhsFjxx8xxp9fDOpgfVJjzzbpKIuA+4HdijTvc7nZS8p0kaVo97\nmpnVi6RBwG9YMHE/DWzuxL1wnLybq9/HxjoTEWeTNn9cL+nD9binmdnCKtobX0hquDS/J4DhEfGv\n5kfVXpy8m+ta4N3AJvW6YUT8FvgKqZTqxvW6r5lZfxRtjS8Fdq659AiwWbEKaQvJybuJImIecDIL\ncWysi/teBBwMXCOpbl8MzMz6omhnfAUwpubSQ6QZd6+bNFn3vGGtyYpGIw8DH4yIp+p8721Jm0N2\niYg/1PPeZmbdKTopXgWMqLl0H2lX+RPNj6p9eebdZBHxMulZ0MENuPc1pM0hl0mq/QUyM2uIopbF\ndSyYuP9F2pzmxF1nnnlnUOwOnwKsGRGzG3D/zUnlVPeOiCn1vr+Z2VuK1cRrWXAvz53AqIh4tvlR\ntT/PvDOIiLuAe4BdG3T/m0ibRc6TVPvsycysLiQtS+rfUJu4bwdGOHE3jpN3PuOp88a1+UXELcD2\nwK8l7dSozzGzapK0AjANqD3lchupAMsLzY+qOpy887kKWK2Rx7si4s/AtsBpknZv1OeYWbUUM+4b\ngdr6En8kVX18uflRVYuTdyYRMZdUqL9hs+/ic/4GjAb+T1JtpSMzs/54BfhnzWvTgdER8WqGeCpn\nkdwBVNwZwAOSVoiI5xr1IRHxD0mjSIVcBkfEWY36LDOrhCWA1UnHXtciPffeMSJmZI2qQjzzzigi\nnidVIjqwCZ91N7AFcFzR1MTMrM+K3eXXk2be7we+DWzvxN1cPiqWmaSNSBWJhhZL6Y3+vHWAqcDP\nIuKkRn+embUPScuRjrn+HjgynECy8cw7s4j4O/AosGOTPu8BYDhwlKSvN+Mzzaz8JL2HtLv8Bpy4\ns3Pybg0NPTZWKyL+Q0rgh0j6TrM+18zKSdLKpN3lVwDfcuLOz8vmLaDoe/sfYJuIqN3B2cjPXYW0\nhH4hcKx/Ic2slqRVSTPu8yJiXO54LPHMuwVExBxgAnBEkz/3SWBzUqW3H9Wrz7iZtQdJq5OOgJ3p\nxN1aPPNuEZJWAv5N2rj2UpM/e3nS7tFpwDc8AzczSUNJK3MnRsQvc8dj7+SZd4uIiKeBScABGT77\neWAksBmpmIv/XphVmKR1gZuA4524W5Nn3i1E0ieA84F1I6Ijw+cvQ+oOdBdwaETMa3YMZpaXpA+Q\nVuK+HxFn5o7HOucZVgspapE/T6pHnuPzXyGVUn0/cKakgTniMLM8JK1PWir/jhN3a3Pybj0n0cRj\nY7Ui4jVgG1Lpw3MluYSuWQUUBaOuB46KiHNzx2Pd87J5i5E0hFS0ZXhE3JMxjsWAy4BXgc8XO+LN\nrA1J+hhwNXBYRFySOx7rmWfeLSYiZgG/osnHxjqJYyawE7AYcFHxpcLM2oykT5E2yx7oxF0ennm3\nIEmrkYr+r5m7vZ6kwcAFwBBg14h4M2c8ZlY/kjYDLgH2iYjrcsdjveeZdwuKiMdJ9YP3a4FYZgN7\nAK8BV0paPHNIZlYHkkaSEveeTtzl4+TdusYDR7TCmevieffewNPAJElLZg7JzBaCpNGkY6m7RcTU\n3PFY32VPDNalPwAzgS1zBwJQtCv9AvAQcJ2kpTOHZGb9IGl7YCKwU0RMzx2P9Y+Td4sqSpRmPTZW\nqygccxBwJzBF0rsyh2RmfSBpF+AMYLuI+GPueKz/vGGthRXPlx8BNomIB3PH85aigckvgU2BLSPi\nxcwhmVkPJO0BnAhsGxG3547HFo5n3i0sImYAZwGH5Y5lfsWqwNdIlZhulLRC5pDMrBuS9gFOIH3Z\nduJuA555tzhJawJ/BdaIiDfyRvNOxQz8OGAXYGTRXMXMWoikA0i/p1tFxL9yx2P14Zl3i4uI/5A2\nr+2dOZQFRDKWdA78Jkmr5o7JzP5L0peAHwBbOHG3FyfvchgPfLmY6baciBhHWt6fLmn13PGYGUj6\nKnAMsHlE3Jc7HqsvJ+9ymEb6b7V55ji6FBE/BU4mJfC1csdjVmWSvgl8hdQj4aHc8Vj9OXmXQCse\nG+tMRJwA/IyUwNfNHY9ZFUn6LnAgKXE/kjseawxvWCuJoqrZI8BHWv0XUtKBpOdsW0bEvzOHY1YJ\nxWO1Y4FdgVER8VTmkKyBPPMuiYh4nVQV6dDcsfQkIs4AvgNMlTQsdzxm7a5I3D8hdQIc4cTd/jzz\nLpFiKfoW0rGxmbnj6Ymkz5HOlm4dEXfkjsesHRWJ+xfAcNJq1wuZQ7Im8My7RCLiftKZ7z1zx9Ib\nEXE+cDgwWdJHc8dj1m6KxkUnAZ8i1Vpw4q4IJ+/yaeljY7Ui4hJSPfRrJH0ydzxm7aJI3BOADUkz\n7pcyh2RN5ORdPpOBpUjftEshIq4E9gWukLRZ7njMyk7SQFJthXWB0RHxauaQrMmcvEsmIuaRzlO3\n9LGxWhFxHbAXcImkkbnjMSsrSYOA84BVSE1GXs8ckmXgDWslJGkZ4D/AhyLiyczh9Imk4cDFwN4R\nMTl3PGZlImkw8FtgCWCXMmxctcbwzLuEIuIV4HzgkNyx9FVETCcdZ5koabvc8ZiVhaQhpC++g4Cd\nnLirzTPvkpL0QVJLzjUiYnbuePpK0seBq4BDI+Ky3PGYtTJJiwGXAm8Ae5Xxd97qyzPvkio6BN0N\n7J47lv6IiNuAbYBTJe2ROx6zViVpCdIX3ZeAPZ24DZy8y67l6513JyJuB7YCfilpn9zxmLUaSUsB\n1wBPAPtExNzMIVmLcPIut6uAlSR9LHcg/RUR/wRGAj+RdEDueMxaRbExdTJwH/CFiOjIHJK1ECfv\nEit+mU8Bjsgdy8IoHgGMAH4gqeVrt5s1mqR3A9cDfwcOKY6Imr3NG9ZKTtJywAPA+yLi2dzxLAxJ\na5M24f0iIv4vdzxmOUhaHpgC3AR8PTxIWyc88y65opbxWyVISy0iHiQ1V/iqpG/mjses2SS9B5hG\nSt5O3NYlz7zbgKQPA5OAtSJiTu54Fpak1UgD2DkR8aPc8Zg1g6SVSStPFwE/cOK27njm3QYi4h/A\nQ6TiJ6UXEY+TZuCfl3RsWZqwmPVX8YV1OvCbiPi+E7f1xMm7fZT62FitiHgK2BzYGfhfJ3BrV5LW\nICXu073SZL3lZfM2UTQreBgYU8zE20KxIe960uB2lGck1k68SdP6yzPvNlE86z6Nkh8bq1VsyBsJ\nfBo4qehhbFZ6kt5H2lH+Eydu6yvPvNtIsVP1XmDtiHgxdzz1VBSsuAb4Fz73aiVX9Ca4HhgbEb/O\nHY+Vj2cxbaQ4530V8MXcsdRb0Ulta2A94NeSBmYOyaxfJG0A3AAc48Rt/eWZd5spSqX+DlgHGNhu\nTQyKJg1XAM8C+7rWs5WJpI+QVpC+GhEX5o7HysvJuw1Jugt4hpTA14uIWZlDqiu3R7Qychtcqycv\nm7cRJdcCHwK2AFanpC1DuxMRM0ln2gcDF0kakjkks25J+jRwNfBFJ26rByfvNlIco7q35uW2Ofs9\nv2I1YTdgDnBZMRs3azmShgOXkVp6Xp07HmsPXjZvM5LWJbUQnN8nIuK2HPE0mqRFgInA8sCOETEj\nc0hmb5M0Cjgf2CMipuWOx9qHZ95tJiLuB66tebktZ98AxYa1vYGngGskLZk5JDMAJG0D/BbYxYnb\n6s3Juz2dVPPnPSStmCWSJij6mn+B1Bp1cnEm3CwbSTsA55BWg36fOx5rP07e7ek6UiJ7yyDg4Eyx\nNEWRwA8G/gFMkfTuzCFZRUnaFfgVqVTxn3LHY+3JybsNFdXHTq55+dCi/nnbKv65Dwf+CEwt6qKb\nNY2kz5FWvkZHxF9yx2Pty8m7fZ1FOgf9llVIHbraWrHj/ihgCnBjUTLWrOEk7Qf8HNgyIu7IHY+1\nNyfvNlWUEz235uW23bg2vyKBfxu4HLhJ0sqZQ7I2J+lA4EfAFhFxV+54rP35qFgbK5of3F3z8kZV\nmhVI+i6wL2lQfTx3PNZ+JB0OHAOMLE57mDWcZ95tLCL+ReoVPL9KzL7fEhE/JG0emi5pjdzxWHuR\ndCTwDWC4E7c1k2febU7STqTqTm95E1it6JNdGZK+ChxJmoE/lDseKz9JxwAHkf5OPZo7HqsWz7zb\n31XAI/P9eVHgwEyxZBMRJwI/JT0DXy93PFZuksYCB5Bm3E7c1nRO3m2uOP98Ss3LhxVlRSslIk4F\njiXtQv9g7nisfIrmPz8E9iQl7idyx2TV5ORdDWeSlsvfsjqwXaZYsoqIM4Fvkc6Br587HisPSSKt\n3mwPbB4RT2cOySrMybsCiufbv6l5uVIb1+YXEROBrwHXS9oodzzW+orEfQIwkvSM+7nMIVnFecNa\nRUj6MFB7RGxYRNQeJasMSbsApwLbt2vXNVt4kgaQKhZuBGwdES9nDsnMM++qiIh/ALUNEo7IEUur\niIhLSZv3rpb0qdzxWOuRNJB01HB9YCsnbmsVTt7VMr7mz/tKeleWSFpERFxFKuJyhaThueOx1lFs\n6jwbGEqacb+aNyKz/3LyrpbLgfl3xy5OaqVZaRFxHWn38MWSRuaOx/IrmvicB6xI6g72euaQzN7B\nybtCImIOcFrNy4cXz/QqLSKmArsC50vaOnc8lo+kwcCFwFLADhExI3NIZgvwhrWKKbpsPQYMXvI9\na7Dax7ZllQ1HPLbIkMVXBAYDs4EHgRuB0yaPHXNnxnCbrnj2fTnwxWJJ3SpE0qLARUAHsEdEzMoc\nUlONHjdpfeBQYASwNh4TWpaTdwUtvco6l75/zCE7L73SUDRwEAMGDuzsbXNJv7R/B/adPHZMZUqK\nSvoYcDVwWERckjseaw5Ji5G+uL0K7FWsVFXC6HGThgITgQ2BIUBng0Jlx4RW5ORdMaPHTdo95s07\nNyIW7SJp1+oAZgH7Tx475qLGRtc6ivPf1wJHRsT5ueOxxpK0BKmU8FPAfhExN3NITTN63KTdSRvz\nukratSo5JrQaJ+8KKX5JzwEW68ePzwT2q9Iva1GBbTLwrYio7Y1ubULSUsAk0tLwgUVJ4UrwmFBe\nTt4VUSyL3UnaYd5fM4Bhk8eOebg+UbU+SR8Arge+X5RWtTYiaRnSCsudwJciYl7mkJrGY0K5VX6X\ncYVMJC2LLYwhxX0qIyL+Tdq8831Jh+WOx+pH0rLADcDfgEOrlLgLHhNKzDPvChg9btIGwJ/o4hv2\n8ftswgdWexcd89Lfhedfe5MDT5ne1e1mAJtUbceppKHAVODEiPhl7nhs4UhanrSiMg34RlRsIOxu\nTLj8mNHv+PPgRQZy9V8f4ZTJXVZSruSYkFvl2kJW1CH08A375Gvv5ro7HuvNvQYX96tUadWIeEjS\n5qRuZIMj4vjcMVn/SFqRNOO+CvifqiXuQpdjwk4/nfz2/1900EAuOGoUN//7qe7uVckxITcn72oY\nQe92kfbGIsX9KiciHilKqE6TNCQixuWOyfpG0iqkFZQLgOMqmrihl2PCph9YiZffmM1dj77Y3dsq\nOybk5ORdDWv39IYvbPE+Dhj5fh5/4XXOvvFe/vlIt7+s69QvtHKJiCeKBD61qMT1vQongFKR9F7S\nMvlZEfHj3PFk1uOYALDlBqtxwz8f781bKzsm5OLkXQ2Du7t45tR7ePT515jbEQz/0Mocu8fHOOxX\nv+epl7qsCjmo/iGWR0Q8LWkEael1iKRjnMBbm6Q1SYn75Ij4ed5oWkK3YwLAe5ZZjPXXWI5fXP3P\n3tyv0mNCDt5tXg2zu7t475MvM3N2B3M65nHDP5/gX4+9yMfWeU93P1KZylNdiYhnSUuFI4ETJClz\nSNYFSesA04ETnLiTiOh2TAAYuf6q3P3Yizzz8sze3LLyY0KzOXlXw4N9eXMAPWSiBxYilrYRES+Q\nkvcngZPd4KX1SHofqSb3jyKitiVu5UhaWtKXZr70dI8rRaM2WJXr/9GrJXPwmNB0Hmyq4UZSXeIF\nLDFkETYeujyDBg5ggMSIYauw/urL8tcHn+vqXnOL+xkQES8DWwIbAKdLqtfGQFtIkj5EWir/XkSc\nnjuenCRtLOl04EnglOfvv33IvI6uK8B+cLV3s/xSi/L77neZv8VjQgZ+5l0NE4D96eS/9yIDB7Df\niPfx3uWWZF4Ejz3/Osf+7m888eIbXd1rdnE/K0TEq0Ub0auBsyQdUKXa2K1I0oeB60hnuH+TO54c\nJC1J6lN/KLDx/Nce/+u1rLrRSBjYeQoYtcGq/OGep5k5u1eVYj0mZOAiLRUxetykW4BPsHBHxjqA\nWyePHbNpfaJqL5IWB64AXgD2qVJXqlYiaWPgGuDLEfG73PE0W/HF5RBgb1JP8k59/MCfsfRq68WA\nAQMXZr+Gx4RMvGxeHfuQOgEtjFnFfawTETED2B5YGrhA0mBJi0p6V+bQKkPSJ0i1yg+pUuKWtLik\n/SX9CbgD+BJdJ+4ngeNeffKBTQcMGNir3Wjd8JiQiWfeFeIOQs0haQhwIWmVYyCwIrBVscHNGkTS\npsClwBciYlLueJpB0gdJs+x9ge6+JAapQ95pwKS3Hut4TCgvJ++Kce/e5pC0GHA/sGrx0j+BLYsj\nZlZnRenai4DPR8SUzOE0lKRFgV1Jz7J7Wq5+BjgT+FVE/KezN3hMKCcn7woqWgGeC2xEKtbQ2a6V\nufthnZUAAAy0SURBVKSNKH8H9nHLv76R9F2gtnzqv4BREdGrLbzWO5K2BH4L7BER03LH0yiS1iPN\nsvcDluvh7TeQNpFd0Zu9Fx4TysfJu8JGj5u0PmkwGEEqbziIVGzhAdLRjwnuFPT/27v3IC3L847j\n34vDYqWKYzxgbKkGY7AqMWk8VKsiJhDAw2BjUq0YokUaTTutbTNpOziNZNoZ2xyro6BNBENMGqIR\nZRQjjYVJlWpKbG1OHmIONmMM4gGjIHD1j/uxWd/ucnB33+d99v1+ZvzDveHZC3T3t9f93IfXJyLG\nUi6+aD3z+fvA1Mx8sv1VDT8RMZPSNf5uZq6tuZxBVx3BO5tffp3uyM+BzwGLM/N17bv2e0JzGN7S\nEKlWn98KTGsZepwS4D9sf1XDR0ScDVwPnJWZ99ddz2Cqpsb/BvgAsMPjDoF7KV32rZk50EWpagjD\nWxpC1Tfh5cCslqEfUgL88fZX1XwRcS7wj8CszPxm3fUMtuq43W8Dk/r5JRspMw6LM/O77apLncPw\nloZYNfX5Rcr0Z28/oQT4I+2vqrki4nzg48C7M/OhuusZbBHxG8AfAH8EjGsZ/galy16emQPd5qUG\nc5+3NMSqSyDeB7TuO/41YE1EHNH+qpopIuYC/0BZuT9sgjsiRkbEmRFxB/AflNCeQVnV/RxlluHo\nzPydzLzJ4Jadt9QmETGKsqDogpahp4HTM9OFQDsQEZcAV1BW7A+LqeKIOBi4GJhHmYlZBPxzdeAP\nEXEK8OCr/y69yvCW2qi6uOR6ykKk3jZQusn17a+q80XEh4C/oPyQ0+gbrKr/B6ZRVnWfQnmlsmg4\nzSRo6BneUptVV4deQzlko7dnKSexPdD+qjpXRFxOef87NTMbu7c4IsYDF1G67A2ULvvmzNxUa2Fq\nJMNbqkG1mvhTwB+3DD0PzMjMf2t/VZ0nIv6SEninZ+aP6q5nd1U/qE2ldNnvpOw8WJSZD9ZamBrP\n8JZqUgX4VcCftwy9CMzMzDXtr6ozVH83VwDnUTru/6m5pN0SEftTXo1cQvnveR2wLDOfr7UwDRuG\nt1SjKqQWAn/dMvQScGZmrm5/VfWq/k4+BpxN6bifqrmkXVLVfSqly3438FXK1Pi69ButBpnhLXWA\niFgAXNny4ZeB2Zl5Vw0l1aIKwL+nTDG/KzOfrrmknYqIfSnnjc+nXNqxCLgpMzfWWpiGNcNb6hAR\n8RHg71o+vAV4T2beXkNJbVUF96eBEykL956puaR+VbWeSAnss4A7KFPj37DLVjsY3lIHiYg/BT7R\n8uGtlBuzbqmhpLaoFnZdC0ymLNh7tuaS+hQR+wBzKO+yeyhd9hLvale7Gd5Sh4mIy4CrWz68Dbgg\nM79YQ0lDqtr3fAPlFquZmflCzSW9RtVlH0fpss8B7qKE9r122apLX3e2SqpRZl4TEVsoARHVh0cC\nyyKiJzOX1lfd4KpOnVsCHEQ5q/zFmkv6PxGxF/D7lNDeG1gMHJ6ZP6u1MAk7b6ljRcT7KcepRq8P\nJzAvM/+pnqoGT0SMBpZRzvGe3SlHgEbE2ymB/V7gXyjvsldn5vZaC5N6sfOWOlRmLomIV4CllM4b\nSpDfUHXg19ZX3cBExBjKsaCjgbMz8+Wa6xkL/B7l1LsDKF32b2bmT+usS+qPnbfU4SLiPcDN/P8f\ntv8kMz9dQ0kDUt1x/hXKSvr3Vbeu1VXLZEqXfR6wlvKqYlVmbqurJmlX2HlLHS4zl1cd+Jcpneqr\nPhURYzLzqppK220RsSfl8JKNlAV4r9RQw69QpsTnAxMoi+Xempk/bnct0utl5y01RETMAG4FxrQM\nLcjMj9VQ0m6ppqZvB54EPpCZW9v8+Y+gBPYFwL9TuuyV7a5DGgwj6i5A0q7JzDuBMyhHp/a2MCKu\nrLY0daSI2JuyxeoJYG67AjMixkTE+RGxhrL47EXgHZk5MzNvM7jVVHbeUsNExBTKiV5jW4auAj7S\naXuPq4NN7gK+BVzajlXbEXE45SCVC6vPuwhYUcc0vTQU7LylhsnMe4HpQOthJh8GPtlJHXh17vc9\nlGnqDw5lcEdET0S8NyJWUxafbQNOzMxpmfkVg1vDiZ231FARcTywirJPurdrgQ/VvS+5uhbza9U/\nHx6qGYGIeBMwj3IF53co+7K/mpmbh+LzSZ3AzltqqMxcB0wFWi/w+CCwuDovvBYRMR74OrCSIQju\niBgdEedExCpgHWUR35TMPC0zv2Rwa7iz85YaLiLeSpma3q9laClwUbv3LEfEwcBq4AvAwsEM7oiY\nQOmyLwYeo7zLXl73IS9Su9l5Sw2XmQ8BU4CnWoYuBD5fHUPaFlW4/itwY2ZeORjBHREjI+LMiLgD\nWE95TfCuzDw5Mz9vcKsb2XlLw0REvIWyHeqNLUO3AOcN9UlmEXFo9fk/k5mfHITnHUzpsOdR9oYv\nAr7UKWegS3UyvKVhJCImUgJ0QsvQ7cC5Q/UuOCIOo0yVX5WZ1wzgOSOAaZQzxk+hnH++qJpdkFQx\nvKVhJiIOoQT4oS1DdwHnZGbrIS8D/XyTKO/cP5qZ17/OZ4wHLqJ02c9QVozfnJmbBq1QaRgxvKVh\nKCJ+ndIJv7llaDXlFq9BuTc7Io4C7gb+KjNv3M3fO4KyWn4+8E5gOaXLfnAwapOGM8NbGqYi4iBK\nBz6pZWgNcEZmth7ysrvPP4bSzV+emV/Yjd+3PzCXcgLaLyjvspdl5nMDqUfqJoa3NIxFxIGUKe2j\nWobuA2a8GpjTF648mvKe+TRgItBDubLzMcp+7etWLZj1X72e+w7KHu7LMnP5LtQRlHfYfwjMoNws\ndh2wrtOOc5WawPCWhrmI2I9yytkxLUMPHHP+gksOmHT8NdXYGGBkH4/YSgny9cCFd19xxgHACmBe\nZt62k8+9L/B+SpedlMC+KTM3DuCPJHU993lLw1xm/hw4HXjNu+QDjzzp2DdMfNs3M/N4YE/6Dm6A\nUdX4Cbl9+3+PP/rUVZSbwfoM7ihOioilwOPAb1HC+8jM/IzBLQ2cnbfUJSJiHHAn8NsHHnkSR82+\nnJE9rVeD71xu3745RoyYs2rBrC+3PH8fyl3Z8ynT7ouBJdUPD5IGkeEtdZGI2Gvs/hPuOWH+J44b\n2bPHQB71C+Cou6844wngOEpgz6asPL8OuNd32dLQGVV3AZLaJzNfmPbRFduzvH/u8+rQU488iAtO\nOZwD9t6DZzZt5uMrHuLhH29sfc6Yzc9v+BrlWtJxlBXjb8nMnw31n0GS4S11lekLV06OESMnRz/B\n/fZD9+PiqZP421vW870nn2XfvfqeVo+IkT1jxx0y4YSzLv3R/StuqPv6UanbuGBN6i7zKavK+zTn\n1MNZtvZRvvvksySw4YXNbHih7xNVR4wanZNmXjLZ4Jbaz85b6i6n0c+q8hEBb37jOO77/lN87rIp\njB41gvu+9xTX3/MdtmztM59HVc+T1GZ23lJ3mdjfwD5jxzB65AhOPmI8f7bkPi5dvJaJ4/fm/JNb\nT1h9jcMGv0RJO2N4S92lp7+BLVu3AXDbA0/wzKbNPP/SK9xy/w849rD9d/S8tt0VLumXDG+pu/R7\np/eml7fy9HOvvXBsF/Z6vTLgiiTtNsNb6i6P7Wjw7od+wlnHHsK4PXv41T1Gcc7xh7LukR3u/np0\ncMuTtCtcsCZ1l69Trgnt82t/2dpH2HvPHj572RS2bN3Gmm//lJvX9pvPW6vnSWozw1vqLoso13H2\n+bW/bXty9Z0Pc/WdD+/Ks7ZUz5PUZk6bS11k1YJZ/wl8C9g2wEdtA9b3viZUUvsY3lL3mQP0ffLK\nrttcPUdSDQxvqcusWjDrccrU+Us7+aX9eQmYu2rBrB8MWlGSdou3ikldavrClecCN1KOS+3vLu/e\ntlE67rmt14FKai/DW+pi0xeufBOwFHgb5QCXvhaybaUsTlsPzLHjlupneEti+sKVR1MuLTmNcuTp\naMoBLI9StoMtcnGa1DkMb0mSGsYFa5IkNYzhLUlSwxjekiQ1jOEtSVLDGN6SJDWM4S1JUsMY3pIk\nNYzhLUlSwxjekiQ1jOEtSVLDGN6SJDWM4S1JUsMY3pIkNYzhLUlSwxjekiQ1jOEtSVLDGN6SJDWM\n4S1JUsMY3pIkNYzhLUlSwxjekiQ1jOEtSVLDGN6SJDWM4S1JUsMY3pIkNYzhLUlSwxjekiQ1jOEt\nSVLDGN6SJDWM4S1JUsMY3pIkNYzhLUlSwxjekiQ1jOEtSVLDGN6SJDWM4S1JUsMY3pIkNYzhLUlS\nwxjekiQ1jOEtSVLDGN6SJDWM4S1JUsMY3pIkNYzhLUlSwxjekiQ1jOEtSVLDGN6SJDWM4S1JUsMY\n3pIkNYzhLUlSw/wv4iPAyKEsxDwAAAAASUVORK5CYII=\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(8, 8))\n",
"plt.axis('off')\n",
"nx.draw_networkx(graph, pos=nx.circular_layout(graph), node_size=400, node_color='steelblue', font_color='white')"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[0, 6, 4, 2], [7], [5], [3], [1]]"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"strongly_connected_components(graph)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 96 - floyd-steinberg.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"3bd33ccb-f982-481a-a275-ddb87e4df923\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"3bd33ccb-f982-481a-a275-ddb87e4df923\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid '3bd33ccb-f982-481a-a275-ddb87e4df923' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"3bd33ccb-f982-481a-a275-ddb87e4df923\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"3bd33ccb-f982-481a-a275-ddb87e4df923\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import numpy as np\n",
"from PIL import Image\n",
"from requests import get\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"from bokeh.layouts import layout\n",
"from bokeh.palettes import gray\n",
"\n",
"output_notebook()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def image_dither(path, black='#000000', white='#ffffff'):\n",
" image_rgb = read_image(path)\n",
" image_gray = grayscale(image_rgb)\n",
" image_bw = floyd_steinberg(image_gray)\n",
"\n",
" show(layout([[\n",
" plot(image_gray, palette=gray(256)),\n",
" plot(image_bw, palette=[black, white]) \n",
" ]]))"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def floyd_steinberg(image):\n",
" image = image.copy()\n",
" distribution = np.array([7, 3, 5, 1], dtype=float) / 16\n",
" u = np.array([0, 1, 1, 1])\n",
" v = np.array([1, -1, 0, 1])\n",
" \n",
" for y in range(image.shape[0] - 1):\n",
" for x in range(image.shape[1] - 1):\n",
" value = np.round(image[y, x])\n",
" error = image[y, x] - value\n",
" image[y, x] = value\n",
" image[y + u, x + v] += error * distribution\n",
" \n",
" image[:, -1] = 1\n",
" image[-1, :] = 1\n",
"\n",
" return image"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def grayscale(image):\n",
" height, width, _ = image.shape\n",
" \n",
" image = np.array(image, dtype=np.float32) / 255\n",
" image = image[:, :, 0] * .21 + image[:, :, 1] * .72 + image[:, :, 2] * .07\n",
" \n",
" return image.reshape(height, width)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def read_image(path, size=400):\n",
" if path.startswith('https://'):\n",
" image = Image.open(get(path, stream=True).raw)\n",
" else:\n",
" image = Image.open(path)\n",
" \n",
" width, height = image.size\n",
" width, height = size, int(size * height / width)\n",
" image = image.resize((width, height), Image.ANTIALIAS)\n",
" \n",
" data = image.getdata()\n",
" assert data.bands in [3, 4], 'RGB or RGBA image is required'\n",
" \n",
" raw = np.array(data, dtype=np.uint8)\n",
" return raw.reshape(height, width, data.bands)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def plot(image, palette):\n",
" y, x = image.shape\n",
"\n",
" plot = figure(x_range=(0, x), y_range=(0, y), plot_width=x, plot_height=y)\n",
" plot.axis.visible = False\n",
" plot.toolbar_location = None\n",
" plot.min_border = 0\n",
" plot.image([np.flipud(image)], x=0, y=0, dw=x, dh=y, palette=palette)\n",
" \n",
" return plot"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"URL = lambda name: 'https://raw.githubusercontent.com/coells/100days/master/resource/day 96 - %s.jpg' % (name,)\n",
"# URL = lambda name: './resource/day 96 - %s.jpg' % (name,)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"image_dither(URL('valinka'))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"image_dither(URL('eagle'), white='#ffebcd')"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"image_dither(URL('winter'), white='#f0f0ff')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 97 - locally weighted regression.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
"
\n",
"
Loading BokehJS ...\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function(global) {\n",
" function now() {\n",
" return new Date();\n",
" }\n",
"\n",
" var force = true;\n",
"\n",
" if (typeof (window._bokeh_onload_callbacks) === \"undefined\" || force === true) {\n",
" window._bokeh_onload_callbacks = [];\n",
" window._bokeh_is_loading = undefined;\n",
" }\n",
"\n",
"\n",
" \n",
" if (typeof (window._bokeh_timeout) === \"undefined\" || force === true) {\n",
" window._bokeh_timeout = Date.now() + 5000;\n",
" window._bokeh_failed_load = false;\n",
" }\n",
"\n",
" var NB_LOAD_WARNING = {'data': {'text/html':\n",
" \"\\n\"+\n",
" \"
\\n\"+\n",
" \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
" \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"- re-rerun `output_notebook()` to attempt to load from CDN again, or
\\n\"+\n",
" \"- use INLINE resources instead, as so:
\\n\"+\n",
" \"
\\n\"+\n",
" \"
\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"\\n\"+\n",
" \"
\"}};\n",
"\n",
" function display_loaded() {\n",
" if (window.Bokeh !== undefined) {\n",
" var el = document.getElementById(\"ee49779f-d563-4fcb-ae28-e997a06043fd\");\n",
" el.textContent = \"BokehJS \" + Bokeh.version + \" successfully loaded.\";\n",
" } else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(display_loaded, 100)\n",
" }\n",
" }\n",
"\n",
" function run_callbacks() {\n",
" window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n",
" delete window._bokeh_onload_callbacks\n",
" console.info(\"Bokeh: all callbacks have finished\");\n",
" }\n",
"\n",
" function load_libs(js_urls, callback) {\n",
" window._bokeh_onload_callbacks.push(callback);\n",
" if (window._bokeh_is_loading > 0) {\n",
" console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
" return null;\n",
" }\n",
" if (js_urls == null || js_urls.length === 0) {\n",
" run_callbacks();\n",
" return null;\n",
" }\n",
" console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
" window._bokeh_is_loading = js_urls.length;\n",
" for (var i = 0; i < js_urls.length; i++) {\n",
" var url = js_urls[i];\n",
" var s = document.createElement('script');\n",
" s.src = url;\n",
" s.async = false;\n",
" s.onreadystatechange = s.onload = function() {\n",
" window._bokeh_is_loading--;\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: all BokehJS libraries loaded\");\n",
" run_callbacks()\n",
" }\n",
" };\n",
" s.onerror = function() {\n",
" console.warn(\"failed to load library \" + url);\n",
" };\n",
" console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
" document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
" }\n",
" };var element = document.getElementById(\"ee49779f-d563-4fcb-ae28-e997a06043fd\");\n",
" if (element == null) {\n",
" console.log(\"Bokeh: ERROR: autoload.js configured with elementid 'ee49779f-d563-4fcb-ae28-e997a06043fd' but no matching script tag was found. \")\n",
" return false;\n",
" }\n",
"\n",
" var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.js\"];\n",
"\n",
" var inline_js = [\n",
" function(Bokeh) {\n",
" Bokeh.set_log_level(\"info\");\n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" },\n",
" \n",
" function(Bokeh) {\n",
" \n",
" document.getElementById(\"ee49779f-d563-4fcb-ae28-e997a06043fd\").textContent = \"BokehJS is loading...\";\n",
" },\n",
" function(Bokeh) {\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css\");\n",
" console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.5.min.css\");\n",
" }\n",
" ];\n",
"\n",
" function run_inline_js() {\n",
" \n",
" if ((window.Bokeh !== undefined) || (force === true)) {\n",
" for (var i = 0; i < inline_js.length; i++) {\n",
" inline_js[i](window.Bokeh);\n",
" }if (force === true) {\n",
" display_loaded();\n",
" }} else if (Date.now() < window._bokeh_timeout) {\n",
" setTimeout(run_inline_js, 100);\n",
" } else if (!window._bokeh_failed_load) {\n",
" console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
" window._bokeh_failed_load = true;\n",
" } else if (force !== true) {\n",
" var cell = $(document.getElementById(\"ee49779f-d563-4fcb-ae28-e997a06043fd\")).parents('.cell').data().cell;\n",
" cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
" }\n",
"\n",
" }\n",
"\n",
" if (window._bokeh_is_loading === 0) {\n",
" console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
" run_inline_js();\n",
" } else {\n",
" load_libs(js_urls, function() {\n",
" console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n",
" run_inline_js();\n",
" });\n",
" }\n",
"}(this));"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import numpy as np\n",
"from ipywidgets import interact\n",
"from bokeh.plotting import figure, show, output_notebook\n",
"from bokeh.layouts import gridplot\n",
"from bokeh.io import push_notebook\n",
"\n",
"output_notebook()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def local_regression(x0, X, Y, tau):\n",
" # add bias term\n",
" x0 = np.r_[1, x0]\n",
" X = np.c_[np.ones(len(X)), X]\n",
" \n",
" # fit model: normal equations with kernel\n",
" xw = X.T * radial_kernel(x0, X, tau)\n",
" beta = np.linalg.pinv(xw @ X) @ xw @ Y\n",
" \n",
" # predict value\n",
" return x0 @ beta"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def radial_kernel(x0, X, tau):\n",
" return np.exp(np.sum((X - x0) ** 2, axis=1) / (-2 * tau * tau))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## data"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"n = 1000\n",
"\n",
"# generate dataset\n",
"X = np.linspace(-3, 3, num=n)\n",
"Y = np.log(np.abs(X ** 2 - 1) + .5)\n",
"\n",
"# jitter X\n",
"X += np.random.normal(scale=.1, size=n)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## fit & plot models"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def plot_lwr(tau):\n",
" # prediction\n",
" domain = np.linspace(-3, 3, num=300)\n",
" prediction = [local_regression(x0, X, Y, tau) for x0 in domain]\n",
"\n",
" plot = figure(plot_width=400, plot_height=400)\n",
" plot.title.text = 'tau=%g' % tau\n",
" plot.scatter(X, Y, alpha=.3)\n",
" plot.line(domain, prediction, line_width=2, color='red')\n",
" \n",
" return plot"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"show(gridplot([\n",
" [plot_lwr(10.), plot_lwr(1.)],\n",
" [plot_lwr(0.1), plot_lwr(0.01)]\n",
"]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## interactive model"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
" \n",
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<Bokeh Notebook handle for In[7]>
"
],
"text/plain": [
""
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def interactive_update(tau):\n",
" model.data_source.data['y'] = [local_regression(x0, X, Y, tau) for x0 in domain]\n",
" push_notebook()\n",
"\n",
"domain = np.linspace(-3, 3, num=100)\n",
"prediction = [local_regression(x0, X, Y, 1.) for x0 in domain]\n",
"\n",
"plot = figure()\n",
"plot.scatter(X, Y, alpha=.3)\n",
"model = plot.line(domain, prediction, line_width=2, color='red')\n",
"show(plot, notebook_handle=True)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "e142749d1ac44704bcf81ddd6742be8c"
}
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
""
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"interact(interactive_update, tau=(0.01, 3., 0.01))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.1"
},
"widgets": {
"state": {
"a40d45b883bd45e6853fbf2102edc08a": {
"views": [
{
"cell_index": 12
}
]
}
},
"version": "1.2.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 98 - romberg integration.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"import sys\n",
"\n",
"np.set_printoptions(precision=14, linewidth=120)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def integrate(fn, a, b, steps=5, debug=False, exact=None):\n",
" table = np.zeros((steps, steps), dtype=np.float64)\n",
" pow_4 = 4 ** np.arange(steps, dtype=np.float64) - 1\n",
"\n",
" # trapezoidal rule\n",
" h = (b - a)\n",
" table[0, 0] = h * (fn(a) + fn(b)) / 2\n",
"\n",
" for j in range(1, steps):\n",
" h /= 2\n",
"\n",
" # extended trapezoidal rule\n",
" table[j, 0] = table[j - 1, 0] / 2\n",
" table[j, 0] += h * np.sum(fn(a + i * h) for i in range(1, 2 ** j + 1, 2))\n",
"\n",
" # richardson extrapolation\n",
" for k in range(1, j + 1):\n",
" table[j, k] = table[j, k - 1] + (table[j, k - 1] - table[j - 1, k - 1]) / pow_4[k]\n",
"\n",
" # debug\n",
" if debug:\n",
" print(table, file=sys.stderr)\n",
" if exact is not None:\n",
" errors = ['%.2e' % i for i in np.abs(table.diagonal() - exact)]\n",
" print('abs. error:', errors, file=sys.stderr)\n",
"\n",
" return table[-1, -1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## integration"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"[[ 0.68393972058572 0. 0. 0. 0. ]\n",
" [ 0.73137025182856 0.74718042890951 0. 0. 0. ]\n",
" [ 0.74298409780038 0.74685537979099 0.74683370984975 0. 0. ]\n",
" [ 0.7458656148457 0.74682612052747 0.7468241699099 0.74682401848228 0. ]\n",
" [ 0.74658459678822 0.74682425743573 0.74682413322961 0.74682413264739 0.74682413309509]]\n",
"abs. error: ['6.29e-02', '3.56e-04', '9.58e-06', '1.14e-07', '2.83e-10']\n"
]
},
{
"data": {
"text/plain": [
"0.74682413309509432"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# integral[0, 1] of e^(-x^2)\n",
"integrate(lambda x: np.exp(-x * x), 0, 1, debug=True, exact=0.746824132812427)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"[[ 0.75 0. 0. 0. 0. ]\n",
" [ 0.70833333333333 0.69444444444444 0. 0. 0. ]\n",
" [ 0.69702380952381 0.69325396825397 0.6931746031746 0. 0. ]\n",
" [ 0.69412185037185 0.69315453065453 0.69314790148123 0.69314747764483 0. ]\n",
" [ 0.69339120220753 0.69314765281942 0.69314719429708 0.69314718307193 0.69314718191674]]\n",
"abs. error: ['5.69e-02', '1.30e-03', '2.74e-05', '2.97e-07', '1.36e-09']\n"
]
},
{
"data": {
"text/plain": [
"0.69314718191674496"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# ln(2)\n",
"integrate(1..__truediv__, 1, 2, debug=True, exact=np.log(2))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"[[ 0.5 0. 0. 0. 0. ]\n",
" [ 0.3125 0.25 0. 0. 0. ]\n",
" [ 0.265625 0.25 0.25 0. 0. ]\n",
" [ 0.25390625 0.25 0.25 0.25 0. ]\n",
" [ 0.2509765625 0.25 0.25 0.25 0.25 ]]\n",
"abs. error: ['2.50e-01', '0.00e+00', '0.00e+00', '0.00e+00', '0.00e+00']\n"
]
},
{
"data": {
"text/plain": [
"0.25"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# integral[0, 1] of x^3\n",
"integrate(lambda x: x**3, 0, 1, debug=True, exact=.25)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## logarithmus naturalis"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def ln(x):\n",
" if x <= 0:\n",
" raise ValueError()\n",
" m, e = np.frexp(x)\n",
" return integrate(1..__truediv__, 1, m) + e * 0.6931471805599453"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.99999999999789502"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ln(np.e)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.69314717920314561"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ln(2)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1.1447298858493882"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ln(np.pi)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## normal distribution"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def norm_pdf(x, mean, sd):\n",
" x0 = (x - mean) ** 2\n",
" v2 = 2 * sd ** 2\n",
" return np.exp(-x0 / v2) / np.sqrt(np.pi * v2)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def norm_cdf(x, mean, sd):\n",
" return integrate(lambda x: norm_pdf(x, mean, sd), mean, x) + .5"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.97500211189427477"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"norm_cdf(1.96, mean=0, sd=1)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.68268949213754959"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"norm_cdf(1.2, mean=1, sd=.2) - norm_cdf(0.8, mean=1, sd=.2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day 99 - simplex.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def simplex(c, A, b):\n",
" table = initialize(c, A, b)\n",
" while not search_optimum(table):\n",
" pass\n",
" return solution(c, table)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def initialize(c, A, b):\n",
" (m, n), k = A.shape, len(c)\n",
"\n",
" # simplex table:\n",
" # |A|E|b|\n",
" # |c|0|0|\n",
" table = np.zeros((m + 1, m + n + 1))\n",
" table[:m, :n] = A\n",
" table[range(m), range(n, n + m)] = 1\n",
" table[:-1, -1] = b\n",
" table[-1, :k] = c\n",
"\n",
" return table"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def search_optimum(table):\n",
" index = np.argwhere(table[-1, :-1] > 0).ravel()\n",
" \n",
" # optimum found\n",
" if not len(index):\n",
" return True\n",
" \n",
" # pivotal column\n",
" j = index[0]\n",
" column = table[:-1, j].copy()\n",
" column[column <= 0] = -1\n",
" \n",
" if np.all(column <= 0):\n",
" raise ArithmeticError('the system is unbounded')\n",
"\n",
" # pivotal row\n",
" pivots = table[:-1, -1] / column\n",
" pivots[column <= 0] = np.inf\n",
" i = np.argmin(pivots).ravel()[0]\n",
"\n",
" # eliminate by pivot at (i, j)\n",
" row = table[i] / table[i][j]\n",
" table[:] -= np.outer(table[:, j], row)\n",
" table[i, :] = row\n",
" table[:, j] = table[:, j].round()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def solution(c, table):\n",
" (m, n), k = table.shape, len(c)\n",
"\n",
" # pivotal columns\n",
" s = np.sum(table == 0, axis=0) == m - 1\n",
" t = np.sum(table == 1, axis=0) == 1\n",
"\n",
" # solution\n",
" x = np.zeros(n - 1)\n",
"\n",
" for j in range(n - 1):\n",
" if s[j] and t[j]:\n",
" x[j] = table[:, j] @ table[:, -1]\n",
"\n",
" return dict(\n",
" x=x[:k],\n",
" slack=x[k:],\n",
" max=-table[-1, -1],\n",
" table=table,\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## linear program #1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```\n",
"maximize: -x + 3y + 2z\n",
"\n",
"subject to:\n",
"x + y + z <= 6\n",
"x + z <= 4\n",
" y + z <= 3\n",
"x + y <= 2\n",
"\n",
"x, y, z >= 0\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"c = np.array([-1, 3, 2])\n",
"A = np.array([\n",
" [1, 1, 1],\n",
" [1, 0, 1],\n",
" [0, 1, 1],\n",
" [1, 1, 0],\n",
"])\n",
"b = np.array([6, 4, 3, 2])"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x \n",
" [ 0. 2. 1.] \n",
"\n",
"slack \n",
" [ 3. 3. 0. 0.] \n",
"\n",
"table \n",
" [[ 1. 0. 0. 1. 0. -1. 0. 3.]\n",
" [ 2. 0. 0. 0. 1. -1. 1. 3.]\n",
" [-1. 0. 1. 0. 0. 1. -1. 1.]\n",
" [ 1. 1. 0. 0. 0. 0. 1. 2.]\n",
" [-2. 0. 0. 0. 0. -2. -1. -8.]] \n",
"\n",
"max \n",
" 8.0 \n",
"\n"
]
}
],
"source": [
"lp = simplex(c, A, b)\n",
"\n",
"for k in ['x', 'slack', 'table', 'max']:\n",
" print(k, '\\n', lp[k], '\\n')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## linear program #2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```\n",
"maximize: 2r + 4s + 3t + u\n",
"\n",
"subject to:\n",
"3r + s + t + 4u <= 12\n",
" r - 3s + 2t + 3u <= 7\n",
"2r + s + 3t - u <= 10\n",
"\n",
"r, s, t, u >= 0\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"c = np.array([2, 4, 3, 1])\n",
"A = np.array([\n",
" [3, 1, 1, 4],\n",
" [1, -3, 2, 3],\n",
" [2, 1, 3, -1]\n",
"])\n",
"b = np.array([12, 7, 10])"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x \n",
" [ 0. 10.4 0. 0.4] \n",
"\n",
"slack \n",
" [ 0. 37. 0.] \n",
"\n",
"table \n",
" [[ 0.2 0. -0.4 1. 0.2 0. -0.2 0.4]\n",
" [ 7. 0. 11. 0. 0. 1. 3. 37. ]\n",
" [ 2.2 1. 2.6 0. 0.2 0. 0.8 10.4]\n",
" [ -7. 0. -7. 0. -1. 0. -3. -42. ]] \n",
"\n",
"max \n",
" 42.0 \n",
"\n"
]
}
],
"source": [
"lp = simplex(c, A, b)\n",
"\n",
"for k in ['x', 'slack', 'table', 'max']:\n",
" print(k, '\\n', lp[k], '\\n')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
================================================
FILE: day I00 - segmented eratosthenes sieve.ipynb
================================================
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%load_ext Cython"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## algorithm"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%%cython\n",
"\n",
"from libc.stdlib cimport malloc, free\n",
"\n",
"DEF LIMIT = 1024 * 31\n",
"DEF PRIME = 1024 * 4\n",
"DEF SIEVE = 1024 * 32\n",
"\n",
"cdef inline int imin(int a, int b) nogil:\n",
" return a if a < b else b\n",
"\n",
"cdef inline int memset(char *p, int n) nogil:\n",
" cdef:\n",
" short *q = p\n",
" int i, j = 0\n",
"\n",
" for i in range((n + 1) >> 1):\n",
" j += q[i]\n",
" q[i] = 0x0100\n",
"\n",
" return j >> 8\n",
"\n",
"cdef int naive_sieve(char *sieve, int *primes, int *offsets, int n) nogil:\n",
" cdef int i, j\n",
"\n",
" memset(sieve, n)\n",
"\n",
" for i in range(3, n, 2):\n",
" if sieve[i]:\n",
" j = i * i\n",
" while j < n:\n",
" sieve[j] = 0\n",
" j += i << 1\n",
"\n",
" primes[0] = i\n",
" offsets[0] = j\n",
" primes += 1\n",
" offsets += 1\n",
"\n",
" primes[0] = 0\n",
" offsets[0] = 0\n",
"\n",
" return memset(sieve, n)\n",
"\n",
"cdef int segmented_sieve(char *sieve, int *primes, int *offsets, int k, int n) nogil:\n",
" cdef int i\n",
"\n",
" while primes[0]:\n",
" i = offsets[0] - k\n",
" while i < n:\n",
" sieve[i] = 0\n",
" i += primes[0] << 1\n",
" offsets[0] = i + k\n",
"\n",
" primes += 1\n",
" offsets += 1\n",
"\n",
" return memset(sieve, n)\n",
"\n",
"cpdef int eratosthenes(int n) nogil:\n",
" cdef:\n",
" char *sieve\n",
" int *primes\n",
" int *offsets\n",
" int k, total\n",
"\n",
" if n > LIMIT * LIMIT:\n",
" return -1\n",
"\n",
" sieve = malloc(SIEVE)\n",
" primes = malloc(PRIME * sizeof(int))\n",
" offsets = malloc(PRIME * sizeof(int))\n",
"\n",
" total = naive_sieve(sieve, primes, offsets, imin(n, LIMIT))\n",
"\n",
" memset(sieve, SIEVE)\n",
" k = LIMIT\n",
" n -= LIMIT\n",
"\n",
" while n > 0:\n",
" total += segmented_sieve(sieve, primes, offsets, k, imin(n, SIEVE))\n",
" k += SIEVE\n",
" n -= SIEVE\n",
"\n",
" free(sieve)\n",
" free(primes)\n",
" free(offsets)\n",
"\n",
" return total"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## run"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"primes below 10**1: 4\n",
"primes below 10**2: 25\n",
"primes below 10**3: 168\n",
"primes below 10**4: 1229\n",
"primes below 10**5: 9592\n",
"primes below 10**6: 78498\n",
"primes below 10**7: 664579\n",
"primes below 10**8: 5761455\n",
"primes below 10**9: 50847534\n"
]
}
],
"source": [
"for i in range(1, 10):\n",
" print('primes below 10**%d: %d' % (i, eratosthenes(10**i)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## timeit"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"10000 loops, best of 3: 56.6 µs per loop\n",
"1000 loops, best of 3: 574 µs per loop\n",
"100 loops, best of 3: 6.07 ms per loop\n",
"10 loops, best of 3: 68.4 ms per loop\n",
"1 loop, best of 3: 863 ms per loop\n"
]
}
],
"source": [
"%timeit eratosthenes(1024 * 31)\n",
"%timeit eratosthenes(10**6)\n",
"%timeit eratosthenes(10**7)\n",
"%timeit eratosthenes(10**8)\n",
"%timeit eratosthenes(10**9)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"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.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}