Skip to content

Commit 3d1be45

Browse files
committed
gcd
1 parent 9e46063 commit 3d1be45

File tree

2 files changed

+294
-1
lines changed

2 files changed

+294
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ If you have any suggestions or want to make additions, I would be very happy if
4949
### Efficiency
5050

5151
- Finding the Maximum Pairwise Product [[ IPython nbviewer ](http://nbviewer.ipython.org/github/rasbt/algorithms_in_ipython_notebooks/blob/master/ipython_nbs/efficiency/maximum-pairwise-product.ipynb)]
52-
- Fibonacci Numbers [[ IPython nbviewer ](http://nbviewer.ipython.org/github/rasbt/algorithms_in_ipython_notebooks/blob/master/ipython_nbs/efficiency/fibonacci-tree.ipynb)]
52+
- Fibonacci Numbers [[ IPython nbviewer ](http://nbviewer.ipython.org/github/rasbt/algorithms_in_ipython_notebooks/blob/master/ipython_nbs/efficiency/fibonacci-tree.ipynb)]
53+
- Greatest Common Divisor [[ IPython nbviewer ](http://nbviewer.ipython.org/github/rasbt/algorithms_in_ipython_notebooks/blob/master/ipython_nbs/efficiency/greatest-common-divisor.ipynb)]
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"metadata": {
7+
"collapsed": false
8+
},
9+
"outputs": [
10+
{
11+
"name": "stdout",
12+
"output_type": "stream",
13+
"text": [
14+
"Sebatian Raschka \n",
15+
"last updated: 2016-06-02 \n",
16+
"\n",
17+
"CPython 3.5.1\n",
18+
"IPython 4.2.0\n"
19+
]
20+
}
21+
],
22+
"source": [
23+
"%load_ext watermark\n",
24+
"%watermark -a 'Sebatian Raschka' -u -d -v"
25+
]
26+
},
27+
{
28+
"cell_type": "markdown",
29+
"metadata": {},
30+
"source": [
31+
"# Greatest Common Divisor"
32+
]
33+
},
34+
{
35+
"cell_type": "markdown",
36+
"metadata": {},
37+
"source": [
38+
"Now, the *greatest common divisor* (GCD) is the largest natural number $d$ that divides $a$ and $b$ in a fraction $\\frac{a}{b}$ without a remainder. \n",
39+
"\n",
40+
"For example, the GCD of the fraction $\\frac{6}{9}$ is 3: $$\\frac{6/3}{9/3} = \\frac{2}{3}$$"
41+
]
42+
},
43+
{
44+
"cell_type": "markdown",
45+
"metadata": {},
46+
"source": [
47+
"First, let us start with the \"intuitive,\" yet naive, brute-force implementation. Here, we simply iterate through all integers from 1 to max(a, b) to find the largest common divisor of the fraction $\\frac{a}{b}$ or equivalently $\\frac{a}{b}$. "
48+
]
49+
},
50+
{
51+
"cell_type": "code",
52+
"execution_count": 17,
53+
"metadata": {
54+
"collapsed": false
55+
},
56+
"outputs": [
57+
{
58+
"name": "stdout",
59+
"output_type": "stream",
60+
"text": [
61+
"In: 1/1, Out: 1\n",
62+
"In: 1/2, Out: 1\n",
63+
"In: 3/9, Out: 3\n",
64+
"In: 12/24, Out: 12\n",
65+
"In: 12/26, Out: 2\n",
66+
"In: 26/12, Out: 2\n",
67+
"In: 13/17, Out: 1\n"
68+
]
69+
}
70+
],
71+
"source": [
72+
"def naive_gcd(a, b):\n",
73+
" gcd = 0\n",
74+
" if a < b:\n",
75+
" n = a\n",
76+
" else:\n",
77+
" n = a\n",
78+
" for d in range(1, n + 1):\n",
79+
" if not a % d and not b % d:\n",
80+
" gcd = d\n",
81+
" return gcd\n",
82+
"\n",
83+
"print('In: 1/1,', 'Out:', naive_gcd(1, 1))\n",
84+
"print('In: 1/2,', 'Out:', naive_gcd(1, 2))\n",
85+
"print('In: 3/9,', 'Out:', naive_gcd(3, 9))\n",
86+
"print('In: 12/24,', 'Out:', naive_gcd(12, 24))\n",
87+
"print('In: 12/26,', 'Out:', naive_gcd(12, 26))\n",
88+
"print('In: 26/12,', 'Out:', naive_gcd(26, 12))\n",
89+
"print('In: 13/17,', 'Out:', naive_gcd(13, 17))"
90+
]
91+
},
92+
{
93+
"cell_type": "markdown",
94+
"metadata": {},
95+
"source": [
96+
"## Euclidean Algorithm\n",
97+
"\n",
98+
"The Greek mathematician Euclid described this algorithm approx. 300 BC in \n",
99+
"It is still being used in the field of number theory and conseqently cryptography to reduce fractions to their simplest form. For additional, interesting details and the proof by Gabriel Lamé in 1844, please take a look at the excellent [Wikipedia](https://en.wikipedia.org/wiki/Euclidean_algorithm) page.\n",
100+
"\n",
101+
"The idea behind the more efficient version, the Euclidean division (in contrast to the substraction-based) approach is the following:\n",
102+
"\n",
103+
"Given that we want to compute `gcd(a, b)`, the greatest common divisor of the fraction{a}{b}, we first compute the remainder of the division $\\frac{a}{b}$; we call this remainder a'. Then, we compute `gcd(a', b)`. We repeat this procedure in recursive manner until b=0."
104+
]
105+
},
106+
{
107+
"cell_type": "code",
108+
"execution_count": 18,
109+
"metadata": {
110+
"collapsed": false
111+
},
112+
"outputs": [
113+
{
114+
"name": "stdout",
115+
"output_type": "stream",
116+
"text": [
117+
"In: 1/1, Out: 1\n",
118+
"In: 1/2, Out: 1\n",
119+
"In: 3/9, Out: 3\n",
120+
"In: 12/24, Out: 12\n",
121+
"In: 12/26, Out: 2\n",
122+
"In: 26/12, Out: 2\n",
123+
"In: 13/17, Out: 1\n"
124+
]
125+
}
126+
],
127+
"source": [
128+
"def eucl_gcd_recurse(a, b):\n",
129+
" if not b:\n",
130+
" return a\n",
131+
" else:\n",
132+
" return eucl_gcd_recurse(b, a % b)\n",
133+
" \n",
134+
"print('In: 1/1,', 'Out:', naive_gcd(1, 1))\n",
135+
"print('In: 1/2,', 'Out:', naive_gcd(1, 2))\n",
136+
"print('In: 3/9,', 'Out:', naive_gcd(3, 9))\n",
137+
"print('In: 12/24,', 'Out:', naive_gcd(12, 24))\n",
138+
"print('In: 12/26,', 'Out:', naive_gcd(12, 26))\n",
139+
"print('In: 26/12,', 'Out:', naive_gcd(26, 12))\n",
140+
"print('In: 13/17,', 'Out:', naive_gcd(13, 17))"
141+
]
142+
},
143+
{
144+
"cell_type": "markdown",
145+
"metadata": {},
146+
"source": [
147+
"The Euclidean GCD algorithm will reduce either of the number at least by half at each step (see the [Wikipedia](https://en.wikipedia.org/wiki/Euclidean_algorithm) page for details). Thus, the time complexity of this algorithm is \n",
148+
"\n",
149+
"$O(log_2 b) + O(log_2 a) = O(log_2 n),$\n",
150+
"\n",
151+
"where $n = max(a, b)$. In contrast, our previous, naive implementation has an upper bound of $O(n)$.\n",
152+
"\n",
153+
"Since Python is \"notoriously bad\" at recursion, let us implement a dynamic version of this algorithm. (One problem of recursion via Python is the limited stack size, and the other one is that tail recursion optimization not implemented.)"
154+
]
155+
},
156+
{
157+
"cell_type": "code",
158+
"execution_count": 19,
159+
"metadata": {
160+
"collapsed": false
161+
},
162+
"outputs": [
163+
{
164+
"name": "stdout",
165+
"output_type": "stream",
166+
"text": [
167+
"In: 1/1, Out: 1\n",
168+
"In: 1/2, Out: 1\n",
169+
"In: 3/9, Out: 3\n",
170+
"In: 12/24, Out: 12\n",
171+
"In: 12/26, Out: 2\n",
172+
"In: 26/12, Out: 2\n",
173+
"In: 13/17, Out: 1\n"
174+
]
175+
}
176+
],
177+
"source": [
178+
"def eucl_gcd_dynamic(a, b):\n",
179+
" while b:\n",
180+
" tmp = b \n",
181+
" b = a % b \n",
182+
" a = tmp \n",
183+
" return a\n",
184+
"\n",
185+
"print('In: 1/1,', 'Out:', naive_gcd(1, 1))\n",
186+
"print('In: 1/2,', 'Out:', naive_gcd(1, 2))\n",
187+
"print('In: 3/9,', 'Out:', naive_gcd(3, 9))\n",
188+
"print('In: 12/24,', 'Out:', naive_gcd(12, 24))\n",
189+
"print('In: 12/26,', 'Out:', naive_gcd(12, 26))\n",
190+
"print('In: 26/12,', 'Out:', naive_gcd(26, 12))\n",
191+
"print('In: 13/17,', 'Out:', naive_gcd(13, 17))"
192+
]
193+
},
194+
{
195+
"cell_type": "markdown",
196+
"metadata": {},
197+
"source": [
198+
"Given an arbitrary fraction $\\frac{a}{b}$, let us use the `%timeit` module for a quick comparison:"
199+
]
200+
},
201+
{
202+
"cell_type": "code",
203+
"execution_count": 27,
204+
"metadata": {
205+
"collapsed": true
206+
},
207+
"outputs": [],
208+
"source": [
209+
"a = 12313432\n",
210+
"b = 34234232342"
211+
]
212+
},
213+
{
214+
"cell_type": "code",
215+
"execution_count": 28,
216+
"metadata": {
217+
"collapsed": false
218+
},
219+
"outputs": [
220+
{
221+
"name": "stdout",
222+
"output_type": "stream",
223+
"text": [
224+
"5 loops, best of 3: 1.78 s per loop\n"
225+
]
226+
}
227+
],
228+
"source": [
229+
"%timeit -r 3 -n 5 naive_gcd(a, b)"
230+
]
231+
},
232+
{
233+
"cell_type": "code",
234+
"execution_count": 29,
235+
"metadata": {
236+
"collapsed": false
237+
},
238+
"outputs": [
239+
{
240+
"name": "stdout",
241+
"output_type": "stream",
242+
"text": [
243+
"5 loops, best of 3: 3.23 µs per loop\n"
244+
]
245+
}
246+
],
247+
"source": [
248+
"%timeit -r 3 -n 5 eucl_gcd_recurse(a, b)"
249+
]
250+
},
251+
{
252+
"cell_type": "code",
253+
"execution_count": 30,
254+
"metadata": {
255+
"collapsed": false
256+
},
257+
"outputs": [
258+
{
259+
"name": "stdout",
260+
"output_type": "stream",
261+
"text": [
262+
"5 loops, best of 3: 2.21 µs per loop\n"
263+
]
264+
}
265+
],
266+
"source": [
267+
"%timeit -r 3 -n 5 eucl_gcd_dynamic(a, b)"
268+
]
269+
}
270+
],
271+
"metadata": {
272+
"kernelspec": {
273+
"display_name": "Python 3",
274+
"language": "python",
275+
"name": "python3"
276+
},
277+
"language_info": {
278+
"codemirror_mode": {
279+
"name": "ipython",
280+
"version": 3
281+
},
282+
"file_extension": ".py",
283+
"mimetype": "text/x-python",
284+
"name": "python",
285+
"nbconvert_exporter": "python",
286+
"pygments_lexer": "ipython3",
287+
"version": "3.5.1"
288+
}
289+
},
290+
"nbformat": 4,
291+
"nbformat_minor": 0
292+
}

0 commit comments

Comments
 (0)