From ed1b2ddc061063fa95c095ca473e530eb53b252a Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Fri, 15 Mar 2013 00:19:38 +0400 Subject: [PATCH 01/89] Initial LCM support: --- algorithms/math/lcm.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 algorithms/math/lcm.py diff --git a/algorithms/math/lcm.py b/algorithms/math/lcm.py new file mode 100644 index 0000000..c99e566 --- /dev/null +++ b/algorithms/math/lcm.py @@ -0,0 +1,9 @@ +from extended_gcd import extended_gcd + + +def lcm(a, b): + """ + Returns LCM based on GCD of digit. + """ + x, y = extended_gcd(a, b) + return a * b / (a * y + b * x) From 9a1c1055ecaa166c4c2e965dcce76d27b64d1a49 Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Fri, 15 Mar 2013 00:19:56 +0400 Subject: [PATCH 02/89] Initial LCM tests --- algorithms/tests/test_math.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index c382f77..465ee1a 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -1,5 +1,6 @@ import unittest from ..math.extended_gcd import extended_gcd +from ..math.lcm import lcm class TestExtendedGCD(unittest.TestCase): @@ -24,3 +25,16 @@ def test_extended_gcd(self): # Find extended_gcd of 50 and 15 (a, b) = extended_gcd(50, 15) self.assertIs(50 * a + 15 * b, 5) + + +class TestLCM(unittest.TestCase): + def test_lcm(self): + # Find lcm of 16 and 20 + r = lcm(16, 20) + self.assertEqual(80, abs(r)) + + # Find lcm for 20 and 16 + r2 = lcm(20, 16) + + # Checks that lcm function is commutative + self.assertEqual(r, r2) From d63d5fc8f5f0e8041f7fab96de9ef16f6b5fc57f Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Fri, 15 Mar 2013 00:24:50 +0400 Subject: [PATCH 03/89] Okay, it's fast fix. --- algorithms/math/lcm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/math/lcm.py b/algorithms/math/lcm.py index c99e566..36c373f 100644 --- a/algorithms/math/lcm.py +++ b/algorithms/math/lcm.py @@ -6,4 +6,4 @@ def lcm(a, b): Returns LCM based on GCD of digit. """ x, y = extended_gcd(a, b) - return a * b / (a * y + b * x) + return a * b / (a * x + b * y) From efb42283eda9345c0e62436b1a62100d9000b2b7 Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Fri, 15 Mar 2013 00:28:18 +0400 Subject: [PATCH 04/89] Pythonic fastfix --- algorithms/tests/test_math.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index 465ee1a..d3eb206 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -29,12 +29,9 @@ def test_extended_gcd(self): class TestLCM(unittest.TestCase): def test_lcm(self): - # Find lcm of 16 and 20 - r = lcm(16, 20) - self.assertEqual(80, abs(r)) - - # Find lcm for 20 and 16 - r2 = lcm(20, 16) + # Find lcm of (16, 20) and (20, 16) + r, r2 = lcm(16, 20), lcm(20, 16) + self.assertEqual(r, 80) # Checks that lcm function is commutative self.assertEqual(r, r2) From 9c69c593bc47daa239549c6c6b0b535df90cfd8a Mon Sep 17 00:00:00 2001 From: liam-m Date: Sat, 13 Apr 2013 23:08:57 +0200 Subject: [PATCH 05/89] Changed base case; single scan through elements Fewer recursive calls as base case is now len(seq) <= 1 rather than len(seq) < 1. Only scans through seq once to increase efficiency. --- algorithms/sorting/quick_sort.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/algorithms/sorting/quick_sort.py b/algorithms/sorting/quick_sort.py index 0fa0b64..5f74794 100644 --- a/algorithms/sorting/quick_sort.py +++ b/algorithms/sorting/quick_sort.py @@ -20,10 +20,14 @@ def sort(seq): - if len(seq) < 1: + if len(seq) <= 1: return seq else: pivot = seq[0] - left = sort([x for x in seq[1:] if x < pivot]) - right = sort([x for x in seq[1:] if x >= pivot]) - return left + [pivot] + right + left, right = [], [] + for x in seq[1:]: + if x < pivot: + left.append(x) + else: + right.append(x) + return sort(left) + [pivot] + sort(right) From 739f7e8c92cbe2f241ffe3965ee9c5dcea23dae8 Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Tue, 16 Apr 2013 03:58:24 +0400 Subject: [PATCH 06/89] Simple version of lcm, that does not have any dependencies --- algorithms/math/lcm.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/algorithms/math/lcm.py b/algorithms/math/lcm.py index 36c373f..35f1c77 100644 --- a/algorithms/math/lcm.py +++ b/algorithms/math/lcm.py @@ -1,9 +1,8 @@ -from extended_gcd import extended_gcd - - def lcm(a, b): """ - Returns LCM based on GCD of digit. + Simple version of lcm, that does not have any dependencies """ - x, y = extended_gcd(a, b) - return a * b / (a * x + b * y) + tmp_a = a + while (tmp_a % b) != 0: + tmp_a += a + return tmp_a From 3753953f0d9fe49301db0a00bc294d803ce988b5 Mon Sep 17 00:00:00 2001 From: lcheung Date: Thu, 18 Apr 2013 13:39:34 -0700 Subject: [PATCH 07/89] Implement Sieve of Eratosthenes with unit tests --- algorithms/math/sieve_eratosthenes.py | 32 +++++++++++++++++++++++++++ algorithms/tests/test_math.py | 13 +++++++++++ 2 files changed, 45 insertions(+) create mode 100644 algorithms/math/sieve_eratosthenes.py diff --git a/algorithms/math/sieve_eratosthenes.py b/algorithms/math/sieve_eratosthenes.py new file mode 100644 index 0000000..0f15e28 --- /dev/null +++ b/algorithms/math/sieve_eratosthenes.py @@ -0,0 +1,32 @@ +""" + sieve_eratosthenes.py + + Implementation of the Sieve of Eratosthenes algorithm. + + Depth First Search Overview: + ------------------------ + Is a simple, ancient algorithm for finding all prime numbers + up to any given limit. It does so by iteratively marking as composite (i.e. not prime) + the multiples of each prime, starting with the multiples of 2. + + The sieve of Eratosthenes is one of the most efficient ways + to find all of the smaller primes (below 10 million or so). + + Time Complexity: O(n log log n) + + Pseudocode: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes +""" + +def eratosthenes(end,start=2): + if start < 2: + start = 2 + primes = range(start,end) + marker = 2 + while marker < end: + for i in xrange(marker, end+1): + if marker*i in primes: + primes.remove(marker*i) + marker += 1 + return primes + + diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index c382f77..582e0de 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -1,5 +1,6 @@ import unittest from ..math.extended_gcd import extended_gcd +from ..math.sieve_eratosthenes import eratosthenes class TestExtendedGCD(unittest.TestCase): @@ -24,3 +25,15 @@ def test_extended_gcd(self): # Find extended_gcd of 50 and 15 (a, b) = extended_gcd(50, 15) self.assertIs(50 * a + 15 * b, 5) + +class TestSieveOfEratosthenes(unittest.TestCase): + + def test_eratosthenes(self): + rv1 = eratosthenes(-10) + rv2 = eratosthenes(10) + rv3 = eratosthenes(100,5) + rv4 = eratosthenes(100,-10) + self.assertEqual(rv1,[]) + self.assertEqual(rv2,[2, 3, 5, 7]) + self.assertEqual(rv3,[5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]) + self.assertEqual(rv4,[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]) From 10009f8a19b417359d41a5e83ff5083e6862b891 Mon Sep 17 00:00:00 2001 From: lcheung Date: Mon, 29 Apr 2013 14:29:53 -0700 Subject: [PATCH 08/89] Fix Sieve of Eratosthenes Overview header --- algorithms/math/sieve_eratosthenes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/math/sieve_eratosthenes.py b/algorithms/math/sieve_eratosthenes.py index 0f15e28..ae2d23e 100644 --- a/algorithms/math/sieve_eratosthenes.py +++ b/algorithms/math/sieve_eratosthenes.py @@ -3,7 +3,7 @@ Implementation of the Sieve of Eratosthenes algorithm. - Depth First Search Overview: + Sieve of Eratosthenes Overview: ------------------------ Is a simple, ancient algorithm for finding all prime numbers up to any given limit. It does so by iteratively marking as composite (i.e. not prime) From 1d465dd8f38fef81fd233ce5c9dc97ab1e541107 Mon Sep 17 00:00:00 2001 From: lcheung Date: Mon, 29 Apr 2013 17:11:45 -0700 Subject: [PATCH 09/89] Implement Sieve of Atkin with unit tests --- algorithms/math/sieve_atkin.py | 51 ++++++++++++++++++++++++++++++++++ algorithms/tests/test_math.py | 20 +++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 algorithms/math/sieve_atkin.py diff --git a/algorithms/math/sieve_atkin.py b/algorithms/math/sieve_atkin.py new file mode 100644 index 0000000..bab4cf6 --- /dev/null +++ b/algorithms/math/sieve_atkin.py @@ -0,0 +1,51 @@ +""" + sieve_atkin.py + + Implementation of the Sieve of Eratosthenes algorithm. + + Sieve of Atkin Overview: + ------------------------ + It is an optimized version of the ancient sieve of Eratosthenes + which does some preliminary work and then marks off + multiples of the square of each prime, rather than multiples of the prime itself. + It was created in 2004 by A. O. L. Atkin and Daniel J. Bernstein. + + Time Complexity: O(n/log log n) + + Pseudocode: https://en.wikipedia.org/wiki/Sieve_of_Atkin +""" +from math import sqrt + +def atkin(limit): + if limit == 2: + return [2] + if limit == 3: + return [2, 3] + if limit == 5: + return [2, 3, 5] + if limit < 2: + return [] + primes = [2, 3, 5] + is_prime = [False] * (limit + 1) + sqrt_limit = int(sqrt(limit)) + 1 + + for x in xrange(1,sqrt_limit): + for y in xrange(1,sqrt_limit): + n = 4 * x ** 2 + y ** 2 + if n <= limit and (n % 12 == 1 or n % 12 == 5): + is_prime[n] = not is_prime[n] + n = 3 * x ** 2 + y ** 2 + if n <= limit and (n % 12 == 7): + is_prime[n] = not is_prime[n] + n = 3 * x ** 2 - y ** 2 + if x > y and (n <= limit) and (n % 12 == 11): + is_prime[n] = not is_prime[n] + + for index in xrange(5,sqrt_limit): + if is_prime[index]: + for composite in xrange(index ** 2, limit, index ** 2): + is_prime[composite] = False + for index in xrange(7, limit): + if is_prime[index]: + primes.append(index) + return primes diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index c382f77..ab3fbe1 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -1,5 +1,6 @@ import unittest from ..math.extended_gcd import extended_gcd +from ..math.sieve_atkin import atkin class TestExtendedGCD(unittest.TestCase): @@ -24,3 +25,22 @@ def test_extended_gcd(self): # Find extended_gcd of 50 and 15 (a, b) = extended_gcd(50, 15) self.assertIs(50 * a + 15 * b, 5) + +class TestSieveOfAtkin(unittest.TestCase): + + def test_atkin(self): + rv1 = atkin(10) + rv2 = atkin(100) + rv3 = atkin(1000) + rv4 = atkin(-10) + self.assertEqual(rv1,[2, 3, 5, 7]) + self.assertEqual(rv2,[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]) + self.assertEqual(rv3,[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, + 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, + 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, + 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, + 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, + 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, + 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]) + self.assertEqual(rv4,[]) From 6b680c6119d82f5dd5c2a9febc4bc3c5a834f57a Mon Sep 17 00:00:00 2001 From: Kartik Nagpal Date: Sat, 13 Jul 2013 03:16:16 +0530 Subject: [PATCH 10/89] resolving bug for odd length list --- algorithms/sorting/merge_sort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/sorting/merge_sort.py b/algorithms/sorting/merge_sort.py index 05ca530..6ec22a5 100644 --- a/algorithms/sorting/merge_sort.py +++ b/algorithms/sorting/merge_sort.py @@ -38,7 +38,7 @@ def sort(seq): if len(seq) <= 1: return seq - middle = len(seq) / 2 + middle = int(len(seq) / 2) left = sort(seq[:middle]) right = sort(seq[middle:]) return merge(left, right) From 80eaaddea2105c43ef07b4ba2d49d4d216c16e00 Mon Sep 17 00:00:00 2001 From: Maksim Sokolski Date: Fri, 15 Nov 2013 15:46:12 +0200 Subject: [PATCH 11/89] Change selection_sort implementation: reduce by half items swapping - change implementation according to wiki pseudocode. Change swap method. --- algorithms/sorting/selection_sort.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/algorithms/sorting/selection_sort.py b/algorithms/sorting/selection_sort.py index 94df4f1..5e4abdc 100644 --- a/algorithms/sorting/selection_sort.py +++ b/algorithms/sorting/selection_sort.py @@ -21,14 +21,11 @@ def sort(seq): for i in range(0, len(seq)): - minat = i - minum = seq[i] - for j in range(i + 1, len(seq)): - if minum > seq[j]: - minat = j - minum = seq[j] - temp = seq[i] - seq[i] = seq[minat] - seq[minat] = temp + iMin = i + for j in range(i+1, len(seq)): + if seq[iMin] > seq[j]: + iMin = j + if i != iMin: + seq[i], seq[iMin] = seq[iMin], seq[i] return seq From cdf897b5faafe61fb1b76f920c17935fb06d88a5 Mon Sep 17 00:00:00 2001 From: rasbt Date: Thu, 27 Mar 2014 00:27:17 -0400 Subject: [PATCH 12/89] math functions cdf + pdf, algorithm todo upd. --- .AUTHORS.rst.swp | Bin 0 -> 12288 bytes .README.rst.swp | Bin 0 -> 16384 bytes .gitignore | 1 + .setup.py.swp | Bin 0 -> 12288 bytes AUTHORS.rst | 2 ++ README.rst | 2 ++ TODO.rst | 7 +++++ algorithms/math/.approx_cdf.py.swp | Bin 0 -> 12288 bytes algorithms/math/.extended_gcd.py.swp | Bin 0 -> 12288 bytes algorithms/math/.std_normal_pdf.py.swp | Bin 0 -> 12288 bytes algorithms/math/approx_cdf.py | 24 ++++++++++++++++ algorithms/math/std_normal_pdf.py | 19 +++++++++++++ algorithms/tests/.test_math.py.swp | Bin 0 -> 12288 bytes algorithms/tests/test_math.py | 38 ++++++++++++++++++++++++- setup.py | 2 ++ 15 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 .AUTHORS.rst.swp create mode 100644 .README.rst.swp create mode 100644 .setup.py.swp create mode 100644 algorithms/math/.approx_cdf.py.swp create mode 100644 algorithms/math/.extended_gcd.py.swp create mode 100644 algorithms/math/.std_normal_pdf.py.swp create mode 100644 algorithms/math/approx_cdf.py create mode 100644 algorithms/math/std_normal_pdf.py create mode 100644 algorithms/tests/.test_math.py.swp diff --git a/.AUTHORS.rst.swp b/.AUTHORS.rst.swp new file mode 100644 index 0000000000000000000000000000000000000000..8f294ae363d7c52ad68a2c13556ed3a669444e60 GIT binary patch literal 12288 zcmeI2&uSDw5XLLw(P%KB*HOXS?rajlxCB8Ws3D19691gsp5D$(XJ)#`?w*Yaq7V;x z1)o4X>CLl`A?Vqoub?$+?E*^(ADuv@w$eHr&%NohRV9^hEcG}o3W~1J^uoVVEr!q29hB> AK>z>% literal 0 HcmV?d00001 diff --git a/.README.rst.swp b/.README.rst.swp new file mode 100644 index 0000000000000000000000000000000000000000..248fcc950b61fc1ec9709b33cd40756e8011bda9 GIT binary patch literal 16384 zcmeI2S&SoB6^4s}Y-9lu0TR621Rkb`?w%zm2{Va=+pg{|##>zF>2wllrfgrgD^spA zwM@qdu}FbHAVDHg1_@q3AVq8sJP<%i2oeH`MLZBB5CQ@OLGr|sJcsXGFYW0ph=>Oe zWzE;Nt8U$U&VSB5x9W~NM@~64b;IgAEncs;tRoL!=LYvYZoTIw%Sv^}PqV;}AhH=*qU$W%~<{cj12(m%mIqZi!al*%8dbnkmDm8mGNwb%{zBo}RP$=*U z709#x%8gfA*Bv{0M0$AJwb!Vt-_U-AofVfA3KR+y3KR+y3KR+y3KR+y3KRrh-!XlE)xP&vPrsj>zOU>%KmA&~6$%sz6bcjy6bcjy6bcjy6bcjy6bcjy z6bcjy{1+Ff9`c0ho|8S@Oiiuw&1C&EbEW(efSc55zfOy za4)A6ds1p!uwzhcR~PFI0+?qHyninAat4xo8g<3lW~*_^sn`{@wynHPTa_El?Uu8#>8Z^|wPLqib6L4j_gc)jh{{mz7g5SKAe*zOk(6K-C+b zsya2tqnX!OHiN$KGSygDHM>>bq?gi~Q+2#;Gv2!6)n%-81}mv%spUE4cD2+}&33EV zaB&hxD~?;PmYkYhS!HxaRQ4%+R_l39)B8E#+lqH<=W{gLU zcndzNy1h|#Htc%Y7Hb;P%a-HXON7e7vJL4{hTSSLCJDm0l89I}`S`LajN*h=IqRxa zIYqCP+2tgI>r4~Jh*;j724K~)4k>S-RpbwKs^Y%N_|lD|ED1V!7AL71`ePNvnd)ej zr@E)ISm~(88Li?(jgoj6XIkESc{fW{Pba}Sn)NwBXF;mcxS#F%iB`M(5Gu|ym5y{b z=m%Xc3g}{&A4p|7QX`!VgES4|$XM0Vy&#>K!J$+Ik?I7IpNv&MPKIijrD-^dr2%-F_T~@oo_9C`RmtelXOD3@7J$ zaW^08DD&kK#TAFKIOa@(Fd4r3VJgm_3wk&$9!@S>F#Scn!ICu2ldjm?(=%@}e>&`c z(iO(PP)l(`VktgOjgTV?>drG^9y>o(yE+UN?-xk2Jn>n=IF0Tw?n!WsV9QG4ZURe) zdwW7<13%lR{bFFaXsEN}mgUAntxPmif}QGw)WVqsLTdtm>rHcAOJO zac41!SCy-^Dz&{$YPS3YPjW~NW5(%eL=RKTTA9AADoGQin$az}>WtT@G~}8y3~bt1 zazsu$YS)kOBNi9aIP$}Aj8}eiR_Sv(8F%8I`JSl(y3Gwjh%{DxKQVTWNrUP1+YL~d zdFJ;dndtV+g2^)cu~pR!wNL1V{#mWk90!b)rW2IU!&y2r?}A|vGDw<@Il7aRpy?5Qw7AVxvUnMHlAL+TPS#@wabW%o12&pea zGvgUv(LSQ8Na~VDsx4Z1MyoDLkQFo_%a5y>L6$iyCX{GD5;uZ0U`UhP()nCI#FTJT zl*p@TuougZ%!UiYOe~CN!|`cTId4YVPAAl-(>Tdi<<{hk~z}HY(nb0 z{2|G1dfBsTTJB^?V8N00DJv4$(hXzNnCP&k#CSgqYcQG_xuE96gMA}PL7j-cqDmv| zyenXZkX2*4s+RL%9&&`FuTWruY^>JvsB4-WT+A``vMn#@`-D1rJ*(CZxk3VTi9h2> zG_N~yM3D>0>XNmh+>EU9lb))xa{8faCUM8_1fiI)-xQkLcd;Bx1==WAxIZ{l^RvP6 zLx;#abu!Ac!Qu5FWdU5-W&uoPJ*l}&CsMsXa<;mMuAq*F7r>3(l$ zcOa{kOX|_u)gdKDq?sOYBI>A50^X7b-Ltc!7ksJdWJk}=IIIWBVh5YrAI(md<6&p- zbN8&QC$sNsX41^R#XIrjvxtRlb9)!V|ANoV@7b@&_sT5cnJglb`;(F~N^{M`F*g)u zI-c{n!OG>ESOVk0%*lbb~TNxkPTVK_1?Y9|kvmpxr3Cu!@UQyV#{o@Az5sh6Xdc9-3XKe*#3&ouU8$ literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index c1d16d0..dddf33e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store *.pyc algo_venv/ venv/ diff --git a/.setup.py.swp b/.setup.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..174a9155d78184f9c74dda3a3edfedd71fb0fe4a GIT binary patch literal 12288 zcmeI2O=}b}7{^nOVpSAR9*p*oU97WxQCb%VthSY^m8C^Nq?B|vGZUOih9uLn9xQn9 z;N1`4Rqzvd@dJ3&FQ6YppLywY-CDuD6`sI@DsqI+d z^32Ny$7bUM5U;Qx2Ee( zF=Q)hR~oHSQB78#VRsCeauyq z^GFmg?hRb5RneRC7T9zHBuKcmGL*tt{{?B@K-uB7XwM36f*2gx^dIq*yLcqzgmsoR z?imkWLIGesNtLSc2 z481_7BkO$jL`_ - `conanchou `_ - `lcheung90 `_ +- `rasbt `_ + diff --git a/README.rst b/README.rst index 51d08ab..c39fea3 100644 --- a/README.rst +++ b/README.rst @@ -39,6 +39,8 @@ Algorithms implemented so far: **Math:** - Extended GCD +- Standard Normal Probability Density Function +- Cumulative Density Function (Approximation; 16 digit precision for 300 iter.) **Random:** diff --git a/TODO.rst b/TODO.rst index cd38ccb..8b70612 100644 --- a/TODO.rst +++ b/TODO.rst @@ -54,6 +54,13 @@ Below is an ever changing list of things that I would like to accomplish or impl - Primality Testing - Integer Factorization - Closest Pair of Points + - Feature Subset Search Algorithms + - Sequential Search Algorithms + - Sequential Forward Selection (SBS) + - Sequential Backward Selection (SFS) + - 'plus l take away r' - algorithm (SBS + SFS) + - Sequential Floating Forward Algorithm (SFFS) + **Misc.:** diff --git a/algorithms/math/.approx_cdf.py.swp b/algorithms/math/.approx_cdf.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..55f8a66c4ef273eaf17201e3f19957f92a0e4480 GIT binary patch literal 12288 zcmeI2O>Y}T7{@255=b-!l?w-?K6NjD(6OI7o{~rbkBpuxHr4qwTmF zc4ay$42oH~T+E~$cjaVaih4VWlVGxYq$M{n0VZ&20;{6G)(c(urPf9IT5r?dOYf&1){Z%lv*FaajO1egF5U;<2l z2`~XBzyz4UJ4isrLi~JI2wFw*`2BzW|Nonhgm?uR=nAv~{d-P`KcL^CU!eke1bqj6 z34H+(bOBn0Uc>Wm=q2$LAW=-?U;)}838PUtoBJR$1vjMdJd=leeXCZD-1&-bvple2Y>uIJK3 z_dlHy<~+4MuVY+}ZaTVUv5ts2b~WcK76$A8-3M;r^TK2@-G)hUn9+PD!X>l3iw0Cy z*?eEAdg3<`OHrr`a1dJMv6rNUv|inAYrl(=(Qy^oMfj|O+5^&!M^YHlq;oPdA1U(s z9c`77Ru_5MS@W+qL9XAiN)ja1n_qGFqJC z4&2dg(%Gaiws|EMWQ1t7TGQOT+K0pRt!jArc%Qu5v8})Dcl-XdwBGHv*JtGols8;C z2!iRfZ+%}i)45L5&D*<1u^wql6Qd$sVxr_AAkXE*ACFZ|wHrMQnq-=2Rwxs|7 literal 0 HcmV?d00001 diff --git a/algorithms/math/.extended_gcd.py.swp b/algorithms/math/.extended_gcd.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..3b2e366d6a6e1902220339a63fb3744c0bcb5914 GIT binary patch literal 12288 zcmeI2&1w@-6vuA`ceaYYz-gqA#56Nw1tGMnQmR6spjKU#JDJ2&msQBM&8^EDbg_LwV4V`; z$KKM_;}717E6YM8%9e@tWRS=Hur_4Z3v`&c?S9ym{X;YFRRxW8PrIpY$bK)3aqB0O zx4zV_p@v!oj&inpU8fyohZf}k8(;%RG%zcc&Rb@le6}`EXHH)~;^#Px4X^<=zy{a= z8(;%$fDN#L|G|KcX2g9Aa%?nAV|1OFx{eY3T1p^2YX`OAC_O)_7%FHF zyxU%+98b2xxM$=~#@hHm`dZrUdybO9c6EHc9APs|bO#OTH0moz##hszfvaLgvN@!4 zWH&SPj|1&xqh$jzHr9iXaD-mYd_UDld3ksqng9sC4281c6N literal 0 HcmV?d00001 diff --git a/algorithms/math/.std_normal_pdf.py.swp b/algorithms/math/.std_normal_pdf.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..1db5bb6249e6b5538c5cfa30c8fe5959863dc97f GIT binary patch literal 12288 zcmeI2!EVz)5Qewh3y9uf2$is{#<884B#4|Kpdb|@Rd7tTjvZpjv0ZzOnoGIyCUD>l zxFD`P2NF+#fLYhADp0Fli~g1Vtg}1+?96wwyn*w2vP*rtCwSZx;`7Ju-qHK7;`t*X z$|#Vfie;+zE*QIV9;YfRmv)lPWpZxh(P_fYvsf*wpd*tvS;3F1^6cYEs%58C;eMJG zE1B%);lj?>VtsC6Y=8g=T!O%+=z6ZB1Mju&($=k4m+&A21V8`;KmY_l00ck)1VG?_ zA)xXN@s69h)-<%$%o~5r&0kC)00JNY0w4eaAOHd&00JNY0w4eaSCD`Vh4`^4#Ea`( zJb(W`dH?@;Lx?Y|Ppq7ku_SAoMXWol+pOR0`^Eas`o{Xeidjq6OIDrt3UWcoAOHd& z00JNY0w4eaAOHd&00I{hARa}esv;#76)TsldiMCGk%wOLXhdVnGN|p^jzz{{dlkvl zv@Fkds6+f~8T#&b7`?66OfpZsE_>OOPJw;KP654C@!FYeY8jm*3gc20aZu^#`z7T?7RVq@Vzs6)N=rT0 Gz5M}wOssbR literal 0 HcmV?d00001 diff --git a/algorithms/math/approx_cdf.py b/algorithms/math/approx_cdf.py new file mode 100644 index 0000000..23d3ece --- /dev/null +++ b/algorithms/math/approx_cdf.py @@ -0,0 +1,24 @@ +""" + Calculates the cumulative distribution function (CDF) + of the normal distribution based on an approximation by George Marsaglia: + Marsaglia, George (2004). "Evaluating the Normal Distribution". + Journal of Statistical Software 11 (4). + + 16 digit precision for 300 iterations when x = 10. + + Equation: + f(x) = 1/2 + pdf(x) * (x + (x^3/3) + (x^5/3*5) + (x^7/3*7) + ...) +""" + +from algorithms.math import std_normal_pdf + +def cdf(x, iterations = 300): + + product = 1.0 + taylor_exp = [x] + for i in range (3,iterations,2): + product *= i + taylor_exp.append(float(x**i)/product) + taylor_fact = sum(taylor_exp) + + return (0.5 + (taylor_fact * std_normal_pdf.pdf(x, mean=0, std_dev=1))) diff --git a/algorithms/math/std_normal_pdf.py b/algorithms/math/std_normal_pdf.py new file mode 100644 index 0000000..62ef51f --- /dev/null +++ b/algorithms/math/std_normal_pdf.py @@ -0,0 +1,19 @@ +""" + Calculates the normal distribution's probability density + function (PDF). + Calculates Standard normal pdf for mean=0, std_dev=1. + + Equation: + f(x) = 1 / sqrt(2*pi) * e^(-(x-mean)^2/ 2*std_dev^2) +""" + + +def pdf(x, mean=0, std_dev=1): + + PI = 3.141592653589793 + E = 2.718281828459045 + term1 = 1.0 / ( (2 * PI)**0.5 ) + term2 = E**( -1.0* (x-mean)**2.0 / 2.0*(std_dev**2.0) ) + + return term1 * term2 + diff --git a/algorithms/tests/.test_math.py.swp b/algorithms/tests/.test_math.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..be3f51aa137c90b374c4db4916662bdfdfbe3983 GIT binary patch literal 12288 zcmeI2&5zqe7>B1wd{aQ}KQQeIX&16yPi(L4YJ&tRTNSB*)PlH0li1m{+(3eR;SZo(IPg)BIKqh|5+64tE+7sd@tefi*lbKU;((}LOV4K3em(Cy&x|Kp zt^4LH7cX(wuo;fW8T zfDDiUGC&5%02v?yWPl8i0W$DEG#~@UzP_KaA0I&R`2YX>@Bd#OW9$d;J-7wl0tpy_ zE_fE40lz=W*f-#Pa1&eyuL1*Xf_uStCmH()+yZZcJs02v?yWPl9(cLsQQBxxQRDox|uka-?vswd-7iq`|#EMQS#<&N?i z=Sf;=HgptTEo8hdTii5+<83=m+p&C0*p6$qHx6%?yu$~dYcLn5Rw`lA!TVplJdTsw zvxQ~buDxw;dyZ`_(#|XScQVAZm>?YT=?kA)Zyv_OjdP3*V$3$bg7-k58IQAc(#5qR ziIfgB^y#k5Lmjx%cudo_E#cW1m+&pmG`;$e#>g`12&=_Mp-ei$Z1G$Ly&yd3G>kM? zG6`fBV5Mav8FTc&htru}-pgwBwr%0NrrWk1;S1Zc+@s|-t;goVR$qe0C}VX!^|{yP zkIwyN6}$kiFP-1L!{qB*%l+YEzV4X(NiO+QTsUa-TfFiN=?A^PfrmMqs4xk_ptm;& za7PQl3%hH4H%fw93{Qs~9b>u?xtPX!X5TL}dt#Q^U-Y>=v+uXKSF^V?Gdk9pJ<*g| zcx7f?0fv0X#KNluHH;YD>b!nqT5Wi$y3AV{%JKmV2dXR8H6?atsv9#~^USV08*$~~ zSf1HJX15a6kQp88A-isUh1S-~(WUxaxtuO^&o5uA!z>+f!ezKycK0F1J12mk;8 literal 0 HcmV?d00001 diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index c382f77..eab325d 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -1,6 +1,9 @@ import unittest -from ..math.extended_gcd import extended_gcd +import nose +from ..math.extended_gcd import extended_gcd +from ..math.std_normal_pdf import pdf +from ..math.approx_cdf import cdf class TestExtendedGCD(unittest.TestCase): @@ -24,3 +27,36 @@ def test_extended_gcd(self): # Find extended_gcd of 50 and 15 (a, b) = extended_gcd(50, 15) self.assertIs(50 * a + 15 * b, 5) + + +class TestStdNormPDF(unittest.TestCase): + + def test_pdf(self): + # Calculate standard normal pdf for x=1 + a = pdf(1) + nose.tools.assert_almost_equal(a, 0.24197072451914337) + + # Calculate standard normal pdf for x=(-1) + a = pdf(-1) + nose.tools.assert_almost_equal(a, 0.24197072451914337) + + # Calculate standard normal pdf for x=13, mean=10, std_dev=1 + a = pdf(x=13, mean=10, std_dev=1) + nose.tools.assert_almost_equal(a, 0.004431848411938008) + + +class TestApproxCdf(unittest.TestCase): + + def test_cdf(self): + # Calculate cumulative distribution function for x=1 + a = cdf(1) + nose.tools.assert_almost_equal(a, 0.841344746068543) + + # Calculate cumulative distribution function x=0 + a = cdf(0) + nose.tools.assert_almost_equal(a, 0.5) + + # Calculate cumulative distribution function for x=(-1) + a = cdf(-1) + nose.tools.assert_almost_equal(a, 0.15865525393145702) + diff --git a/setup.py b/setup.py index 8684d60..a914f81 100644 --- a/setup.py +++ b/setup.py @@ -16,4 +16,6 @@ def long_description(): license='BSD', packages=['algorithms', 'algorithms.sorting', 'algorithms.shuffling', 'algorithms.searching', 'algorithms.math', 'algorithms.tests'], + classifiers=[ + 'Programming Language :: Python :: 2.7',], zip_safe=False) From 7075bc3ec402bab61fb69df3816344e32ec9a4ef Mon Sep 17 00:00:00 2001 From: rasbt Date: Thu, 27 Mar 2014 00:27:48 -0400 Subject: [PATCH 13/89] math functions cdf + pdf, algorithm todo upd. --- .AUTHORS.rst.swp | Bin 12288 -> 0 bytes .README.rst.swp | Bin 16384 -> 0 bytes .gitignore | 1 + .setup.py.swp | Bin 12288 -> 0 bytes algorithms/math/.approx_cdf.py.swp | Bin 12288 -> 0 bytes algorithms/math/.extended_gcd.py.swp | Bin 12288 -> 0 bytes algorithms/math/.std_normal_pdf.py.swp | Bin 12288 -> 0 bytes algorithms/tests/.test_math.py.swp | Bin 12288 -> 0 bytes 8 files changed, 1 insertion(+) delete mode 100644 .AUTHORS.rst.swp delete mode 100644 .README.rst.swp delete mode 100644 .setup.py.swp delete mode 100644 algorithms/math/.approx_cdf.py.swp delete mode 100644 algorithms/math/.extended_gcd.py.swp delete mode 100644 algorithms/math/.std_normal_pdf.py.swp delete mode 100644 algorithms/tests/.test_math.py.swp diff --git a/.AUTHORS.rst.swp b/.AUTHORS.rst.swp deleted file mode 100644 index 8f294ae363d7c52ad68a2c13556ed3a669444e60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&uSDw5XLLw(P%KB*HOXS?rajlxCB8Ws3D19691gsp5D$(XJ)#`?w*Yaq7V;x z1)o4X>CLl`A?Vqoub?$+?E*^(ADuv@w$eHr&%NohRV9^hEcG}o3W~1J^uoVVEr!q29hB> AK>z>% diff --git a/.README.rst.swp b/.README.rst.swp deleted file mode 100644 index 248fcc950b61fc1ec9709b33cd40756e8011bda9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI2S&SoB6^4s}Y-9lu0TR621Rkb`?w%zm2{Va=+pg{|##>zF>2wllrfgrgD^spA zwM@qdu}FbHAVDHg1_@q3AVq8sJP<%i2oeH`MLZBB5CQ@OLGr|sJcsXGFYW0ph=>Oe zWzE;Nt8U$U&VSB5x9W~NM@~64b;IgAEncs;tRoL!=LYvYZoTIw%Sv^}PqV;}AhH=*qU$W%~<{cj12(m%mIqZi!al*%8dbnkmDm8mGNwb%{zBo}RP$=*U z709#x%8gfA*Bv{0M0$AJwb!Vt-_U-AofVfA3KR+y3KR+y3KR+y3KR+y3KRrh-!XlE)xP&vPrsj>zOU>%KmA&~6$%sz6bcjy6bcjy6bcjy6bcjy6bcjy z6bcjy{1+Ff9`c0ho|8S@Oiiuw&1C&EbEW(efSc55zfOy za4)A6ds1p!uwzhcR~PFI0+?qHyninAat4xo8g<3lW~*_^sn`{@wynHPTa_El?Uu8#>8Z^|wPLqib6L4j_gc)jh{{mz7g5SKAe*zOk(6K-C+b zsya2tqnX!OHiN$KGSygDHM>>bq?gi~Q+2#;Gv2!6)n%-81}mv%spUE4cD2+}&33EV zaB&hxD~?;PmYkYhS!HxaRQ4%+R_l39)B8E#+lqH<=W{gLU zcndzNy1h|#Htc%Y7Hb;P%a-HXON7e7vJL4{hTSSLCJDm0l89I}`S`LajN*h=IqRxa zIYqCP+2tgI>r4~Jh*;j724K~)4k>S-RpbwKs^Y%N_|lD|ED1V!7AL71`ePNvnd)ej zr@E)ISm~(88Li?(jgoj6XIkESc{fW{Pba}Sn)NwBXF;mcxS#F%iB`M(5Gu|ym5y{b z=m%Xc3g}{&A4p|7QX`!VgES4|$XM0Vy&#>K!J$+Ik?I7IpNv&MPKIijrD-^dr2%-F_T~@oo_9C`RmtelXOD3@7J$ zaW^08DD&kK#TAFKIOa@(Fd4r3VJgm_3wk&$9!@S>F#Scn!ICu2ldjm?(=%@}e>&`c z(iO(PP)l(`VktgOjgTV?>drG^9y>o(yE+UN?-xk2Jn>n=IF0Tw?n!WsV9QG4ZURe) zdwW7<13%lR{bFFaXsEN}mgUAntxPmif}QGw)WVqsLTdtm>rHcAOJO zac41!SCy-^Dz&{$YPS3YPjW~NW5(%eL=RKTTA9AADoGQin$az}>WtT@G~}8y3~bt1 zazsu$YS)kOBNi9aIP$}Aj8}eiR_Sv(8F%8I`JSl(y3Gwjh%{DxKQVTWNrUP1+YL~d zdFJ;dndtV+g2^)cu~pR!wNL1V{#mWk90!b)rW2IU!&y2r?}A|vGDw<@Il7aRpy?5Qw7AVxvUnMHlAL+TPS#@wabW%o12&pea zGvgUv(LSQ8Na~VDsx4Z1MyoDLkQFo_%a5y>L6$iyCX{GD5;uZ0U`UhP()nCI#FTJT zl*p@TuougZ%!UiYOe~CN!|`cTId4YVPAAl-(>Tdi<<{hk~z}HY(nb0 z{2|G1dfBsTTJB^?V8N00DJv4$(hXzNnCP&k#CSgqYcQG_xuE96gMA}PL7j-cqDmv| zyenXZkX2*4s+RL%9&&`FuTWruY^>JvsB4-WT+A``vMn#@`-D1rJ*(CZxk3VTi9h2> zG_N~yM3D>0>XNmh+>EU9lb))xa{8faCUM8_1fiI)-xQkLcd;Bx1==WAxIZ{l^RvP6 zLx;#abu!Ac!Qu5FWdU5-W&uoPJ*l}&CsMsXa<;mMuAq*F7r>3(l$ zcOa{kOX|_u)gdKDq?sOYBI>A50^X7b-Ltc!7ksJdWJk}=IIIWBVh5YrAI(md<6&p- zbN8&QC$sNsX41^R#XIrjvxtRlb9)!V|ANoV@7b@&_sT5cnJglb`;(F~N^{M`F*g)u zI-c{n!OG>ESOVk0%*lbb~TNxkPTVK_1?Y9|kvmpxr3Cu!@UQyV#{o@Az5sh6Xdc9-3XKe*#3&ouU8$ diff --git a/.gitignore b/.gitignore index dddf33e..e5a450e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.swp .DS_Store *.pyc algo_venv/ diff --git a/.setup.py.swp b/.setup.py.swp deleted file mode 100644 index 174a9155d78184f9c74dda3a3edfedd71fb0fe4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2O=}b}7{^nOVpSAR9*p*oU97WxQCb%VthSY^m8C^Nq?B|vGZUOih9uLn9xQn9 z;N1`4Rqzvd@dJ3&FQ6YppLywY-CDuD6`sI@DsqI+d z^32Ny$7bUM5U;Qx2Ee( zF=Q)hR~oHSQB78#VRsCeauyq z^GFmg?hRb5RneRC7T9zHBuKcmGL*tt{{?B@K-uB7XwM36f*2gx^dIq*yLcqzgmsoR z?imkWLIGesNtLSc2 z481_7BkO$jLY}T7{@255=b-!l?w-?K6NjD(6OI7o{~rbkBpuxHr4qwTmF zc4ay$42oH~T+E~$cjaVaih4VWlVGxYq$M{n0VZ&20;{6G)(c(urPf9IT5r?dOYf&1){Z%lv*FaajO1egF5U;<2l z2`~XBzyz4UJ4isrLi~JI2wFw*`2BzW|Nonhgm?uR=nAv~{d-P`KcL^CU!eke1bqj6 z34H+(bOBn0Uc>Wm=q2$LAW=-?U;)}838PUtoBJR$1vjMdJd=leeXCZD-1&-bvple2Y>uIJK3 z_dlHy<~+4MuVY+}ZaTVUv5ts2b~WcK76$A8-3M;r^TK2@-G)hUn9+PD!X>l3iw0Cy z*?eEAdg3<`OHrr`a1dJMv6rNUv|inAYrl(=(Qy^oMfj|O+5^&!M^YHlq;oPdA1U(s z9c`77Ru_5MS@W+qL9XAiN)ja1n_qGFqJC z4&2dg(%Gaiws|EMWQ1t7TGQOT+K0pRt!jArc%Qu5v8})Dcl-XdwBGHv*JtGols8;C z2!iRfZ+%}i)45L5&D*<1u^wql6Qd$sVxr_AAkXE*ACFZ|wHrMQnq-=2Rwxs|7 diff --git a/algorithms/math/.extended_gcd.py.swp b/algorithms/math/.extended_gcd.py.swp deleted file mode 100644 index 3b2e366d6a6e1902220339a63fb3744c0bcb5914..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&1w@-6vuA`ceaYYz-gqA#56Nw1tGMnQmR6spjKU#JDJ2&msQBM&8^EDbg_LwV4V`; z$KKM_;}717E6YM8%9e@tWRS=Hur_4Z3v`&c?S9ym{X;YFRRxW8PrIpY$bK)3aqB0O zx4zV_p@v!oj&inpU8fyohZf}k8(;%RG%zcc&Rb@le6}`EXHH)~;^#Px4X^<=zy{a= z8(;%$fDN#L|G|KcX2g9Aa%?nAV|1OFx{eY3T1p^2YX`OAC_O)_7%FHF zyxU%+98b2xxM$=~#@hHm`dZrUdybO9c6EHc9APs|bO#OTH0moz##hszfvaLgvN@!4 zWH&SPj|1&xqh$jzHr9iXaD-mYd_UDld3ksqng9sC4281c6N diff --git a/algorithms/math/.std_normal_pdf.py.swp b/algorithms/math/.std_normal_pdf.py.swp deleted file mode 100644 index 1db5bb6249e6b5538c5cfa30c8fe5959863dc97f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2!EVz)5Qewh3y9uf2$is{#<884B#4|Kpdb|@Rd7tTjvZpjv0ZzOnoGIyCUD>l zxFD`P2NF+#fLYhADp0Fli~g1Vtg}1+?96wwyn*w2vP*rtCwSZx;`7Ju-qHK7;`t*X z$|#Vfie;+zE*QIV9;YfRmv)lPWpZxh(P_fYvsf*wpd*tvS;3F1^6cYEs%58C;eMJG zE1B%);lj?>VtsC6Y=8g=T!O%+=z6ZB1Mju&($=k4m+&A21V8`;KmY_l00ck)1VG?_ zA)xXN@s69h)-<%$%o~5r&0kC)00JNY0w4eaAOHd&00JNY0w4eaSCD`Vh4`^4#Ea`( zJb(W`dH?@;Lx?Y|Ppq7ku_SAoMXWol+pOR0`^Eas`o{Xeidjq6OIDrt3UWcoAOHd& z00JNY0w4eaAOHd&00I{hARa}esv;#76)TsldiMCGk%wOLXhdVnGN|p^jzz{{dlkvl zv@Fkds6+f~8T#&b7`?66OfpZsE_>OOPJw;KP654C@!FYeY8jm*3gc20aZu^#`z7T?7RVq@Vzs6)N=rT0 Gz5M}wOssbR diff --git a/algorithms/tests/.test_math.py.swp b/algorithms/tests/.test_math.py.swp deleted file mode 100644 index be3f51aa137c90b374c4db4916662bdfdfbe3983..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&5zqe7>B1wd{aQ}KQQeIX&16yPi(L4YJ&tRTNSB*)PlH0li1m{+(3eR;SZo(IPg)BIKqh|5+64tE+7sd@tefi*lbKU;((}LOV4K3em(Cy&x|Kp zt^4LH7cX(wuo;fW8T zfDDiUGC&5%02v?yWPl8i0W$DEG#~@UzP_KaA0I&R`2YX>@Bd#OW9$d;J-7wl0tpy_ zE_fE40lz=W*f-#Pa1&eyuL1*Xf_uStCmH()+yZZcJs02v?yWPl9(cLsQQBxxQRDox|uka-?vswd-7iq`|#EMQS#<&N?i z=Sf;=HgptTEo8hdTii5+<83=m+p&C0*p6$qHx6%?yu$~dYcLn5Rw`lA!TVplJdTsw zvxQ~buDxw;dyZ`_(#|XScQVAZm>?YT=?kA)Zyv_OjdP3*V$3$bg7-k58IQAc(#5qR ziIfgB^y#k5Lmjx%cudo_E#cW1m+&pmG`;$e#>g`12&=_Mp-ei$Z1G$Ly&yd3G>kM? zG6`fBV5Mav8FTc&htru}-pgwBwr%0NrrWk1;S1Zc+@s|-t;goVR$qe0C}VX!^|{yP zkIwyN6}$kiFP-1L!{qB*%l+YEzV4X(NiO+QTsUa-TfFiN=?A^PfrmMqs4xk_ptm;& za7PQl3%hH4H%fw93{Qs~9b>u?xtPX!X5TL}dt#Q^U-Y>=v+uXKSF^V?Gdk9pJ<*g| zcx7f?0fv0X#KNluHH;YD>b!nqT5Wi$y3AV{%JKmV2dXR8H6?atsv9#~^USV08*$~~ zSf1HJX15a6kQp88A-isUh1S-~(WUxaxtuO^&o5uA!z>+f!ezKycK0F1J12mk;8 From 6a4a9f57cf4451aa91a1ab96cd3640399474fb46 Mon Sep 17 00:00:00 2001 From: fsp Date: Mon, 7 Apr 2014 14:04:37 +0800 Subject: [PATCH 14/89] Moved import statement out of sort func in quick_sort_in_place --- algorithms/sorting/quick_sort_in_place.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/sorting/quick_sort_in_place.py b/algorithms/sorting/quick_sort_in_place.py index 375261f..52141b7 100644 --- a/algorithms/sorting/quick_sort_in_place.py +++ b/algorithms/sorting/quick_sort_in_place.py @@ -17,6 +17,7 @@ Psuedo Code: http://en.wikipedia.org/wiki/Quicksort#In-place_version """ +from random import randrange def partition(seq, left, right, pivot_index): pivot_value = seq[pivot_index] @@ -31,7 +32,6 @@ def partition(seq, left, right, pivot_index): def sort(seq, left, right): """in-place version of quicksort""" - from random import randrange if len(seq) <= 1: return seq elif left < right: From fa762b40f949533b7477d33eafe6400bcf74309b Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Fri, 15 Mar 2013 00:19:38 +0400 Subject: [PATCH 15/89] Initial LCM support: --- algorithms/math/lcm.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 algorithms/math/lcm.py diff --git a/algorithms/math/lcm.py b/algorithms/math/lcm.py new file mode 100644 index 0000000..c99e566 --- /dev/null +++ b/algorithms/math/lcm.py @@ -0,0 +1,9 @@ +from extended_gcd import extended_gcd + + +def lcm(a, b): + """ + Returns LCM based on GCD of digit. + """ + x, y = extended_gcd(a, b) + return a * b / (a * y + b * x) From cff8321469e66e61f01e7c92d98596a09600cb19 Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Fri, 15 Mar 2013 00:19:56 +0400 Subject: [PATCH 16/89] Initial LCM tests --- algorithms/tests/test_math.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index c382f77..465ee1a 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -1,5 +1,6 @@ import unittest from ..math.extended_gcd import extended_gcd +from ..math.lcm import lcm class TestExtendedGCD(unittest.TestCase): @@ -24,3 +25,16 @@ def test_extended_gcd(self): # Find extended_gcd of 50 and 15 (a, b) = extended_gcd(50, 15) self.assertIs(50 * a + 15 * b, 5) + + +class TestLCM(unittest.TestCase): + def test_lcm(self): + # Find lcm of 16 and 20 + r = lcm(16, 20) + self.assertEqual(80, abs(r)) + + # Find lcm for 20 and 16 + r2 = lcm(20, 16) + + # Checks that lcm function is commutative + self.assertEqual(r, r2) From 27648e81bdce789e4d7f389b5f78fa34dfd8c687 Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Fri, 15 Mar 2013 00:24:50 +0400 Subject: [PATCH 17/89] Okay, it's fast fix. --- algorithms/math/lcm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/math/lcm.py b/algorithms/math/lcm.py index c99e566..36c373f 100644 --- a/algorithms/math/lcm.py +++ b/algorithms/math/lcm.py @@ -6,4 +6,4 @@ def lcm(a, b): Returns LCM based on GCD of digit. """ x, y = extended_gcd(a, b) - return a * b / (a * y + b * x) + return a * b / (a * x + b * y) From 8f99144bc3e06794b5cd7aff1a33e44a1098db0e Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Fri, 15 Mar 2013 00:28:18 +0400 Subject: [PATCH 18/89] Pythonic fastfix --- algorithms/tests/test_math.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index 465ee1a..d3eb206 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -29,12 +29,9 @@ def test_extended_gcd(self): class TestLCM(unittest.TestCase): def test_lcm(self): - # Find lcm of 16 and 20 - r = lcm(16, 20) - self.assertEqual(80, abs(r)) - - # Find lcm for 20 and 16 - r2 = lcm(20, 16) + # Find lcm of (16, 20) and (20, 16) + r, r2 = lcm(16, 20), lcm(20, 16) + self.assertEqual(r, 80) # Checks that lcm function is commutative self.assertEqual(r, r2) From 1857b1e61f4e16fa11a7111defa95da3f3f214f7 Mon Sep 17 00:00:00 2001 From: Dmitrij Veselov Date: Tue, 16 Apr 2013 03:58:24 +0400 Subject: [PATCH 19/89] Simple version of lcm, that does not have any dependencies --- algorithms/math/lcm.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/algorithms/math/lcm.py b/algorithms/math/lcm.py index 36c373f..35f1c77 100644 --- a/algorithms/math/lcm.py +++ b/algorithms/math/lcm.py @@ -1,9 +1,8 @@ -from extended_gcd import extended_gcd - - def lcm(a, b): """ - Returns LCM based on GCD of digit. + Simple version of lcm, that does not have any dependencies """ - x, y = extended_gcd(a, b) - return a * b / (a * x + b * y) + tmp_a = a + while (tmp_a % b) != 0: + tmp_a += a + return tmp_a From f0ee0b92370f14f9580ef810fe9e0e18bdab6434 Mon Sep 17 00:00:00 2001 From: kabrapratik28 Date: Wed, 16 Jul 2014 01:19:39 +0530 Subject: [PATCH 20/89] Data Structure Implemented 1.Stack 2.Queue --- algorithms/data_structure/__init__.py | 0 algorithms/data_structure/queue.py | 33 ++++++++++++++++++++++ algorithms/data_structure/stack.py | 30 ++++++++++++++++++++ algorithms/tests/test_data_structure.py | 37 +++++++++++++++++++++++++ setup.py | 2 +- 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 algorithms/data_structure/__init__.py create mode 100644 algorithms/data_structure/queue.py create mode 100644 algorithms/data_structure/stack.py create mode 100644 algorithms/tests/test_data_structure.py diff --git a/algorithms/data_structure/__init__.py b/algorithms/data_structure/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/algorithms/data_structure/queue.py b/algorithms/data_structure/queue.py new file mode 100644 index 0000000..0c938d5 --- /dev/null +++ b/algorithms/data_structure/queue.py @@ -0,0 +1,33 @@ +""" + Queue data structure implemented: + -------------------------------- + add : add element at last + remove : remove element from front + return value + is_empty : 1 value returned on empty + 0 value returned on not empty + size : return size of queue + + Time Complexity: O(1) +""" + +from collections import deque + +class queue : + queue_list = deque([]) + def __init__(self): + self.queue_list = deque([]) + def add(self,value): + self.queue_list.append(value) + def remove(self): + return self.queue_list.popleft() + def is_empty(self): + if not len(self.queue_list): + return 1 + else : + return 0 + def size(self): + return len(self.queue_list) + + + diff --git a/algorithms/data_structure/stack.py b/algorithms/data_structure/stack.py new file mode 100644 index 0000000..cc1584b --- /dev/null +++ b/algorithms/data_structure/stack.py @@ -0,0 +1,30 @@ +""" + Stack data structure implemented: + -------------------------------- + add : add element at last + remove : remove element from last + return value + is_empty : 1 value returned on empty + 0 value returned on not empty + size : return size of stack + + Time Complexity: O(1) +""" + +class stack : + stack_list = [] + def __init__(self): + self.stack_list = [] + def add(self,value): + self.stack_list.append(value) + def remove(self): + return self.stack_list.pop() + def is_empty(self): + if not len(self.stack_list): + return 1 + else : + return 0 + def size(self): + return len(self.stack_list) + + diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py new file mode 100644 index 0000000..5b15a67 --- /dev/null +++ b/algorithms/tests/test_data_structure.py @@ -0,0 +1,37 @@ +import unittest +from ..data_structure import stack,queue + +class TestStack(unittest.TestCase): + """ + Test Stack Implementation + """ + def test_stack(self): + self.sta = stack.stack() + self.sta.add(5) + self.sta.add(8) + self.sta.add(10) + self.sta.add(2) + + self.assertEqual(self.sta.remove(),2) + self.assertEqual(self.sta.is_empty(),0) + self.assertEqual(self.sta.size(),3) + +class TestQueue(unittest.TestCase): + """ + Test Queue Implementation + """ + def test_queue(self): + self.que = queue.queue() + self.que.add(1) + self.que.add(2) + self.que.add(8) + self.que.add(5) + self.que.add(6) + + self.assertEqual(self.que.remove(),1) + self.assertEqual(self.que.size(),4) + self.assertEqual(self.que.remove(),2) + self.assertEqual(self.que.remove(),8) + self.assertEqual(self.que.remove(),5) + self.assertEqual(self.que.remove(),6) + self.assertEqual(self.que.is_empty(),1) diff --git a/setup.py b/setup.py index a914f81..7c914c0 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ def long_description(): author='Nic Young', author_email='nryoung@gmail.com', license='BSD', - packages=['algorithms', 'algorithms.sorting', 'algorithms.shuffling', + packages=['algorithms', 'algorithms.data_structure','algorithms.sorting', 'algorithms.shuffling', 'algorithms.searching', 'algorithms.math', 'algorithms.tests'], classifiers=[ 'Programming Language :: Python :: 2.7',], From 0eaba3dc1c43dd26bd12be7b07b70607368f904e Mon Sep 17 00:00:00 2001 From: kabrapratik28 Date: Wed, 16 Jul 2014 01:50:51 +0530 Subject: [PATCH 21/89] True, False return on is_empty change in data structure (queue and stack) --- algorithms/data_structure/queue.py | 4 ++-- algorithms/data_structure/stack.py | 4 ++-- algorithms/tests/test_data_structure.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/algorithms/data_structure/queue.py b/algorithms/data_structure/queue.py index 0c938d5..5b6a29c 100644 --- a/algorithms/data_structure/queue.py +++ b/algorithms/data_structure/queue.py @@ -23,9 +23,9 @@ def remove(self): return self.queue_list.popleft() def is_empty(self): if not len(self.queue_list): - return 1 + return True else : - return 0 + return False def size(self): return len(self.queue_list) diff --git a/algorithms/data_structure/stack.py b/algorithms/data_structure/stack.py index cc1584b..f94b3eb 100644 --- a/algorithms/data_structure/stack.py +++ b/algorithms/data_structure/stack.py @@ -21,9 +21,9 @@ def remove(self): return self.stack_list.pop() def is_empty(self): if not len(self.stack_list): - return 1 + return True else : - return 0 + return False def size(self): return len(self.stack_list) diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index 5b15a67..f43d882 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -13,7 +13,7 @@ def test_stack(self): self.sta.add(2) self.assertEqual(self.sta.remove(),2) - self.assertEqual(self.sta.is_empty(),0) + self.assertEqual(self.sta.is_empty(),False) self.assertEqual(self.sta.size(),3) class TestQueue(unittest.TestCase): @@ -34,4 +34,4 @@ def test_queue(self): self.assertEqual(self.que.remove(),8) self.assertEqual(self.que.remove(),5) self.assertEqual(self.que.remove(),6) - self.assertEqual(self.que.is_empty(),1) + self.assertEqual(self.que.is_empty(),True) From be9b2fd188ad7a93f3bb293e0241c182b6c5e1b3 Mon Sep 17 00:00:00 2001 From: kabrapratik28 Date: Wed, 16 Jul 2014 09:01:09 +0530 Subject: [PATCH 22/89] If else in function is_empty removed --- algorithms/data_structure/queue.py | 5 +---- algorithms/data_structure/stack.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/algorithms/data_structure/queue.py b/algorithms/data_structure/queue.py index 5b6a29c..e2325a7 100644 --- a/algorithms/data_structure/queue.py +++ b/algorithms/data_structure/queue.py @@ -22,10 +22,7 @@ def add(self,value): def remove(self): return self.queue_list.popleft() def is_empty(self): - if not len(self.queue_list): - return True - else : - return False + return not len(self.queue_list) def size(self): return len(self.queue_list) diff --git a/algorithms/data_structure/stack.py b/algorithms/data_structure/stack.py index f94b3eb..e877e42 100644 --- a/algorithms/data_structure/stack.py +++ b/algorithms/data_structure/stack.py @@ -20,10 +20,7 @@ def add(self,value): def remove(self): return self.stack_list.pop() def is_empty(self): - if not len(self.stack_list): - return True - else : - return False + return not len(self.stack_list) def size(self): return len(self.stack_list) From 664a758ed5ee8e0eea2757c0b0fa1fdaedea9d41 Mon Sep 17 00:00:00 2001 From: Sahaj Sawhney Date: Wed, 16 Jul 2014 12:58:48 +0530 Subject: [PATCH 23/89] gnome sort added --- algorithms/sorting/gnome_sort.py | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 algorithms/sorting/gnome_sort.py diff --git a/algorithms/sorting/gnome_sort.py b/algorithms/sorting/gnome_sort.py new file mode 100644 index 0000000..5d351d3 --- /dev/null +++ b/algorithms/sorting/gnome_sort.py @@ -0,0 +1,33 @@ +""" + gnome_sort.py + + Implementation of gnome sort on a list and returns a sorted list. + + Gnome Sort Overview: + --------------------- + A sorting algorithm similar to insertion sort except that the element is moved to its proper place by a series of swaps. + + Time Complexity: O(n^2) + + Space Complexity: O(1) auxillary + + Stable: No + + Psuedo code: http://en.wikipedia.org/wiki/Gnome_sort + +""" + + +def sort(seq): + + i = 1 + while i < len(seq): + if seq[i] < seq[i-1]: + seq[i], seq[i-1] = seq[i-1], seq[i] + if i > 1: + i -= 1 + else: + i += 1 + return seq + +print sort([11, 14, 11, -1, 24, -12343, -0.34, 123.22, 12, 14, 23,33]) \ No newline at end of file From 4302572bd4815b50868a8b56e5489b77ea78f05b Mon Sep 17 00:00:00 2001 From: Sahaj Sawhney Date: Wed, 16 Jul 2014 13:15:54 +0530 Subject: [PATCH 24/89] added unit test for gnome_sort --- algorithms/sorting/gnome_sort.py | 10 ++++++++-- algorithms/tests/test_sorting.py | 12 +++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/algorithms/sorting/gnome_sort.py b/algorithms/sorting/gnome_sort.py index 5d351d3..5c41fdd 100644 --- a/algorithms/sorting/gnome_sort.py +++ b/algorithms/sorting/gnome_sort.py @@ -21,13 +21,19 @@ def sort(seq): i = 1 + last = 0 while i < len(seq): if seq[i] < seq[i-1]: seq[i], seq[i-1] = seq[i-1], seq[i] if i > 1: + if last == 0: + last = i i -= 1 + else: + i += 1 else: + if last != 0: + i = last + last = 0 i += 1 return seq - -print sort([11, 14, 11, -1, 24, -12343, -0.34, 123.22, 12, 14, 23,33]) \ No newline at end of file diff --git a/algorithms/tests/test_sorting.py b/algorithms/tests/test_sorting.py index 23f3665..16ba65c 100644 --- a/algorithms/tests/test_sorting.py +++ b/algorithms/tests/test_sorting.py @@ -2,7 +2,7 @@ import unittest from ..sorting import bubble_sort, selection_sort, insertion_sort, \ merge_sort, quick_sort, heap_sort, shell_sort, comb_sort, cocktail_sort, \ - quick_sort_in_place + quick_sort_in_place, gnome_sort class SortingAlgorithmTestCase(unittest.TestCase): @@ -128,3 +128,13 @@ class TestCocktailSort(SortingAlgorithmTestCase): def test_cocktailsort(self): self.output = cocktail_sort.sort(self.input) self.assertEqual(self.correct, self.output) + + +class TestGnomeSort(SortingAlgorithmTestCase): + """ + Tests Gnome sort on a small range from 0-9 + """ + + def test_gnomesort(self): + self.output = gnome_sort.sort(self.input) + self.assertEqual(self.correct, self.output) From 00790f60261873661187dff6f61af7a8183b190c Mon Sep 17 00:00:00 2001 From: Sahaj Sawhney Date: Wed, 16 Jul 2014 13:24:09 +0530 Subject: [PATCH 25/89] updated README.rst --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index c39fea3..7400d2a 100644 --- a/README.rst +++ b/README.rst @@ -23,6 +23,7 @@ Algorithms implemented so far: - In Place Quick Sort - Selection Sort - Shell Sort +- Gnome Sort **Searching:** From 09e54873077cd1f5d279e5ba3450c047e65216ae Mon Sep 17 00:00:00 2001 From: Sai Teja Pratap Date: Tue, 16 Sep 2014 00:14:42 +0530 Subject: [PATCH 26/89] make sieve of eratosthenes efficient --- algorithms/math/sieve_eratosthenes.py | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/algorithms/math/sieve_eratosthenes.py b/algorithms/math/sieve_eratosthenes.py index ae2d23e..077461e 100644 --- a/algorithms/math/sieve_eratosthenes.py +++ b/algorithms/math/sieve_eratosthenes.py @@ -17,16 +17,20 @@ Pseudocode: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes """ -def eratosthenes(end,start=2): - if start < 2: - start = 2 - primes = range(start,end) - marker = 2 - while marker < end: - for i in xrange(marker, end+1): - if marker*i in primes: - primes.remove(marker*i) - marker += 1 + +def eratosthenes(end, start=2): + primes = [] + if end < start or end < 2: + return [] + is_prime = [True for i in xrange(end + 1)] + is_prime[0] = is_prime[1] = False + for i in xrange(2, end + 1): + if not is_prime[i]: + continue + if start <= i <= end: + primes.append(i) + j = i * i + while j <= end: + is_prime[j] = False + j += i return primes - - From 3d68e6c87dec636d9620f3e8b862038562934964 Mon Sep 17 00:00:00 2001 From: Sai Teja Pratap Date: Thu, 18 Sep 2014 18:00:00 +0530 Subject: [PATCH 27/89] basic primality test --- algorithms/math/primality_test.py | 34 +++++++++++++++++++++++++++ algorithms/math/sieve_eratosthenes.py | 4 +++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 algorithms/math/primality_test.py diff --git a/algorithms/math/primality_test.py b/algorithms/math/primality_test.py new file mode 100644 index 0000000..036ec5a --- /dev/null +++ b/algorithms/math/primality_test.py @@ -0,0 +1,34 @@ +from math import sqrt +from algorithms.math.sieve_eratosthenes import eratosthenes +CACHE_LIMIT = 10 ** 6 +primes_cache_list = [] +primes_cache_bool = [] + + +def is_prime(number, cache=True): + if number < 2: + return False + global primes_cache_list, primes_cache_bool + if cache and len(primes_cache_list) == 0: + primes_cache_list,primes_cache_bool = eratosthenes(CACHE_LIMIT, return_boolean=True) + for prime in primes_cache_list: + primes_cache_bool[prime] = True + if number < len(primes_cache_bool): + return primes_cache_bool[number] + + sqrt_number = sqrt(number) + for prime in primes_cache_list: + if prime > sqrt_number: + return True + if number % prime == 0: + return False + + to_check = 2 + if len(primes_cache_list) > 0: + to_check = primes_cache_list[-1] + 1 + while to_check <= sqrt_number: + if number % to_check == 0: + return False + to_check += 1 + return True + diff --git a/algorithms/math/sieve_eratosthenes.py b/algorithms/math/sieve_eratosthenes.py index 077461e..9c29ec2 100644 --- a/algorithms/math/sieve_eratosthenes.py +++ b/algorithms/math/sieve_eratosthenes.py @@ -18,7 +18,7 @@ """ -def eratosthenes(end, start=2): +def eratosthenes(end, start=2, return_boolean=False): primes = [] if end < start or end < 2: return [] @@ -33,4 +33,6 @@ def eratosthenes(end, start=2): while j <= end: is_prime[j] = False j += i + if return_boolean: + return primes, is_prime return primes From 28f355f91aded258e1f1388ee9f544eb5bc73ea3 Mon Sep 17 00:00:00 2001 From: JoaoGFarias Date: Sun, 5 Apr 2015 00:23:33 -0300 Subject: [PATCH 28/89] Updating README and AUTHORS files Including new section/folder: Data Structures --- AUTHORS.rst | 3 ++- README.rst | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index a6eb4db..4bf9bf9 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -20,4 +20,5 @@ List of contributors: - `conanchou `_ - `lcheung90 `_ - `rasbt `_ - +- `JoaoGFarias `_ +- `kabrapratik28 `_ diff --git a/README.rst b/README.rst index c39fea3..5eaece7 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,11 @@ I used psuedo code from various sources and I have listed them as references in Algorithms implemented so far: ------------------------------ +**Data Structures:** + +- Queue +- Stack + **Sorting:** - Bogo Sort @@ -76,7 +81,7 @@ All prequisites for the algorithms are listed in the source code for each algori Tests: ------ -Nose is used as the main test runner and all Unit Tests can be run by: +Nose is used as the main test runner and all Unit Tests can be run by: :: From a6ece721a1577a1c3b9112df1a1b66e4ce5da548 Mon Sep 17 00:00:00 2001 From: JoaoGFarias Date: Sun, 5 Apr 2015 00:34:05 -0300 Subject: [PATCH 29/89] Updating TODO list Removing Stack and Queue --- TODO.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO.rst b/TODO.rst index 8b70612..99f7fd4 100644 --- a/TODO.rst +++ b/TODO.rst @@ -18,8 +18,6 @@ Below is an ever changing list of things that I would like to accomplish or impl - Strassen's Matrix Multiplication - *k*-Selection (Minimum, Maximum, Median, Arbitrary *k*) - Data Structures - - Stacks - - Queues - Linked Lists - Hash Tables - Binary Search Trees From 627e8c4f20cef22061f0c3ad7ce6895380d8cdad Mon Sep 17 00:00:00 2001 From: JoaoGFarias Date: Thu, 9 Apr 2015 00:37:00 -0300 Subject: [PATCH 30/89] Updating TODO list Removing Gnome Sort, done by @sawhney --- TODO.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO.rst b/TODO.rst index 8b70612..9f7066b 100644 --- a/TODO.rst +++ b/TODO.rst @@ -12,7 +12,6 @@ Below is an ever changing list of things that I would like to accomplish or impl - Cycle Sort - Smoothsort - Strand Sort - - Gnome Sort - Divide and Conquer - Maximum Subarray - Strassen's Matrix Multiplication From 3f3e9b09656d537941ac01369d4b3a96c40c1be3 Mon Sep 17 00:00:00 2001 From: Truong Le Date: Mon, 27 Apr 2015 14:10:51 -0400 Subject: [PATCH 31/89] initialize for union find --- algorithms/data_structure/union_find.py | 58 +++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 algorithms/data_structure/union_find.py diff --git a/algorithms/data_structure/union_find.py b/algorithms/data_structure/union_find.py new file mode 100644 index 0000000..d665b0b --- /dev/null +++ b/algorithms/data_structure/union_find.py @@ -0,0 +1,58 @@ +""" + union_find.py + A Naive Implementation of union find data structure. + Union Find Overview: + ------------------------ + A disjoint-set data structure, also called union-find data structure implements two functions: + union(A, B) - merge A's set with B's set + find(A) - finds what set A belongs to + Navie approach: + Find follows parent nodes until it reaches the root. + Union combines two trees into one by attaching the root of one to the root of the other + Time Complexity : O(N) (a highly unbalanced tree might be created) + Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure +""" +class UnionFind: + def __init__(self, N): + if type(N) != int: + raise TypeError, "size must be integer" + if N < 0: + raise ValueError, "N is not a negative integer" + self.forests = [] + self.N = N + for i in range(0, N): + self.forests.append(i) + + def make_set(self, x): + if type(x) != int: + raise TypeError, "x must be integer" + if x != self.N: + raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.N) + self.forests[x] = x + + def union(self, x, y): + self.__validate_ele(x) + self.__validate_ele(y) + x_root = find(x) + y_root = find(y) + self.forests[x_root] = y_root + + def find(self, x): + self.__validate_ele(x) + if self.forest[x] == x: + return x + else: + return find(x) + + def is_connected(self, x, y): + self.__validate_ele(x) + self.__validate_ele(y) + if find(x) == find(y): + return True + return False + + def __validate_ele(self, x): + if type(x) != int: + raise TypeError, "{0} is not an integer".format(x) + if x < 0 or x >= self.N: + raise ValueError, "{0} is not in [0,{1})".format(x, self.N) From e509c4fdea09141cf3c670b379ffd6e81e32b844 Mon Sep 17 00:00:00 2001 From: Truong Le Date: Mon, 27 Apr 2015 14:38:18 -0400 Subject: [PATCH 32/89] fix bug for union find and add unit test --- algorithms/data_structure/union_find.py | 18 ++++++++++-------- algorithms/tests/test_data_structure.py | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/algorithms/data_structure/union_find.py b/algorithms/data_structure/union_find.py index d665b0b..63178b4 100644 --- a/algorithms/data_structure/union_find.py +++ b/algorithms/data_structure/union_find.py @@ -9,7 +9,7 @@ Navie approach: Find follows parent nodes until it reaches the root. Union combines two trees into one by attaching the root of one to the root of the other - Time Complexity : O(N) (a highly unbalanced tree might be created) + Time Complexity : O(N) (a highly unbalanced tree might be created, nothing better a linked-list) Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure """ class UnionFind: @@ -17,37 +17,39 @@ def __init__(self, N): if type(N) != int: raise TypeError, "size must be integer" if N < 0: - raise ValueError, "N is not a negative integer" + raise ValueError, "N cannot be a negative integer" self.forests = [] self.N = N for i in range(0, N): self.forests.append(i) + print self.forests def make_set(self, x): if type(x) != int: raise TypeError, "x must be integer" if x != self.N: raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.N) - self.forests[x] = x + self.forests.append(x) + self.N = self.N + 1 def union(self, x, y): self.__validate_ele(x) self.__validate_ele(y) - x_root = find(x) - y_root = find(y) + x_root = self.find(x) + y_root = self.find(y) self.forests[x_root] = y_root def find(self, x): self.__validate_ele(x) - if self.forest[x] == x: + if self.forests[x] == x: return x else: - return find(x) + return self.find(self.forests[x]) def is_connected(self, x, y): self.__validate_ele(x) self.__validate_ele(y) - if find(x) == find(y): + if self.find(x) == self.find(y): return True return False diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index f43d882..e5a4884 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -1,5 +1,5 @@ import unittest -from ..data_structure import stack,queue +from ..data_structure import stack,queue,union_find class TestStack(unittest.TestCase): """ @@ -35,3 +35,18 @@ def test_queue(self): self.assertEqual(self.que.remove(),5) self.assertEqual(self.que.remove(),6) self.assertEqual(self.que.is_empty(),True) + +class TestUnionFind(unittest.TestCase): + """ + Test Union Find Implementation + """ + def test_union_find(self): + self.uf = union_find.UnionFind(4) + self.uf.make_set(4) + self.uf.union(1, 0) + self.uf.union(3, 4) + + self.assertEqual(self.uf.find(1), 0) + self.assertEqual(self.uf.find(3), 4) + self.assertEqual(self.uf.is_connected(0, 1), True) + self.assertEqual(self.uf.is_connected(3, 4), True) From 6f065c61bede5a0574d1bed92dcaacf671397faa Mon Sep 17 00:00:00 2001 From: Truong Le Date: Mon, 27 Apr 2015 17:46:43 -0400 Subject: [PATCH 33/89] improve code for find function --- algorithms/data_structure/union_find.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/algorithms/data_structure/union_find.py b/algorithms/data_structure/union_find.py index 63178b4..38d49e5 100644 --- a/algorithms/data_structure/union_find.py +++ b/algorithms/data_structure/union_find.py @@ -49,9 +49,7 @@ def find(self, x): def is_connected(self, x, y): self.__validate_ele(x) self.__validate_ele(y) - if self.find(x) == self.find(y): - return True - return False + return self.find(x) == self.find(y) def __validate_ele(self, x): if type(x) != int: From 6b62d707a019ff65b458ef6656f39d06f82f7b1b Mon Sep 17 00:00:00 2001 From: Truong Le Date: Mon, 27 Apr 2015 21:32:39 -0400 Subject: [PATCH 34/89] clean print function in constructor --- algorithms/data_structure/union_find.py | 1 - 1 file changed, 1 deletion(-) diff --git a/algorithms/data_structure/union_find.py b/algorithms/data_structure/union_find.py index 38d49e5..89025b8 100644 --- a/algorithms/data_structure/union_find.py +++ b/algorithms/data_structure/union_find.py @@ -22,7 +22,6 @@ def __init__(self, N): self.N = N for i in range(0, N): self.forests.append(i) - print self.forests def make_set(self, x): if type(x) != int: From df614e25bb8737f6e31665bb97ec7885abb20b05 Mon Sep 17 00:00:00 2001 From: Truong Le Date: Mon, 27 Apr 2015 22:06:34 -0400 Subject: [PATCH 35/89] union find by rank --- .../data_structure/union_find_by_rank.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 algorithms/data_structure/union_find_by_rank.py diff --git a/algorithms/data_structure/union_find_by_rank.py b/algorithms/data_structure/union_find_by_rank.py new file mode 100644 index 0000000..1de81b0 --- /dev/null +++ b/algorithms/data_structure/union_find_by_rank.py @@ -0,0 +1,69 @@ +""" + union_find.py + An implementation of union find by rank data structure. + Union Find Overview: + ------------------------ + A disjoint-set data structure, also called union-find data structure implements two functions: + union(A, B) - merge A's set with B's set + find(A) - finds what set A belongs to + Union by rank approach: + attach the smaller tree to the root of the larger tree + Time Complexity : O(logn) + Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure +""" +class UnionFindByRank: + def __init__(self, N): + if type(N) != int: + raise TypeError, "size must be integer" + if N < 0: + raise ValueError, "N cannot be a negative integer" + self.parent = [] + self.rank = [] + self.N = N + for i in range(0, N): + self.parent.append(i) + self.rank.append(0) + + def make_set(self, x): + if type(x) != int: + raise TypeError, "x must be integer" + if x != self.N: + raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.N) + self.parent.append(x) + self.rank.append(0) + self.N = self.N + 1 + + def union(self, x, y): + self.__validate_ele(x) + self.__validate_ele(y) + x_root = self.find(x) + y_root = self.find(y) + if x_root == y_root: + return + # x and y are not already in same set. Merge them + if self.rank[x_root] < self.rank[y_root]: + self.parent[x_root] = y_root + elif self.rank[x_root] > self.rank[y_root]: + self.parent[y_root] = x_root + else: + self.parent[y_root] = x_root + self.rank[x_root] = self.rank[x_root] + 1 + + def find(self, x): + self.__validate_ele(x) + if self.parent[x] == x: + return x + else: + return self.find(self.parent[x]) + + def is_connected(self, x, y): + self.__validate_ele(x) + self.__validate_ele(y) + return self.find(x) == self.find(y) + + def __validate_ele(self, x): + if type(x) != int: + raise TypeError, "{0} is not an integer".format(x) + if x < 0 or x >= self.N: + raise ValueError, "{0} is not in [0,{1})".format(x, self.N) + From 55ece640b212976741385a1917b69c31d15a46ed Mon Sep 17 00:00:00 2001 From: Truong Le Date: Mon, 27 Apr 2015 22:22:15 -0400 Subject: [PATCH 36/89] union find with path compression approach --- .../union_find_with_path_compression.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 algorithms/data_structure/union_find_with_path_compression.py diff --git a/algorithms/data_structure/union_find_with_path_compression.py b/algorithms/data_structure/union_find_with_path_compression.py new file mode 100644 index 0000000..b793fac --- /dev/null +++ b/algorithms/data_structure/union_find_with_path_compression.py @@ -0,0 +1,75 @@ +""" + union_find_with_path_compression.py + An implementation of union find with path compression data structure. + Union Find Overview: + ------------------------ + A disjoint-set data structure, also called union-find data structure implements two functions: + union(A, B) - merge A's set with B's set + find(A) - finds what set A belongs to + Union with path compression approach: + Each node visited on the way to a root node may as well be attached directly to the root node. + attach the smaller tree to the root of the larger tree + Time Complexity : O(a(n)), where a(n) is the inverse of the function n=f(x)=A(x,x) and A is the extremely fast-growing Ackermann function. + Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure +""" +class UnionFindWithPathCompression: + def __init__(self, N): + if type(N) != int: + raise TypeError, "size must be integer" + if N < 0: + raise ValueError, "N cannot be a negative integer" + self.parent = [] + self.rank = [] + self.N = N + for i in range(0, N): + self.parent.append(i) + self.rank.append(0) + + def make_set(self, x): + if type(x) != int: + raise TypeError, "x must be integer" + if x != self.N: + raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.N) + self.parent.append(x) + self.rank.append(0) + self.N = self.N + 1 + + def union(self, x, y): + self.__validate_ele(x) + self.__validate_ele(y) + x_root = self.find(x) + y_root = self.find(y) + if x_root == y_root: + return + # x and y are not already in same set. Merge them + if self.rank[x_root] < self.rank[y_root]: + self.parent[x_root] = y_root + elif self.rank[x_root] > self.rank[y_root]: + self.parent[y_root] = x_root + else: + self.parent[y_root] = x_root + self.rank[x_root] = self.rank[x_root] + 1 + + def __find(self, x): + if self.parent[x] != x: + self.parent[x] = self.__find(self.parent[x]) + return self.parent[x] + + def find(self, x): + self.__validate_ele(x) + if self.parent[x] == x: + return x + else: + return self.find(self.parent[x]) + + def is_connected(self, x, y): + self.__validate_ele(x) + self.__validate_ele(y) + return self.find(x) == self.find(y) + + def __validate_ele(self, x): + if type(x) != int: + raise TypeError, "{0} is not an integer".format(x) + if x < 0 or x >= self.N: + raise ValueError, "{0} is not in [0,{1})".format(x, self.N) + From 21c185548bc2232e53ea7188ed0cea32bac9562d Mon Sep 17 00:00:00 2001 From: Truong Le Date: Mon, 27 Apr 2015 22:52:36 -0400 Subject: [PATCH 37/89] change union find class to make it follow oop encapsulation property --- algorithms/data_structure/union_find.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/algorithms/data_structure/union_find.py b/algorithms/data_structure/union_find.py index 89025b8..81be744 100644 --- a/algorithms/data_structure/union_find.py +++ b/algorithms/data_structure/union_find.py @@ -18,32 +18,32 @@ def __init__(self, N): raise TypeError, "size must be integer" if N < 0: raise ValueError, "N cannot be a negative integer" - self.forests = [] - self.N = N + self.__parent = [] + self.__N = N for i in range(0, N): - self.forests.append(i) + self.__parent.append(i) def make_set(self, x): if type(x) != int: raise TypeError, "x must be integer" - if x != self.N: - raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.N) - self.forests.append(x) - self.N = self.N + 1 + if x != self.__N: + raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.__N) + self.__parent.append(x) + self.__N = self.__N + 1 def union(self, x, y): self.__validate_ele(x) self.__validate_ele(y) x_root = self.find(x) y_root = self.find(y) - self.forests[x_root] = y_root + self.__parent[x_root] = y_root def find(self, x): self.__validate_ele(x) - if self.forests[x] == x: + if self.__parent[x] == x: return x else: - return self.find(self.forests[x]) + return self.find(self.__parent[x]) def is_connected(self, x, y): self.__validate_ele(x) @@ -53,5 +53,5 @@ def is_connected(self, x, y): def __validate_ele(self, x): if type(x) != int: raise TypeError, "{0} is not an integer".format(x) - if x < 0 or x >= self.N: - raise ValueError, "{0} is not in [0,{1})".format(x, self.N) + if x < 0 or x >= self.__N: + raise ValueError, "{0} is not in [0,{1})".format(x, self.__N) From 6c37ff47969c305e499d48473e9b2b7f45a22bdd Mon Sep 17 00:00:00 2001 From: Truong Le Date: Mon, 27 Apr 2015 22:57:23 -0400 Subject: [PATCH 38/89] add union find by rank && union with path compression along with unit test --- .../data_structure/union_find_by_rank.py | 42 +++++++-------- .../union_find_with_path_compression.py | 54 ++++++++++--------- algorithms/tests/test_data_structure.py | 53 +++++++++++++++++- 3 files changed, 102 insertions(+), 47 deletions(-) diff --git a/algorithms/data_structure/union_find_by_rank.py b/algorithms/data_structure/union_find_by_rank.py index 1de81b0..d0d7449 100644 --- a/algorithms/data_structure/union_find_by_rank.py +++ b/algorithms/data_structure/union_find_by_rank.py @@ -1,5 +1,5 @@ """ - union_find.py + union_find_by_rank.py An implementation of union find by rank data structure. Union Find Overview: ------------------------ @@ -17,21 +17,21 @@ def __init__(self, N): raise TypeError, "size must be integer" if N < 0: raise ValueError, "N cannot be a negative integer" - self.parent = [] - self.rank = [] - self.N = N + self.__parent = [] + self.__rank = [] + self.__N = N for i in range(0, N): - self.parent.append(i) - self.rank.append(0) + self.__parent.append(i) + self.__rank.append(0) def make_set(self, x): if type(x) != int: raise TypeError, "x must be integer" - if x != self.N: - raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.N) - self.parent.append(x) - self.rank.append(0) - self.N = self.N + 1 + if x != self.__N: + raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.__N) + self.__parent.append(x) + self.__rank.append(0) + self.__N = self.__N + 1 def union(self, x, y): self.__validate_ele(x) @@ -41,20 +41,20 @@ def union(self, x, y): if x_root == y_root: return # x and y are not already in same set. Merge them - if self.rank[x_root] < self.rank[y_root]: - self.parent[x_root] = y_root - elif self.rank[x_root] > self.rank[y_root]: - self.parent[y_root] = x_root + if self.__rank[x_root] < self.__rank[y_root]: + self.__parent[x_root] = y_root + elif self.__rank[x_root] > self.__rank[y_root]: + self.__parent[y_root] = x_root else: - self.parent[y_root] = x_root - self.rank[x_root] = self.rank[x_root] + 1 + self.__parent[y_root] = x_root + self.__rank[x_root] = self.__rank[x_root] + 1 def find(self, x): self.__validate_ele(x) - if self.parent[x] == x: + if self.__parent[x] == x: return x else: - return self.find(self.parent[x]) + return self.find(self.__parent[x]) def is_connected(self, x, y): self.__validate_ele(x) @@ -64,6 +64,6 @@ def is_connected(self, x, y): def __validate_ele(self, x): if type(x) != int: raise TypeError, "{0} is not an integer".format(x) - if x < 0 or x >= self.N: - raise ValueError, "{0} is not in [0,{1})".format(x, self.N) + if x < 0 or x >= self.__N: + raise ValueError, "{0} is not in [0,{1})".format(x, self.__N) diff --git a/algorithms/data_structure/union_find_with_path_compression.py b/algorithms/data_structure/union_find_with_path_compression.py index b793fac..d9e192d 100644 --- a/algorithms/data_structure/union_find_with_path_compression.py +++ b/algorithms/data_structure/union_find_with_path_compression.py @@ -18,58 +18,62 @@ def __init__(self, N): raise TypeError, "size must be integer" if N < 0: raise ValueError, "N cannot be a negative integer" - self.parent = [] - self.rank = [] - self.N = N + self.__parent = [] + self.__rank = [] + self.__N = N for i in range(0, N): - self.parent.append(i) - self.rank.append(0) + self.__parent.append(i) + self.__rank.append(0) def make_set(self, x): if type(x) != int: raise TypeError, "x must be integer" - if x != self.N: - raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.N) - self.parent.append(x) - self.rank.append(0) - self.N = self.N + 1 + if x != self.__N: + raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.__N) + self.__parent.append(x) + self.__rank.append(0) + self.__N = self.__N + 1 def union(self, x, y): self.__validate_ele(x) self.__validate_ele(y) - x_root = self.find(x) - y_root = self.find(y) + x_root = self.__find(x) + y_root = self.__find(y) if x_root == y_root: return # x and y are not already in same set. Merge them - if self.rank[x_root] < self.rank[y_root]: - self.parent[x_root] = y_root - elif self.rank[x_root] > self.rank[y_root]: - self.parent[y_root] = x_root + if self.__rank[x_root] < self.__rank[y_root]: + self.__parent[x_root] = y_root + elif self.__rank[x_root] > self.__rank[y_root]: + self.__parent[y_root] = x_root else: - self.parent[y_root] = x_root - self.rank[x_root] = self.rank[x_root] + 1 + self.__parent[y_root] = x_root + self.__rank[x_root] = self.__rank[x_root] + 1 def __find(self, x): - if self.parent[x] != x: - self.parent[x] = self.__find(self.parent[x]) - return self.parent[x] + if self.__parent[x] != x: + self.__parent[x] = self.__find(self.__parent[x]) + return self.__parent[x] def find(self, x): self.__validate_ele(x) - if self.parent[x] == x: + if self.__parent[x] == x: return x else: - return self.find(self.parent[x]) + return self.find(self.__parent[x]) def is_connected(self, x, y): self.__validate_ele(x) self.__validate_ele(y) return self.find(x) == self.find(y) + # use for unit testing check if the path is compressed + def parent(self, x): + return self.__parent[x] + def __validate_ele(self, x): if type(x) != int: raise TypeError, "{0} is not an integer".format(x) - if x < 0 or x >= self.N: - raise ValueError, "{0} is not in [0,{1})".format(x, self.N) + if x < 0 or x >= self.__N: + raise ValueError, "{0} is not in [0,{1})".format(x, self.__N) diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index e5a4884..0fea406 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -1,5 +1,5 @@ import unittest -from ..data_structure import stack,queue,union_find +from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression class TestStack(unittest.TestCase): """ @@ -50,3 +50,54 @@ def test_union_find(self): self.assertEqual(self.uf.find(3), 4) self.assertEqual(self.uf.is_connected(0, 1), True) self.assertEqual(self.uf.is_connected(3, 4), True) + +class TestUnionFindByRank(unittest.TestCase): + """ + Test Union Find Implementation + """ + def test_union_find_by_rank(self): + self.uf = union_find_by_rank.UnionFindByRank(6) + self.uf.make_set(6) + self.uf.union(1, 0) + self.uf.union(3, 4) + self.uf.union(2, 4) + self.uf.union(5, 2) + self.uf.union(6, 5) + + self.assertEqual(self.uf.find(1), 1) + self.assertEqual(self.uf.find(3), 3) + # test tree is created by rank + self.uf.union(5, 0) + self.assertEqual(self.uf.find(2), 3) + self.assertEqual(self.uf.find(5), 3) + self.assertEqual(self.uf.find(6), 3) + self.assertEqual(self.uf.find(0), 3) + + self.assertEqual(self.uf.is_connected(0, 1), True) + self.assertEqual(self.uf.is_connected(3, 4), True) + self.assertEqual(self.uf.is_connected(5, 3), True) + +class TestUnionFindWithPathCompression(unittest.TestCase): + """ + Test Union Find Implementation + """ + def test_union_find_with_path_compression(self): + self.uf = union_find_with_path_compression.UnionFindWithPathCompression(5) + self.uf.make_set(5) + self.uf.union(0, 1) + self.uf.union(2, 3) + self.uf.union(1, 3) + self.uf.union(4, 5) + self.assertEqual(self.uf.find(1), 0) + self.assertEqual(self.uf.find(3), 0) + self.assertEqual(self.uf.parent(3), 2) + self.assertEqual(self.uf.parent(5), 4) + self.assertEqual(self.uf.is_connected(3, 5), False) + self.assertEqual(self.uf.is_connected(4, 5), True) + self.assertEqual(self.uf.is_connected(2, 3), True) + # test tree is created by path compression + self.uf.union(5, 3) + self.assertEqual(self.uf.parent(3), 0) + + self.assertEqual(self.uf.is_connected(3, 5), True) + From 95cc2b69d7815896118f30d76e43fcb06ea360ff Mon Sep 17 00:00:00 2001 From: JoaoGFarias Date: Mon, 25 May 2015 00:50:17 -0300 Subject: [PATCH 39/89] Updating Authors and README --- AUTHORS.rst | 3 +++ README.rst | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 4bf9bf9..9784045 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,10 +2,13 @@ Development Lead: ----------------- - `Nic Young `_ +- `JoaoGFarias `_ List of contributors: -------------------- +- `dotslash `_ +- `travistrle `_ - `jxtcman `_ - `derv82 `_ - `ppinette `_ diff --git a/README.rst b/README.rst index 7f3e0ce..f65c7a2 100644 --- a/README.rst +++ b/README.rst @@ -14,6 +14,7 @@ Algorithms implemented so far: - Queue - Stack +- Disjoint Set **Sorting:** @@ -47,7 +48,7 @@ Algorithms implemented so far: - Extended GCD - Standard Normal Probability Density Function - Cumulative Density Function (Approximation; 16 digit precision for 300 iter.) - +- Sieve of Eratosthenes **Random:** - Mersenne Twister From 8c03ab6fa9b65c548f1a8af95cb644a73b60aa82 Mon Sep 17 00:00:00 2001 From: Joseph Lane Date: Tue, 9 Jun 2015 20:03:31 -0500 Subject: [PATCH 40/89] Added Singly Linked List Data Structure and the corresponding Unit Test --- .../data_structure/singly_linked_list.py | 74 +++++++++++++++++++ algorithms/tests/test_data_structure.py | 23 +++++- 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 algorithms/data_structure/singly_linked_list.py diff --git a/algorithms/data_structure/singly_linked_list.py b/algorithms/data_structure/singly_linked_list.py new file mode 100644 index 0000000..87cb40d --- /dev/null +++ b/algorithms/data_structure/singly_linked_list.py @@ -0,0 +1,74 @@ +""" + Singly Linked List data structure implemented: + -------------------------------- + add : add element to list + remove : remove element from list + search : search for value in list + size : return size of list + + Time Complexity: O(N) +""" +class Node: + + def __init__(self, data=None, next=None): + self.data = data + self.next = next + + def setData(self, data): + self.data = data + + def getData(self): + return self.data + + def setNext(self, next): + self.next = next + + def getNext(self): + return self.next + +class SinglyLinkedList: + + def __init__(self): + self.head = None + self.size = 0 + + def add(self, value): + node = Node(value) + node.setNext(self.head) + self.head = node + self.size += 1 + + def remove(self, value): + current = self.head + previous = None + found = False + + while not found: + if current.data == value: + found = True + self.size-=1 + else: + previous = current + current = current.next + + if previous == None: # Head node + self.head = current.next + else: # None head node + previous.setNext(current.next) + + return found + + def search(self, value): + current = self.head + found = False + + while current and not found: + if current.getData() == value: + found = True + else: + current = current.next + + return found + + def size(self): + return self.size \ No newline at end of file diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index 0fea406..bd3c07f 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -1,5 +1,5 @@ import unittest -from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression +from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression,singly_linked_list class TestStack(unittest.TestCase): """ @@ -101,3 +101,24 @@ def test_union_find_with_path_compression(self): self.assertEqual(self.uf.is_connected(3, 5), True) +class TestSinglyLinkedList(unittest.TestCase): + """ + Test Singly Linked List Implementation + """ + + def test_singly_linked_list(self): + self.sl = singly_linked_list.SinglyLinkedList() + self.sl.add(10) + self.sl.add(5) + self.sl.add(30) + self.sl.remove(30) + + self.assertEqual(self.sl.size, 2) + self.assertEqual(self.sl.search(30), False) + self.assertEqual(self.sl.search(5),True) + self.assertEqual(self.sl.search(10), True) + self.assertEqual(self.sl.remove(5), True) + self.assertEqual(self.sl.remove(10), True) + self.assertEqual(self.sl.size, 0) + + From fc8b45638b420c2f111eeb55f90f244cb48b8721 Mon Sep 17 00:00:00 2001 From: JulianGriggs Date: Fri, 7 Aug 2015 21:22:42 -0400 Subject: [PATCH 41/89] Adding Undirected Graph --- algorithms/data_structure/undirected_graph.py | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 algorithms/data_structure/undirected_graph.py diff --git a/algorithms/data_structure/undirected_graph.py b/algorithms/data_structure/undirected_graph.py new file mode 100644 index 0000000..b0e956b --- /dev/null +++ b/algorithms/data_structure/undirected_graph.py @@ -0,0 +1,92 @@ +""" + Unidrected Graph data structure implemented: + -------------------------------------------- + The Undirected_Graph class represents an undirected graph of vertices + which can be any hashable value. + + It supports the following two primary operations: + add_edge: add an edge to the graph O(1) + adj: return list of all of the vertices adjacent to a vertex O(1) + vertices: return list of all vertices in the graph O(N) + + It also supports the followign secondary operations: + vertex_count: return the number of vertices O(1) + edge_count: return the number of edges O(1) + degree: return degree of the vertex O(1) + + Parallel edges and self-loops are permitted. + + Adapted from: http://algs4.cs.princeton.edu/41undirected/Graph.java.html + """ + +class Undirected_Graph : + def __init__(self): + self.__adj = {} + self.__v_count = 0 + self.__e_count = 0 + + def vertex_count(self): + """ + Returns the number of vertices in the graph. + """ + + return self.__v_count + + def edge_count(self): + """ + Returns the number of edges in the graph. + """ + + return self.__e_count + + def add_edge(self, src, dest): + """ + Adds an undirected edge 'src'-'dest' to the graph. + """ + if src in self.__adj: + self.__adj[src].append(dest) + else: + self.__adj[src] = [dest] + self.__v_count += 1 + + + if dest in self.__adj: + self.__adj[dest].append(src) + else: + self.__adj[dest] = [src] + self.__v_count += 1 + + self.__e_count += 1 + + def adj(self, src): + """ + Returns the vertices adjacent to vertex 'src'. + """ + return self.__adj[src] + + def degree(self, src): + """ + Returns the degree of the vertex 'src' + """ + if src in self.__adj: + return len(self.__adj[src]) + else: + raise LookupError("This vertex is not in the graph.") + + def vertices(self): + """ + Returns an iterable of all the vertices in the graph. + """ + return self.__adj.keys() + + def __str__(self): + s = [] + s.append("{0} vertices and {1} edges \n".format(self.__vertex_count, + self.__edge_count)) + for key in self.vertices(): + s.append("{0}: ".format(key)) + for val in self.adj(key): + s.append("{0} ".format(val)) + s.append("\n") + + return "".join(s) \ No newline at end of file From 0412d5d7bba169f2507a5b7e7ba3392ba37d8285 Mon Sep 17 00:00:00 2001 From: JulianGriggs Date: Fri, 7 Aug 2015 21:23:52 -0400 Subject: [PATCH 42/89] Adding tests for Undirected Graph --- algorithms/tests/test_data_structure.py | 81 ++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index 0fea406..bf52075 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -1,5 +1,5 @@ import unittest -from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression +from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression, undirected_graph class TestStack(unittest.TestCase): """ @@ -101,3 +101,82 @@ def test_union_find_with_path_compression(self): self.assertEqual(self.uf.is_connected(3, 5), True) +class TestUndirectedGraph(unittest.TestCase): + """ + Test Undirected Graph Implementation + """ + def test_undirected_graph(self): + + # init + self.ug0 = undirected_graph.Undirected_Graph() + self.ug1 = undirected_graph.Undirected_Graph() + self.ug2 = undirected_graph.Undirected_Graph() + self.ug3 = undirected_graph.Undirected_Graph() + + # populating + self.ug1.add_edge(1, 2) + + self.ug2.add_edge(1,2) + self.ug2.add_edge(1,2) + + self.ug3.add_edge(1,2) + self.ug3.add_edge(1,2) + self.ug3.add_edge(3,1) + + # test adj + self.assertTrue(2 in self.ug1.adj(1)) + self.assertEqual(len(self.ug1.adj(1)), 1) + self.assertTrue(1 in self.ug1.adj(2)) + self.assertEqual(len(self.ug1.adj(1)), 1) + + self.assertTrue(2 in self.ug2.adj(1)) + self.assertEqual(len(self.ug2.adj(1)), 2) + self.assertTrue(1 in self.ug2.adj(2)) + self.assertEqual(len(self.ug2.adj(1)), 2) + + self.assertTrue(2 in self.ug3.adj(1)) + self.assertTrue(3 in self.ug3.adj(1)) + self.assertEqual(len(self.ug3.adj(1)), 3) + self.assertTrue(1 in self.ug3.adj(2)) + self.assertEqual(len(self.ug3.adj(2)), 2) + self.assertTrue(1 in self.ug3.adj(3)) + self.assertEqual(len(self.ug3.adj(3)), 1) + + # test degree + self.assertEqual(self.ug1.degree(1), 1) + self.assertEqual(self.ug1.degree(2), 1) + self.assertEqual(self.ug2.degree(1), 2) + self.assertEqual(self.ug2.degree(2), 2) + self.assertEqual(self.ug3.degree(1), 3) + self.assertEqual(self.ug3.degree(2), 2) + self.assertEqual(self.ug3.degree(3), 1) + + # test vertices + self.assertEqual(self.ug0.vertices(), []) + self.assertEqual(len(self.ug0.vertices()), 0) + + self.assertTrue(1 in self.ug1.vertices()) + self.assertTrue(2 in self.ug1.vertices()) + self.assertEqual(len(self.ug1.vertices()), 2) + + self.assertTrue(1 in self.ug2.vertices()) + self.assertTrue(2 in self.ug2.vertices()) + self.assertEqual(len(self.ug2.vertices()), 2) + + self.assertTrue(1 in self.ug3.vertices()) + self.assertTrue(2 in self.ug3.vertices()) + self.assertTrue(3 in self.ug3.vertices()) + self.assertEqual(len(self.ug3.vertices()), 3) + + # test vertex_count + self.assertEqual(self.ug0.vertex_count(), 0) + self.assertEqual(self.ug1.vertex_count(), 2) + self.assertEqual(self.ug2.vertex_count(), 2) + self.assertEqual(self.ug3.vertex_count(), 3) + + # test edge_count + self.assertEqual(self.ug0.edge_count(), 0) + self.assertEqual(self.ug1.edge_count(), 1) + self.assertEqual(self.ug2.edge_count(), 2) + self.assertEqual(self.ug3.edge_count(), 3) + From e3325c88c4e4ce313e6074a0575db761a0809fcd Mon Sep 17 00:00:00 2001 From: JulianGriggs Date: Fri, 7 Aug 2015 22:00:16 -0400 Subject: [PATCH 43/89] Adding Digraph data structure --- algorithms/data_structure/digraph.py | 106 +++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 algorithms/data_structure/digraph.py diff --git a/algorithms/data_structure/digraph.py b/algorithms/data_structure/digraph.py new file mode 100644 index 0000000..26eafac --- /dev/null +++ b/algorithms/data_structure/digraph.py @@ -0,0 +1,106 @@ +""" + Directed Graph data structure implemented: + -------------------------------------------- + The Directed_Graph class represents an undirected graph of vertices + which can be any hashable value. + + It supports the following two primary operations: + add_edge: add an edge to the graph O(1) + adj: return list of all of the vertices adjacent to a vertex O(1) + vertices: return list of all vertices in the graph O(V) + + It also supports the followign secondary operations: + vertex_count: return the number of vertices O(1) + edge_count: return the number of edges O(1) + degree: return degree of the vertex O(1) + reverse: return a reversed version of the digraph O (V*E) + + Parallel edges and self-loops are permitted. + + Adapted from: http://algs4.cs.princeton.edu/41undirected/Graph.java.html + """ + +class Digraph : + def __init__(self): + self.__adj = {} + self.__v_count = 0 + self.__e_count = 0 + + def vertex_count(self): + """ + Returns the number of vertices in the graph. + """ + + return self.__v_count + + def edge_count(self): + """ + Returns the number of edges in the graph. + """ + + return self.__e_count + + def add_edge(self, src, dest): + """ + Adds an undirected edge 'src'-'dest' to the graph. + """ + + if src in self.__adj: + self.__adj[src].append(dest) + else: + self.__adj[src] = [dest] + self.__v_count += 1 + + if dest in self.__adj: + pass + else: + self.__adj[dest] = [] + self.__v_count += 1 + + self.__e_count += 1 + + def adj(self, src): + """ + Returns the vertices adjacent to vertex 'src'. + """ + return self.__adj[src] + + def outdegree(self, src): + """ + Returns the degree of the vertex 'src' + """ + if src in self.__adj: + return len(self.__adj[src]) + else: + raise LookupError("This vertex is not in the graph.") + + def vertices(self): + """ + Returns an iterable of all the vertices in the graph. + """ + return self.__adj.keys() + + def reverse(self): + """ + Returns the reverse of this digraph + """ + digraph_reversed = Digraph() + old_vertices = self.vertices() + + for src in old_vertices: + for dest in old_vertices.adj(src): + digraph_reversed.add_edge(dest, src) + return digraph_reversed; + + + def __str__(self): + s = [] + s.append("{0} vertices and {1} edges \n".format(self.__v_count, + self.__e_count)) + for key in self.vertices(): + s.append("{0}: ".format(key)) + for val in self.adj(key): + s.append("{0} ".format(val)) + s.append("\n") + + return "".join(s) \ No newline at end of file From 2ae4c0a14ccb98e87f09ca5aa99fe6fd835847bb Mon Sep 17 00:00:00 2001 From: JulianGriggs Date: Fri, 7 Aug 2015 22:00:39 -0400 Subject: [PATCH 44/89] Adding tests for Digraph --- algorithms/tests/test_data_structure.py | 85 ++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index 0fea406..beee0d7 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -1,5 +1,5 @@ import unittest -from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression +from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression, digraph class TestStack(unittest.TestCase): """ @@ -101,3 +101,86 @@ def test_union_find_with_path_compression(self): self.assertEqual(self.uf.is_connected(3, 5), True) + +class TestDirectedGraph(unittest.TestCase): + """ + Test Undirected Graph Implementation + """ + def test_directed_graph(self): + + # init + self.dg0 = digraph.Digraph() + self.dg1 = digraph.Digraph() + self.dg2 = digraph.Digraph() + self.dg3 = digraph.Digraph() + + # populating + self.dg1.add_edge(1, 2) + + self.dg2.add_edge(1,2) + self.dg2.add_edge(1,2) + + self.dg3.add_edge(1,2) + self.dg3.add_edge(1,2) + self.dg3.add_edge(3,1) + + # test adj + self.assertTrue(2 in self.dg1.adj(1)) + self.assertEqual(len(self.dg1.adj(1)), 1) + self.assertTrue(1 not in self.dg1.adj(2)) + self.assertEqual(len(self.dg1.adj(2)), 0) + + self.assertTrue(2 in self.dg2.adj(1)) + self.assertEqual(len(self.dg2.adj(1)), 2) + self.assertTrue(1 not in self.dg2.adj(2)) + self.assertEqual(len(self.dg2.adj(2)), 0) + + self.assertTrue(2 in self.dg3.adj(1)) + self.assertTrue(1 in self.dg3.adj(3)) + self.assertEqual(len(self.dg3.adj(1)), 2) + self.assertTrue(1 not in self.dg3.adj(2)) + self.assertEqual(len(self.dg3.adj(2)), 0) + self.assertTrue(3 not in self.dg3.adj(1)) + self.assertEqual(len(self.dg3.adj(3)), 1) + + # test degree + self.assertEqual(self.dg1.outdegree(1), 1) + self.assertEqual(self.dg1.outdegree(2), 0) + self.assertEqual(self.dg2.outdegree(1), 2) + self.assertEqual(self.dg2.outdegree(2), 0) + self.assertEqual(self.dg3.outdegree(1), 2) + self.assertEqual(self.dg3.outdegree(2), 0) + self.assertEqual(self.dg3.outdegree(3), 1) + + # test vertices + self.assertEqual(self.dg0.vertices(), []) + self.assertEqual(len(self.dg0.vertices()), 0) + + self.assertTrue(1 in self.dg1.vertices()) + self.assertTrue(2 in self.dg1.vertices()) + self.assertEqual(len(self.dg1.vertices()), 2) + + self.assertTrue(1 in self.dg2.vertices()) + self.assertTrue(2 in self.dg2.vertices()) + self.assertEqual(len(self.dg2.vertices()), 2) + + self.assertTrue(1 in self.dg3.vertices()) + self.assertTrue(2 in self.dg3.vertices()) + self.assertTrue(3 in self.dg3.vertices()) + self.assertEqual(len(self.dg3.vertices()), 3) + + # test vertex_count + self.assertEqual(self.dg0.vertex_count(), 0) + self.assertEqual(self.dg1.vertex_count(), 2) + self.assertEqual(self.dg2.vertex_count(), 2) + self.assertEqual(self.dg3.vertex_count(), 3) + + # test edge_count + self.assertEqual(self.dg0.edge_count(), 0) + self.assertEqual(self.dg1.edge_count(), 1) + self.assertEqual(self.dg2.edge_count(), 2) + self.assertEqual(self.dg3.edge_count(), 3) + + print(self.dg1) + + From ce82f90b11bedefa2d672ff38b3389af79bda7a8 Mon Sep 17 00:00:00 2001 From: JulianGriggs Date: Fri, 7 Aug 2015 22:02:36 -0400 Subject: [PATCH 45/89] Fixing __str__ method --- algorithms/data_structure/undirected_graph.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/algorithms/data_structure/undirected_graph.py b/algorithms/data_structure/undirected_graph.py index b0e956b..b937226 100644 --- a/algorithms/data_structure/undirected_graph.py +++ b/algorithms/data_structure/undirected_graph.py @@ -81,8 +81,8 @@ def vertices(self): def __str__(self): s = [] - s.append("{0} vertices and {1} edges \n".format(self.__vertex_count, - self.__edge_count)) + s.append("{0} vertices and {1} edges \n".format(self.__v_count, + self.__e_count)) for key in self.vertices(): s.append("{0}: ".format(key)) for val in self.adj(key): From d8820ac705c4b4c70a3683add6e6abd06f9c2e1f Mon Sep 17 00:00:00 2001 From: JulianGriggs Date: Fri, 7 Aug 2015 22:03:22 -0400 Subject: [PATCH 46/89] Updating documentation --- algorithms/data_structure/undirected_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/data_structure/undirected_graph.py b/algorithms/data_structure/undirected_graph.py index b937226..b4544ac 100644 --- a/algorithms/data_structure/undirected_graph.py +++ b/algorithms/data_structure/undirected_graph.py @@ -7,7 +7,7 @@ It supports the following two primary operations: add_edge: add an edge to the graph O(1) adj: return list of all of the vertices adjacent to a vertex O(1) - vertices: return list of all vertices in the graph O(N) + vertices: return list of all vertices in the graph O(V) It also supports the followign secondary operations: vertex_count: return the number of vertices O(1) From 3bb57917f1cda56da7facb8bbe7e71f93a9faabb Mon Sep 17 00:00:00 2001 From: JulianGriggs Date: Fri, 7 Aug 2015 22:38:24 -0400 Subject: [PATCH 47/89] Fixing Digraph and corresponding tests --- algorithms/data_structure/digraph.py | 34 ++++++++++++------------- algorithms/tests/test_data_structure.py | 22 +++++++++++++--- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/algorithms/data_structure/digraph.py b/algorithms/data_structure/digraph.py index 26eafac..a7d8696 100644 --- a/algorithms/data_structure/digraph.py +++ b/algorithms/data_structure/digraph.py @@ -1,7 +1,7 @@ """ Directed Graph data structure implemented: -------------------------------------------- - The Directed_Graph class represents an undirected graph of vertices + The Digraph class represents a directed graph of vertices which can be any hashable value. It supports the following two primary operations: @@ -9,18 +9,18 @@ adj: return list of all of the vertices adjacent to a vertex O(1) vertices: return list of all vertices in the graph O(V) - It also supports the followign secondary operations: + It also supports the following secondary operations: vertex_count: return the number of vertices O(1) edge_count: return the number of edges O(1) degree: return degree of the vertex O(1) - reverse: return a reversed version of the digraph O (V*E) + reverse: return a reversed version of the digraph O(V+E) Parallel edges and self-loops are permitted. - Adapted from: http://algs4.cs.princeton.edu/41undirected/Graph.java.html + Adapted from: http://algs4.cs.princeton.edu/42directed/Digraph.java.html """ -class Digraph : +class Digraph() : def __init__(self): self.__adj = {} self.__v_count = 0 @@ -80,17 +80,17 @@ def vertices(self): """ return self.__adj.keys() - def reverse(self): - """ - Returns the reverse of this digraph - """ - digraph_reversed = Digraph() - old_vertices = self.vertices() - - for src in old_vertices: - for dest in old_vertices.adj(src): - digraph_reversed.add_edge(dest, src) - return digraph_reversed; + def reverse(self): + """ + Returns the reverse of this digraph + """ + digraph_reversed = Digraph() + old_vertices = self.vertices() + + for src in old_vertices: + for dest in self.adj(src): + digraph_reversed.add_edge(dest, src) + return digraph_reversed; def __str__(self): @@ -103,4 +103,4 @@ def __str__(self): s.append("{0} ".format(val)) s.append("\n") - return "".join(s) \ No newline at end of file + return "".join(s) diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index beee0d7..baf4bdf 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -116,6 +116,8 @@ def test_directed_graph(self): # populating self.dg1.add_edge(1, 2) + + self.dg1_rev = self.dg1.reverse() # reverse self.dg2.add_edge(1,2) self.dg2.add_edge(1,2) @@ -130,6 +132,11 @@ def test_directed_graph(self): self.assertTrue(1 not in self.dg1.adj(2)) self.assertEqual(len(self.dg1.adj(2)), 0) + self.assertTrue(1 in self.dg1_rev.adj(2)) + self.assertEqual(len(self.dg1_rev.adj(2)), 1) + self.assertTrue(2 not in self.dg1_rev.adj(1)) + self.assertEqual(len(self.dg1_rev.adj(1)), 0) + self.assertTrue(2 in self.dg2.adj(1)) self.assertEqual(len(self.dg2.adj(1)), 2) self.assertTrue(1 not in self.dg2.adj(2)) @@ -146,8 +153,13 @@ def test_directed_graph(self): # test degree self.assertEqual(self.dg1.outdegree(1), 1) self.assertEqual(self.dg1.outdegree(2), 0) + + self.assertEqual(self.dg1_rev.outdegree(2), 1) + self.assertEqual(self.dg1_rev.outdegree(1), 0) + self.assertEqual(self.dg2.outdegree(1), 2) self.assertEqual(self.dg2.outdegree(2), 0) + self.assertEqual(self.dg3.outdegree(1), 2) self.assertEqual(self.dg3.outdegree(2), 0) self.assertEqual(self.dg3.outdegree(3), 1) @@ -160,6 +172,10 @@ def test_directed_graph(self): self.assertTrue(2 in self.dg1.vertices()) self.assertEqual(len(self.dg1.vertices()), 2) + self.assertTrue(2 in self.dg1_rev.vertices()) + self.assertTrue(1 in self.dg1_rev.vertices()) + self.assertEqual(len(self.dg1_rev.vertices()), 2) + self.assertTrue(1 in self.dg2.vertices()) self.assertTrue(2 in self.dg2.vertices()) self.assertEqual(len(self.dg2.vertices()), 2) @@ -172,15 +188,13 @@ def test_directed_graph(self): # test vertex_count self.assertEqual(self.dg0.vertex_count(), 0) self.assertEqual(self.dg1.vertex_count(), 2) + self.assertEqual(self.dg1_rev.vertex_count(), 2) self.assertEqual(self.dg2.vertex_count(), 2) self.assertEqual(self.dg3.vertex_count(), 3) # test edge_count self.assertEqual(self.dg0.edge_count(), 0) self.assertEqual(self.dg1.edge_count(), 1) + self.assertEqual(self.dg1_rev.edge_count(), 1) self.assertEqual(self.dg2.edge_count(), 2) self.assertEqual(self.dg3.edge_count(), 3) - - print(self.dg1) - - From fc47dcad17ebcaa3af2cae0eebe80dd39b555c13 Mon Sep 17 00:00:00 2001 From: JoaoGFarias Date: Wed, 12 Aug 2015 22:14:22 -0300 Subject: [PATCH 48/89] Updating README, AUTHORS and TODO --- AUTHORS.rst | 2 ++ README.rst | 3 +++ TODO.rst | 3 +-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 9784045..dad7e08 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -25,3 +25,5 @@ List of contributors: - `rasbt `_ - `JoaoGFarias `_ - `kabrapratik28 `_ +- `JulianGriggs `_ +- `oprblackout `_ diff --git a/README.rst b/README.rst index f65c7a2..671e5d1 100644 --- a/README.rst +++ b/README.rst @@ -15,6 +15,9 @@ Algorithms implemented so far: - Queue - Stack - Disjoint Set +- Single Linked List +- Undirected Graph +- Digraph **Sorting:** diff --git a/TODO.rst b/TODO.rst index a342ff8..422a1b0 100644 --- a/TODO.rst +++ b/TODO.rst @@ -17,7 +17,6 @@ Below is an ever changing list of things that I would like to accomplish or impl - Strassen's Matrix Multiplication - *k*-Selection (Minimum, Maximum, Median, Arbitrary *k*) - Data Structures - - Linked Lists - Hash Tables - Binary Search Trees - Red-Black Trees @@ -57,7 +56,7 @@ Below is an ever changing list of things that I would like to accomplish or impl - Sequential Backward Selection (SFS) - 'plus l take away r' - algorithm (SBS + SFS) - Sequential Floating Forward Algorithm (SFFS) - + **Misc.:** From ad023ddd03bfafbc1e16a61a93ca2ac64722fcaf Mon Sep 17 00:00:00 2001 From: JulianGriggs Date: Thu, 13 Aug 2015 20:26:32 -0400 Subject: [PATCH 49/89] Adding BST implementation --- .../data_structure/binary_search_tree.py | 359 ++++++++++++++++++ algorithms/tests/test_data_structure.py | 359 +++++++++++++++++- 2 files changed, 716 insertions(+), 2 deletions(-) create mode 100644 algorithms/data_structure/binary_search_tree.py diff --git a/algorithms/data_structure/binary_search_tree.py b/algorithms/data_structure/binary_search_tree.py new file mode 100644 index 0000000..ed132f8 --- /dev/null +++ b/algorithms/data_structure/binary_search_tree.py @@ -0,0 +1,359 @@ +""" + Binary Search Tree data structure implemented: + -------------------------------- + The Binary Search Tree represents an ordered symbol table of generic + key-value pairs. Keys must be comparable. Does not permit duplicate keys. + When assocating a value with a key already present in the BST, the previous + value is replaced by the new one. This implementation is for an unbalanced + BST. + + It supports the following primary operations: + Method Description + ----------------------------------------- + size Return size of BST + get Retrieve value for key in BST + put Add key-value pair to BST + contains Check if key is in BST + is_empty Check if BST is empty + min_key Get the minimum key in BST + max_key Get the maximum key in BST + floor_key Get the biggest key that is less than or equal to key + ceiling_key Get the smallest key that is greater than or equal to key + rank Get get the number of keys less than key + select_key Get the key with a given rank + delete_min Delete the key-value pair with minimum key from BST + delete_max Delete the key-value pair with maximum key from BST + delete Delete key-value pair with given key from BST + keys Get all keys in BST in ascending order + + Method Worst Case Balanced Tree + ----------------------------------------- + size O(1) O(1) + get O(N) O(lg N) + put O(N) O(lg N) + contains O(N) O(lg N) + is_empty O(1) O(1) + min_key O(N) O(lg N) + max_key O(N) O(lg N) + floor_key O(N) O(lg N) + ceiling_key O(N) O(lg N) + rank O(N) O(lg N) + select_key O(N) O(lg N) + delete_min O(N) O(lg N) + delete_max O(N) O(lg N) + delete O(N) O(lg N) + keys O(N) O(N) + + Adapted from: http://algs4.cs.princeton.edu/32bst +""" +class Node: + + def __init__(self, key=None, val=None, size_of_subtree=1): + self.key = key + self.val = val + self.size_of_subtree = size_of_subtree + self.left = None + self.right = None + +class BinarySearchTree: + + def __init__(self): + self.root = None + + + def _size(self, node): + if node == None: + return 0 + else: + return node.size_of_subtree + + def size(self): + ''' + Return the number of nodes in the BST + ''' + return self._size(self.root) + + def is_empty(self): + ''' + Returns True if the BST is empty, False otherwise + ''' + return self.size() == 0 + + def _get(self, key, node): + if node == None: + return None + + if key < node.key: + return self._get(key, node.left) + elif key > node.key: + return self._get(key, node.right) + else: + return node.val + + def get(self, key): + ''' + Return the value paired with 'key' + ''' + return self._get(key, self.root) + + def contains(self, key): + ''' + Returns True if the BST contains 'key', False otherwise + ''' + return self.get(key) != None + + def _put(self, key, val, node): + + # If we hit the end of a branch, create a new node + if node == None: + return Node(key, val) + + # Follow left branch + if key < node.key: + node.left = self._put(key, val, node.left) + # Follow right branch + elif key > node.key: + node.right = self._put(key, val, node.right) + # Overwrite value + else: + node.val = val + + node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 + return node + + def put(self, key, val): + ''' + Add a new key-value pair. + ''' + self.root = self._put(key, val, self.root) + + def _min_node(self): + ''' + Return the node with the minimum key in the BST + ''' + min_node = self.root + # Return none if empty BST + if min_node == None: return None + + while min_node.left != None: + min_node = min_node.left + + return min_node + + def min_key(self): + ''' + Return the minimum key in the BST + ''' + min_node = self._min_node() + if min_node == None: + return None + else: + return min_node.key + + def _max_node(self): + ''' + Return the node with the maximum key in the BST + ''' + max_node = self.root + # Return none if empty BST + if max_node == None: return None + + while max_node.right != None: + max_node = max_node.right + + return max_node + + def max_key(self): + ''' + Return the maximum key in the BST + ''' + max_node = self._max_node() + if max_node == None: + return None + else: + return max_node.key + + def _floor_node(self, key, node): + ''' + Returns the node with the biggest key that is less than or equal to the + given value 'key' + ''' + if node == None: return None + + if key < node.key: + # Floor must be in left subtree + return self._floor_node(key, node.left) + + elif key > node.key: + # Floor is either in right subtree or is this node + attempt_in_right = self._floor_node(key, node.right) + if attempt_in_right == None: + return node + else: + return attempt_in_right + + else: + # Keys are equal so floor is node with this key + return node + + def floor_key(self, key): + ''' + Returns the biggest key that is less than or equal to the given value + 'key' + ''' + floor_node = self._floor_node(key, self.root) + if floor_node == None: + return None + else: + return floor_node.key + + def _ceiling_node(self, key, node): + ''' + Returns the node with the smallest key that is greater than or equal to + the given value 'key' + ''' + if node == None: + return None + + if key < node.key: + # Ceiling is either in left subtree or is this node + attempt_in_left = self._ceiling_node(key, node.left) + if attempt_in_left == None: + return node + else: + return attempt_in_left + elif key > node.key: + # Ceiling must be in right subtree + return self._ceiling_node(key, node.right) + else: + # Keys are equal so ceiling is node with this key + return node + + def ceiling_key(self, key): + ''' + Returns the smallest key that is greater than or equal to the given + value 'key' + ''' + ceiling_node = self._ceiling_node(key, self.root) + if ceiling_node == None: + return None + else: + return ceiling_node.key + + def _select_node(self, rank, node): + ''' + Return the node with rank equal to 'rank' + ''' + if node == None: + return None + + left_size = self._size(node.left) + if left_size < rank: + return self._select_node(rank - left_size - 1, node.right) + elif left_size > rank: + return self._select_node(rank, node.left) + else: + return node + + def select_key(self, rank): + ''' + Return the key with rank equal to 'rank' + ''' + select_node = self._select_node(rank, self.root) + if select_node == None: + return None + else: + return select_node.key + + def _rank(self, key, node): + if node == None: return None + + if key < node.key: + return self._rank(key, node.left) + elif key > node.key: + return self._size(node.left) + self._rank(key, node.right) + 1 + + else: + return self._size(node.left) + + def rank(self, key): + ''' + Return the number of keys less than a given 'key'. + ''' + return self._rank(key, self.root) + + def _delete(self, key, node): + if node == None: + return None + if key < node.key: + node.left = self._delete(key, node.left) + elif key > node.key: + node.right = self._delete(key, node.right) + + else: + if node.right == None: + return node.left + elif node.left == None: + return node.right + else: + old_node = node + node = self._ceiling_node(key, node.right) + node.right = self._delete_min(old_node.right) + node.left = old_node.left + node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 + return node + + def delete(self, key): + ''' + Remove the node with key equal to 'key' + ''' + self.root = self._delete(key, self.root) + + def _delete_min(self, node): + if node.left == None: + return node.right + + node.left = self._delete_min(node.left) + node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 + return node + + def delete_min(self): + ''' + Remove the key-value pair with the smallest key. + ''' + self.root = self._delete_min(self.root) + + def _delete_max(self, node): + if node.right == None: + return node.left + + node.right = self._delete_max(node.right) + node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 + return node + + def delete_max(self): + ''' + Remove the key-value pair with the largest key. + ''' + self.root = self._delete_max(self.root) + + def _keys(self, node, keys): + if node == None: + return keys + + if node.left != None: + keys = self._keys(node.left, keys) + + keys.append(node.key) + + if node.right != None: + keys = self._keys(node.right, keys) + + return keys + + def keys(self): + ''' + Return all of the keys in the BST in aschending order + ''' + keys = [] + return self._keys(self.root, keys) diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index 4944a04..807234d 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -1,7 +1,7 @@ import unittest -from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression,digraph,singly_linked_list, undirected_graph - +from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression,digraph,singly_linked_list, undirected_graph, binary_search_tree +from random import shuffle class TestStack(unittest.TestCase): """ Test Stack Implementation @@ -297,3 +297,358 @@ def test_directed_graph(self): self.assertEqual(self.dg1_rev.edge_count(), 1) self.assertEqual(self.dg2.edge_count(), 2) self.assertEqual(self.dg3.edge_count(), 3) + +class TestBinarySearchTree(unittest.TestCase): + """ + Test Binary Search Tree Implementation + """ + key_val = [("a", 1), ("b", 2), ("c", 3), + ("d", 4), ("e", 5), ("f", 6), + ("g", 7), ("h", 8), ("i", 9)] + + + def shuffle_list(self, ls): + shuffle(ls) + return ls + + def test_size(self): + # Size starts at 0 + self.bst = binary_search_tree.BinarySearchTree() + self.assertEqual(self.bst.size(), 0) + # Doing a put increases the size to 1 + self.bst.put("one", 1) + self.assertEqual(self.bst.size(), 1) + # Putting a key that is already in doesn't change size + self.bst.put("one", 1) + self.assertEqual(self.bst.size(), 1) + self.bst.put("one", 2) + self.assertEqual(self.bst.size(), 1) + + + self.bst = binary_search_tree.BinarySearchTree() + size = 0 + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + size += 1 + self.assertEqual(self.bst.size(), size) + + shuffled = self.shuffle_list(self.key_val[:]) + + self.bst = binary_search_tree.BinarySearchTree() + size = 0 + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + size += 1 + self.assertEqual(self.bst.size(), size) + + def test_is_empty(self): + self.bst = binary_search_tree.BinarySearchTree() + self.assertTrue(self.bst.is_empty()) + self.bst.put("a", 1) + self.assertFalse(self.bst.is_empty()) + + def test_get(self): + self.bst = binary_search_tree.BinarySearchTree() + # Getting a key not in BST returns None + self.assertEqual(self.bst.get("one"), None) + + # Get with a present key returns proper value + self.bst.put("one", 1) + self.assertEqual(self.bst.get("one"), 1) + + + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.get(k), v) + + shuffled = self.shuffle_list(self.key_val[:]) + + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.get(k), v) + + def test_contains(self): + self.bst = binary_search_tree.BinarySearchTree() + self.assertFalse(self.bst.contains("a")) + self.bst.put("a", 1) + self.assertTrue(self.bst.contains("a")) + + def test_put(self): + self.bst = binary_search_tree.BinarySearchTree() + + # When BST is empty first put becomes root + self.bst.put("bbb", 1) + self.assertEqual(self.bst.root.key, "bbb") + self.assertEqual(self.bst.root.left, None) + + # Adding a key greater than root doesn't update the left tree + # but does update the right + self.bst.put("ccc", 2) + self.assertEqual(self.bst.root.key, "bbb") + self.assertEqual(self.bst.root.left, None) + self.assertEqual(self.bst.root.right.key, "ccc") + + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("bbb", 1) + # Adding a key less than root doesn't update the right tree + # but does update the left + self.bst.put("aaa", 2) + self.assertEqual(self.bst.root.key, "bbb") + self.assertEqual(self.bst.root.right, None) + self.assertEqual(self.bst.root.left.key, "aaa") + + + self.bst = binary_search_tree.BinarySearchTree() + size = 0 + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + size += 1 + self.assertEqual(self.bst.get(k), v) + self.assertEqual(self.bst.size(), size) + + self.bst = binary_search_tree.BinarySearchTree() + + shuffled = self.shuffle_list(self.key_val[:]) + + size = 0 + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + size += 1 + self.assertEqual(self.bst.get(k), v) + self.assertEqual(self.bst.size(), size) + + + def test_min_key(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val[::-1]: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.min_key(), k) + + shuffled = self.shuffle_list(self.key_val[:]) + + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.min_key(), "a") + + + def test_max_key(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.max_key(), k) + + shuffled = self.shuffle_list(self.key_val[:]) + + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.max_key(), "i") + + def test_floor_key(self): + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.put("c", 3) + self.bst.put("e", 5) + self.bst.put("g", 7) + self.assertEqual(self.bst.floor_key("a"), "a") + self.assertEqual(self.bst.floor_key("b"), "a") + self.assertEqual(self.bst.floor_key("g"), "g") + self.assertEqual(self.bst.floor_key("h"), "g") + + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("c", 3) + self.bst.put("e", 5) + self.bst.put("a", 1) + self.bst.put("g", 7) + self.assertEqual(self.bst.floor_key("a"), "a") + self.assertEqual(self.bst.floor_key("b"), "a") + self.assertEqual(self.bst.floor_key("g"), "g") + self.assertEqual(self.bst.floor_key("h"), "g") + + def test_ceiling_key(self): + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.put("c", 3) + self.bst.put("e", 5) + self.bst.put("g", 7) + self.assertEqual(self.bst.ceiling_key("a"), "a") + self.assertEqual(self.bst.ceiling_key("b"), "c") + self.assertEqual(self.bst.ceiling_key("g"), "g") + self.assertEqual(self.bst.ceiling_key("f"), "g") + + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("c", 3) + self.bst.put("e", 5) + self.bst.put("a", 1) + self.bst.put("g", 7) + self.assertEqual(self.bst.ceiling_key("a"), "a") + self.assertEqual(self.bst.ceiling_key("b"), "c") + self.assertEqual(self.bst.ceiling_key("g"), "g") + self.assertEqual(self.bst.ceiling_key("f"), "g") + + def test_select_key(self): + shuffled = self.shuffle_list(self.key_val[:]) + + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.select_key(0), "a") + self.assertEqual(self.bst.select_key(1), "b") + self.assertEqual(self.bst.select_key(2), "c") + + def test_rank(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + + self.assertEqual(self.bst.rank("a"), 0) + self.assertEqual(self.bst.rank("b"), 1) + self.assertEqual(self.bst.rank("c"), 2) + self.assertEqual(self.bst.rank("d"), 3) + + shuffled = self.shuffle_list(self.key_val[:]) + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + + self.assertEqual(self.bst.rank("a"), 0) + self.assertEqual(self.bst.rank("b"), 1) + self.assertEqual(self.bst.rank("c"), 2) + self.assertEqual(self.bst.rank("d"), 3) + + + def test_delete_min(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + for i in range(self.bst.size() - 1): + self.bst.delete_min() + self.assertEqual(self.bst.min_key(), self.key_val[i+1][0]) + self.bst.delete_min() + self.assertEqual(self.bst.min_key(), None) + + + shuffled = self.shuffle_list(self.key_val[:]) + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + for i in range(self.bst.size() - 1): + self.bst.delete_min() + self.assertEqual(self.bst.min_key(), self.key_val[i+1][0]) + self.bst.delete_min() + self.assertEqual(self.bst.min_key(), None) + + def test_delete_max(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + for i in range(self.bst.size() - 1, 0, -1): + self.bst.delete_max() + self.assertEqual(self.bst.max_key(), self.key_val[i-1][0]) + self.bst.delete_max() + self.assertEqual(self.bst.max_key(), None) + + + shuffled = self.shuffle_list(self.key_val[:]) + + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + for i in range(self.bst.size() - 1, 0, -1): + self.bst.delete_max() + self.assertEqual(self.bst.max_key(), self.key_val[i-1][0]) + self.bst.delete_max() + self.assertEqual(self.bst.max_key(), None) + + def test_delete(self): + # delete key from an empty bst + self.bst = binary_search_tree.BinarySearchTree() + self.bst.delete("a") + self.assertEqual(self.bst.root, None) + self.assertEqual(self.bst.size(), 0) + + # delete key not present in bst + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.delete("b") + self.assertEqual(self.bst.root.key, "a") + self.assertEqual(self.bst.size(), 1) + + # delete key when bst only contains one key + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.assertEqual(self.bst.root.key, "a") + self.bst.delete("a") + self.assertEqual(self.bst.root, None) + self.assertEqual(self.bst.size(), 0) + + # delete parent key when it only has a left child + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("b", 2) + self.bst.put("a", 1) + self.assertEqual(self.bst.root.left.key, "a") + self.bst.delete("b") + self.assertEqual(self.bst.root.key, "a") + self.assertEqual(self.bst.size(), 1) + + # delete parent key when it only has a right child + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.put("b", 2) + self.assertEqual(self.bst.root.right.key, "b") + self.bst.delete("a") + self.assertEqual(self.bst.root.key, "b") + self.assertEqual(self.bst.size(), 1) + + # delete left child key + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("b", 2) + self.bst.put("a", 1) + self.assertEqual(self.bst.root.left.key, "a") + self.bst.delete("a") + self.assertEqual(self.bst.root.key, "b") + self.assertEqual(self.bst.size(), 1) + + # delete right child key + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.put("b", 2) + self.assertEqual(self.bst.root.right.key, "b") + self.bst.delete("b") + self.assertEqual(self.bst.root.key, "a") + self.assertEqual(self.bst.size(), 1) + + # delete parent key when it has a left and right child + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("b", 2) + self.bst.put("a", 1) + self.bst.put("c", 3) + self.bst.delete("b") + self.assertEqual(self.bst.root.key, "c") + self.assertEqual(self.bst.size(), 2) + + def test_keys(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.keys(), ["a", "b", "c", "d", "e", "f", "g", "h", "i"]) From 1bd211f8227f6e98b320503b50bc467f8a2632e4 Mon Sep 17 00:00:00 2001 From: Amaury Medeiros Date: Fri, 18 Sep 2015 12:27:44 -0300 Subject: [PATCH 50/89] Add Longest Common Subsequence algorithm Add dynamic_programming package to setup.py --- README.rst | 8 +++- TODO.rst | 1 - algorithms/dynamic_programming/__init__.py | 0 algorithms/dynamic_programming/lcs.py | 39 ++++++++++++++++++++ algorithms/tests/test_dynamic_programming.py | 22 +++++++++++ setup.py | 2 +- 6 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 algorithms/dynamic_programming/__init__.py create mode 100644 algorithms/dynamic_programming/lcs.py create mode 100644 algorithms/tests/test_dynamic_programming.py diff --git a/README.rst b/README.rst index 671e5d1..cce6042 100644 --- a/README.rst +++ b/README.rst @@ -52,6 +52,11 @@ Algorithms implemented so far: - Standard Normal Probability Density Function - Cumulative Density Function (Approximation; 16 digit precision for 300 iter.) - Sieve of Eratosthenes + ++**Dynamic Programming:** + +- Longest Common Subsequence + **Random:** - Mersenne Twister @@ -106,8 +111,9 @@ I want to personally thank everybody that has contributed so far and your names TODO: ----- +See `TODO.rst`_. -See `TODO.rst`. +.. _`TODO.rst`: TODO.rst License: diff --git a/TODO.rst b/TODO.rst index 422a1b0..01fcc44 100644 --- a/TODO.rst +++ b/TODO.rst @@ -25,7 +25,6 @@ Below is an ever changing list of things that I would like to accomplish or impl - van Emde Boas Trees - Dynamic Programming - Matrix-Chain Multiplication - - Longest Common Subsequence - Huffman Encoding - Graph Algorithms - Breadth-First Search diff --git a/algorithms/dynamic_programming/__init__.py b/algorithms/dynamic_programming/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/algorithms/dynamic_programming/lcs.py b/algorithms/dynamic_programming/lcs.py new file mode 100644 index 0000000..9336a38 --- /dev/null +++ b/algorithms/dynamic_programming/lcs.py @@ -0,0 +1,39 @@ +""" + lcs.py + + This module implements the dynamic programming solution to + the longest common subsequence algorithm. + + Pre: two strings str1 and str2 + Post: a string representing the longest subsequence common to str1 and str2 + + Pseudo Code: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem +""" + +def build_lengths_matrix(str1, str2): + matrix = [[0 for j in range(len(str2)+1)] for i in range(len(str1)+1)] + for i, x in enumerate(str1): + for j, y in enumerate(str2): + if x == y: + matrix[i+1][j+1] = matrix[i][j] + 1 + else: + matrix[i+1][j+1] = max(matrix[i+1][j], matrix[i][j+1]) + return matrix + +def read_from_matrix(matrix, str1, str2): + result = "" + i, j = len(str1), len(str2) + while i != 0 and j != 0: + if matrix[i][j] == matrix[i-1][j]: + i -= 1 + elif matrix[i][j] == matrix[i][j-1]: + j -= 1 + else: + result += str1[i-1] + i -= 1 + j -= 1 + return result[::-1] + +def lcs(str1, str2): + lengths = build_lengths_matrix(str1, str2) + return read_from_matrix(lengths, str1, str2) \ No newline at end of file diff --git a/algorithms/tests/test_dynamic_programming.py b/algorithms/tests/test_dynamic_programming.py new file mode 100644 index 0000000..effae9f --- /dev/null +++ b/algorithms/tests/test_dynamic_programming.py @@ -0,0 +1,22 @@ +import unittest +from ..dynamic_programming.lcs import lcs + + +class TestLCS(unittest.TestCase): + """ + Tests the Longest Common Subsequence of several strings + """ + + def test_lcs(self): + str1 = "BANANA" + str2 = "ABA" + str3 = "BCAD" + str4 = "NNAD" + + self.assertEqual(lcs(str1, str1), str1) + self.assertEqual(lcs(str1, str2), "BA") + self.assertEqual(lcs(str1, str3), "BA") + self.assertEqual(lcs(str1, str4), "NNA") + self.assertEqual(lcs(str2, str3), "BA") + self.assertEqual(lcs(str2, str4), "A") + self.assertEqual(lcs(str3, str4), "AD") \ No newline at end of file diff --git a/setup.py b/setup.py index 7c914c0..c40ebca 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ def long_description(): author='Nic Young', author_email='nryoung@gmail.com', license='BSD', - packages=['algorithms', 'algorithms.data_structure','algorithms.sorting', 'algorithms.shuffling', + packages=['algorithms', 'algorithms.data_structure', 'algorithms.dynamic_programming', 'algorithms.sorting', 'algorithms.shuffling', 'algorithms.searching', 'algorithms.math', 'algorithms.tests'], classifiers=[ 'Programming Language :: Python :: 2.7',], From 28200ed41c12a6ace0ed9444879d9da1eddb549d Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 19 Sep 2015 01:59:15 -0700 Subject: [PATCH 51/89] Add Travis integration, fixes #91 - update requirements - Remove dev python branches from Travis testing - Add build status icon to README --- .travis.yml | 11 +++++++++++ README.rst | 3 +++ requirements.txt | 4 ---- 3 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bf846ff --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: python +python: + - "2.7" + - "3.2" + - "3.3" + - "3.4" + - "3.5" +# command to install dependencies +install: "pip install -r requirements.txt" +# command to run tests +script: nosetests diff --git a/README.rst b/README.rst index cce6042..f378c93 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,6 @@ +.. image:: https://travis-ci.org/nryoung/algorithms.svg?branch=master + :target: https://travis-ci.org/nryoung/algorithms + Algorithms ========== diff --git a/requirements.txt b/requirements.txt index 65d9a46..e69de29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +0,0 @@ -argparse==1.2.1 -distribute==0.6.27 -nose==1.1.2 -wsgiref==0.1.2 From 5667d05fa9df36b5abca2ec257c2fb8d35bc2c38 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Thu, 24 Sep 2015 21:48:50 -0700 Subject: [PATCH 52/89] Add Python 3 compatibility, fixes #102 - Remove deprecated syntax for raising exceptions - Replace deprecated syntax for xrange with range calls - Encode strings to utf-8 before hashing them - Wrap range calls in a list, where lists are expected - Replace division operator with new syntax to get truncation - Wrap calls to dict_keys() in list, where lists are expected --- algorithms/data_structure/union_find.py | 16 +++++++++------- algorithms/data_structure/union_find_by_rank.py | 12 ++++++------ .../union_find_with_path_compression.py | 12 ++++++------ algorithms/math/extended_gcd.py | 2 +- algorithms/math/sieve_atkin.py | 10 +++++----- algorithms/math/sieve_eratosthenes.py | 4 ++-- algorithms/searching/kmp_search.py | 2 +- algorithms/searching/rabinkarp_search.py | 6 +++--- algorithms/sorting/bogo_sort.py | 2 +- algorithms/sorting/heap_sort.py | 2 +- algorithms/sorting/shell_sort.py | 2 +- algorithms/tests/test_data_structure.py | 4 ++-- algorithms/tests/test_math.py | 1 + algorithms/tests/test_shuffling.py | 2 +- algorithms/tests/test_sorting.py | 10 +++++----- 15 files changed, 45 insertions(+), 42 deletions(-) diff --git a/algorithms/data_structure/union_find.py b/algorithms/data_structure/union_find.py index 81be744..b77e29f 100644 --- a/algorithms/data_structure/union_find.py +++ b/algorithms/data_structure/union_find.py @@ -12,22 +12,24 @@ Time Complexity : O(N) (a highly unbalanced tree might be created, nothing better a linked-list) Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure """ + + class UnionFind: def __init__(self, N): if type(N) != int: - raise TypeError, "size must be integer" + raise TypeError("size must be integer") if N < 0: - raise ValueError, "N cannot be a negative integer" + raise ValueError("N cannot be a negative integer") self.__parent = [] - self.__N = N + self.__N = N for i in range(0, N): self.__parent.append(i) def make_set(self, x): if type(x) != int: - raise TypeError, "x must be integer" + raise TypeError("x must be integer") if x != self.__N: - raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.__N) + raise ValueError("a new element must have index {0} since the total num of elements is {0}".format(self.__N)) self.__parent.append(x) self.__N = self.__N + 1 @@ -52,6 +54,6 @@ def is_connected(self, x, y): def __validate_ele(self, x): if type(x) != int: - raise TypeError, "{0} is not an integer".format(x) + raise TypeError("{0} is not an integer".format(x)) if x < 0 or x >= self.__N: - raise ValueError, "{0} is not in [0,{1})".format(x, self.__N) + raise ValueError("{0} is not in [0,{1})".format(x, self.__N)) diff --git a/algorithms/data_structure/union_find_by_rank.py b/algorithms/data_structure/union_find_by_rank.py index d0d7449..ec00258 100644 --- a/algorithms/data_structure/union_find_by_rank.py +++ b/algorithms/data_structure/union_find_by_rank.py @@ -14,9 +14,9 @@ class UnionFindByRank: def __init__(self, N): if type(N) != int: - raise TypeError, "size must be integer" + raise TypeError("size must be integer") if N < 0: - raise ValueError, "N cannot be a negative integer" + raise ValueError("N cannot be a negative integer") self.__parent = [] self.__rank = [] self.__N = N @@ -26,9 +26,9 @@ def __init__(self, N): def make_set(self, x): if type(x) != int: - raise TypeError, "x must be integer" + raise TypeError("x must be integer") if x != self.__N: - raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.__N) + raise ValueError("a new element must have index {0} since the total num of elements is {0}".format(self.__N)) self.__parent.append(x) self.__rank.append(0) self.__N = self.__N + 1 @@ -63,7 +63,7 @@ def is_connected(self, x, y): def __validate_ele(self, x): if type(x) != int: - raise TypeError, "{0} is not an integer".format(x) + raise TypeError("{0} is not an integer".format(x)) if x < 0 or x >= self.__N: - raise ValueError, "{0} is not in [0,{1})".format(x, self.__N) + raise ValueError("{0} is not in [0,{1})".format(x, self.__N)) diff --git a/algorithms/data_structure/union_find_with_path_compression.py b/algorithms/data_structure/union_find_with_path_compression.py index d9e192d..a23fed9 100644 --- a/algorithms/data_structure/union_find_with_path_compression.py +++ b/algorithms/data_structure/union_find_with_path_compression.py @@ -15,9 +15,9 @@ class UnionFindWithPathCompression: def __init__(self, N): if type(N) != int: - raise TypeError, "size must be integer" + raise TypeError("size must be integer") if N < 0: - raise ValueError, "N cannot be a negative integer" + raise ValueError("N cannot be a negative integer") self.__parent = [] self.__rank = [] self.__N = N @@ -27,9 +27,9 @@ def __init__(self, N): def make_set(self, x): if type(x) != int: - raise TypeError, "x must be integer" + raise TypeError("x must be integer") if x != self.__N: - raise ValueError, "a new element must have index {0} since the total num of elements is {0}".format(self.__N) + raise ValueError("a new element must have index {0} since the total num of elements is {0}".format(self.__N)) self.__parent.append(x) self.__rank.append(0) self.__N = self.__N + 1 @@ -73,7 +73,7 @@ def parent(self, x): def __validate_ele(self, x): if type(x) != int: - raise TypeError, "{0} is not an integer".format(x) + raise TypeError("{0} is not an integer".format(x)) if x < 0 or x >= self.__N: - raise ValueError, "{0} is not in [0,{1})".format(x, self.__N) + raise ValueError("{0} is not in [0,{1})".format(x, self.__N)) diff --git a/algorithms/math/extended_gcd.py b/algorithms/math/extended_gcd.py index 4083d84..8a15469 100644 --- a/algorithms/math/extended_gcd.py +++ b/algorithms/math/extended_gcd.py @@ -27,7 +27,7 @@ def extended_gcd(p, q): y0 = 1 while(b != 0): - quotient = a / b + quotient = a // b (a, b) = (b, a % b) (x1, x0) = (x0 - quotient * x1, x1) (y1, y0) = (y0 - quotient * y1, y1) diff --git a/algorithms/math/sieve_atkin.py b/algorithms/math/sieve_atkin.py index bab4cf6..be30bc7 100644 --- a/algorithms/math/sieve_atkin.py +++ b/algorithms/math/sieve_atkin.py @@ -29,8 +29,8 @@ def atkin(limit): is_prime = [False] * (limit + 1) sqrt_limit = int(sqrt(limit)) + 1 - for x in xrange(1,sqrt_limit): - for y in xrange(1,sqrt_limit): + for x in range(1,sqrt_limit): + for y in range(1,sqrt_limit): n = 4 * x ** 2 + y ** 2 if n <= limit and (n % 12 == 1 or n % 12 == 5): is_prime[n] = not is_prime[n] @@ -41,11 +41,11 @@ def atkin(limit): if x > y and (n <= limit) and (n % 12 == 11): is_prime[n] = not is_prime[n] - for index in xrange(5,sqrt_limit): + for index in range(5,sqrt_limit): if is_prime[index]: - for composite in xrange(index ** 2, limit, index ** 2): + for composite in range(index ** 2, limit, index ** 2): is_prime[composite] = False - for index in xrange(7, limit): + for index in range(7, limit): if is_prime[index]: primes.append(index) return primes diff --git a/algorithms/math/sieve_eratosthenes.py b/algorithms/math/sieve_eratosthenes.py index 9c29ec2..28a04f2 100644 --- a/algorithms/math/sieve_eratosthenes.py +++ b/algorithms/math/sieve_eratosthenes.py @@ -22,9 +22,9 @@ def eratosthenes(end, start=2, return_boolean=False): primes = [] if end < start or end < 2: return [] - is_prime = [True for i in xrange(end + 1)] + is_prime = [True for i in range(end + 1)] is_prime[0] = is_prime[1] = False - for i in xrange(2, end + 1): + for i in range(2, end + 1): if not is_prime[i]: continue if start <= i <= end: diff --git a/algorithms/searching/kmp_search.py b/algorithms/searching/kmp_search.py index 46a48b3..a8f5944 100644 --- a/algorithms/searching/kmp_search.py +++ b/algorithms/searching/kmp_search.py @@ -40,7 +40,7 @@ def compute_prefix(word): prefix = [0] * word_length k = 0 - for q in xrange(1, word_length): + for q in range(1, word_length): while k > 0 and word[k] != word[q]: k = prefix[k - 1] diff --git a/algorithms/searching/rabinkarp_search.py b/algorithms/searching/rabinkarp_search.py index e779b5b..0c5909d 100644 --- a/algorithms/searching/rabinkarp_search.py +++ b/algorithms/searching/rabinkarp_search.py @@ -19,13 +19,13 @@ def search(s, sub): n, m = len(s), len(sub) - hsub_digest = md5(sub).digest() + hsub_digest = md5(sub.encode('utf-8')).digest() offsets = [] if m > n: return offsets - for i in xrange(n - m + 1): - if md5(s[i:i + m]).digest() == hsub_digest: + for i in range(n - m + 1): + if md5(s[i:i + m].encode('utf-8')).digest() == hsub_digest: if s[i:i + m] == sub: offsets.append(i) diff --git a/algorithms/sorting/bogo_sort.py b/algorithms/sorting/bogo_sort.py index c3f087d..7a9ff18 100644 --- a/algorithms/sorting/bogo_sort.py +++ b/algorithms/sorting/bogo_sort.py @@ -35,4 +35,4 @@ def sort(seq): def is_sorted(seq): - return all(seq[i - 1] <= seq[i] for i in xrange(1, len(seq))) + return all(seq[i - 1] <= seq[i] for i in range(1, len(seq))) diff --git a/algorithms/sorting/heap_sort.py b/algorithms/sorting/heap_sort.py index 5c564c9..d9b1ddb 100644 --- a/algorithms/sorting/heap_sort.py +++ b/algorithms/sorting/heap_sort.py @@ -36,7 +36,7 @@ def max_heapify(seq, i, n): def build_heap(seq): n = len(seq) - 1 - for i in range(n/2, -1, -1): + for i in range(n//2, -1, -1): max_heapify(seq, i, n) diff --git a/algorithms/sorting/shell_sort.py b/algorithms/sorting/shell_sort.py index c5b640a..40c2647 100644 --- a/algorithms/sorting/shell_sort.py +++ b/algorithms/sorting/shell_sort.py @@ -20,7 +20,7 @@ def sort(seq): - gaps = [x for x in range(len(seq) / 2, 0, -1)] + gaps = [x for x in range(len(seq) // 2, 0, -1)] for gap in gaps: for i in range(gap, len(seq)): diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index 807234d..29cbb58 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -173,7 +173,7 @@ def test_undirected_graph(self): self.assertEqual(self.ug3.degree(3), 1) # test vertices - self.assertEqual(self.ug0.vertices(), []) + self.assertEqual(list(self.ug0.vertices()), []) self.assertEqual(len(self.ug0.vertices()), 0) self.assertTrue(1 in self.ug1.vertices()) @@ -264,7 +264,7 @@ def test_directed_graph(self): self.assertEqual(self.dg3.outdegree(3), 1) # test vertices - self.assertEqual(self.dg0.vertices(), []) + self.assertEqual(list(self.dg0.vertices()), []) self.assertEqual(len(self.dg0.vertices()), 0) self.assertTrue(1 in self.dg1.vertices()) diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index 943e8c4..86f6b56 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -12,6 +12,7 @@ class TestExtendedGCD(unittest.TestCase): def test_extended_gcd(self): # Find extended_gcd of 35 and 77 (a, b) = extended_gcd(35, 77) + print(a, b) self.assertIs(35 * a + 77 * b, 7) # Find extended_gcd of 15 and 19 diff --git a/algorithms/tests/test_shuffling.py b/algorithms/tests/test_shuffling.py index 413c44b..59c3f8c 100644 --- a/algorithms/tests/test_shuffling.py +++ b/algorithms/tests/test_shuffling.py @@ -16,7 +16,7 @@ class TestKnuthShuffle(ShufflingAlgorithmTestCase): Tests Knuth shuffle on a small range from 0-9 """ def test_knuthshuffle(self): - self.shuffle = knuth.shuffle(range(10)) + self.shuffle = knuth.shuffle(list(range(10))) self.not_shuffled = 0 for i in self.sorted: diff --git a/algorithms/tests/test_sorting.py b/algorithms/tests/test_sorting.py index 16ba65c..f80f277 100644 --- a/algorithms/tests/test_sorting.py +++ b/algorithms/tests/test_sorting.py @@ -11,9 +11,9 @@ class SortingAlgorithmTestCase(unittest.TestCase): """ def setUp(self): - self.input = range(10) + self.input = list(range(10)) random.shuffle(self.input) - self.correct = range(10) + self.correct = list(range(10)) class TestBubbleSort(SortingAlgorithmTestCase): @@ -57,8 +57,8 @@ def test_mergesort(self): self.assertEqual(self.correct, self.output) def test_merge(self): - self.seq1 = range(0, 5) - self.seq2 = range(5, 10) + self.seq1 = list(range(0, 5)) + self.seq2 = list(range(5, 10)) self.seq = merge_sort.merge(self.seq1, self.seq2) self.assertIs(self.seq[0], 0) self.assertIs(self.seq[-1], 9) @@ -85,7 +85,7 @@ def test_quicksort_in_place(self): self.assertEqual(self.correct, self.output) def test_partition(self): - self.seq = range(10) + self.seq = list(range(10)) self.assertIs(quick_sort_in_place.partition(self.seq, 0, len(self.seq)-1, 5), 5) From 0560efed01b43a92fc9f77fcd999669e1abafca6 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Fri, 25 Sep 2015 18:47:00 -0700 Subject: [PATCH 53/89] Update setup.py to use find_packages - Add metadata about Python 3 version being supported --- setup.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index c40ebca..a0ff991 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ -from setuptools import setup +from setuptools import find_packages, setup -#Read in the README for the long description on PyPI + +# Read in the README for the long description on PyPI def long_description(): with open('README.rst', 'r') as f: readme = unicode(f.read()) @@ -14,8 +15,11 @@ def long_description(): author='Nic Young', author_email='nryoung@gmail.com', license='BSD', - packages=['algorithms', 'algorithms.data_structure', 'algorithms.dynamic_programming', 'algorithms.sorting', 'algorithms.shuffling', - 'algorithms.searching', 'algorithms.math', 'algorithms.tests'], + packages=find_packages(), classifiers=[ - 'Programming Language :: Python :: 2.7',], + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ], zip_safe=False) From a76fe1e9d0003a83790eec2e2f2a40724b6258e7 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Fri, 25 Sep 2015 22:53:27 -0700 Subject: [PATCH 54/89] Integrate flake8 with Travis CI, fixes #99 - Fix all flake8 errors - Add back requirements since they got wiped some how - Open README with utf-8 in setup.py --- .travis.yml | 5 +- .../data_structure/binary_search_tree.py | 710 +++++++++--------- algorithms/data_structure/digraph.py | 174 ++--- algorithms/data_structure/queue.py | 18 +- .../data_structure/singly_linked_list.py | 115 +-- algorithms/data_structure/stack.py | 18 +- algorithms/data_structure/undirected_graph.py | 148 ++-- algorithms/data_structure/union_find.py | 13 +- .../data_structure/union_find_by_rank.py | 12 +- .../union_find_with_path_compression.py | 17 +- algorithms/dynamic_programming/lcs.py | 8 +- algorithms/math/approx_cdf.py | 15 +- algorithms/math/primality_test.py | 8 +- algorithms/math/sieve_atkin.py | 21 +- algorithms/math/sieve_eratosthenes.py | 11 +- algorithms/math/std_normal_pdf.py | 9 +- algorithms/random/mersenne_twister.py | 12 +- algorithms/searching/depth_first_search.py | 10 +- algorithms/sorting/gnome_sort.py | 3 +- algorithms/sorting/quick_sort_in_place.py | 6 +- algorithms/tests/test_data_structure.py | 163 ++-- algorithms/tests/test_dynamic_programming.py | 5 +- algorithms/tests/test_math.py | 58 +- algorithms/tests/test_random.py | 37 +- algorithms/tests/test_searching.py | 115 +-- algorithms/tests/test_sorting.py | 31 +- requirements.txt | 5 + setup.cfg | 2 + setup.py | 4 +- 29 files changed, 947 insertions(+), 806 deletions(-) create mode 100644 setup.cfg diff --git a/.travis.yml b/.travis.yml index bf846ff..74acec6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,5 +7,8 @@ python: - "3.5" # command to install dependencies install: "pip install -r requirements.txt" +# run the linter +before_script: + - "flake8 ." # command to run tests -script: nosetests +script: "nosetests" diff --git a/algorithms/data_structure/binary_search_tree.py b/algorithms/data_structure/binary_search_tree.py index ed132f8..1efd4a8 100644 --- a/algorithms/data_structure/binary_search_tree.py +++ b/algorithms/data_structure/binary_search_tree.py @@ -1,359 +1,365 @@ """ - Binary Search Tree data structure implemented: - -------------------------------- - The Binary Search Tree represents an ordered symbol table of generic - key-value pairs. Keys must be comparable. Does not permit duplicate keys. - When assocating a value with a key already present in the BST, the previous - value is replaced by the new one. This implementation is for an unbalanced - BST. - - It supports the following primary operations: - Method Description - ----------------------------------------- - size Return size of BST - get Retrieve value for key in BST - put Add key-value pair to BST - contains Check if key is in BST - is_empty Check if BST is empty - min_key Get the minimum key in BST - max_key Get the maximum key in BST - floor_key Get the biggest key that is less than or equal to key - ceiling_key Get the smallest key that is greater than or equal to key - rank Get get the number of keys less than key - select_key Get the key with a given rank - delete_min Delete the key-value pair with minimum key from BST - delete_max Delete the key-value pair with maximum key from BST - delete Delete key-value pair with given key from BST - keys Get all keys in BST in ascending order - - Method Worst Case Balanced Tree - ----------------------------------------- - size O(1) O(1) - get O(N) O(lg N) - put O(N) O(lg N) - contains O(N) O(lg N) - is_empty O(1) O(1) - min_key O(N) O(lg N) - max_key O(N) O(lg N) - floor_key O(N) O(lg N) - ceiling_key O(N) O(lg N) - rank O(N) O(lg N) - select_key O(N) O(lg N) - delete_min O(N) O(lg N) - delete_max O(N) O(lg N) - delete O(N) O(lg N) - keys O(N) O(N) - - Adapted from: http://algs4.cs.princeton.edu/32bst + Binary Search Tree data structure implemented: + -------------------------------- + The Binary Search Tree represents an ordered symbol table of generic + key-value pairs. Keys must be comparable. Does not permit duplicate keys. + When assocating a value with a key already present in the BST, the previous + value is replaced by the new one. This implementation is for an unbalanced + BST. + + It supports the following primary operations: + Method Description + ----------------------------------------- + size Return size of BST + get Retrieve value for key in BST + put Add key-value pair to BST + contains Check if key is in BST + is_empty Check if BST is empty + min_key Get the minimum key in BST + max_key Get the maximum key in BST + floor_key Get the biggest key that is less than or equal to key + ceiling_key Get the smallest key that is greater than or equal to key + rank Get get the number of keys less than key + select_key Get the key with a given rank + delete_min Delete the key-value pair with minimum key from BST + delete_max Delete the key-value pair with maximum key from BST + delete Delete key-value pair with given key from BST + keys Get all keys in BST in ascending order + + Method Worst Case Balanced Tree + ----------------------------------------- + size O(1) O(1) + get O(N) O(lg N) + put O(N) O(lg N) + contains O(N) O(lg N) + is_empty O(1) O(1) + min_key O(N) O(lg N) + max_key O(N) O(lg N) + floor_key O(N) O(lg N) + ceiling_key O(N) O(lg N) + rank O(N) O(lg N) + select_key O(N) O(lg N) + delete_min O(N) O(lg N) + delete_max O(N) O(lg N) + delete O(N) O(lg N) + keys O(N) O(N) + + Adapted from: http://algs4.cs.princeton.edu/32bst """ + + class Node: - def __init__(self, key=None, val=None, size_of_subtree=1): - self.key = key - self.val = val - self.size_of_subtree = size_of_subtree - self.left = None - self.right = None + def __init__(self, key=None, val=None, size_of_subtree=1): + self.key = key + self.val = val + self.size_of_subtree = size_of_subtree + self.left = None + self.right = None + class BinarySearchTree: - def __init__(self): - self.root = None - - - def _size(self, node): - if node == None: - return 0 - else: - return node.size_of_subtree - - def size(self): - ''' - Return the number of nodes in the BST - ''' - return self._size(self.root) - - def is_empty(self): - ''' - Returns True if the BST is empty, False otherwise - ''' - return self.size() == 0 - - def _get(self, key, node): - if node == None: - return None - - if key < node.key: - return self._get(key, node.left) - elif key > node.key: - return self._get(key, node.right) - else: - return node.val - - def get(self, key): - ''' - Return the value paired with 'key' - ''' - return self._get(key, self.root) - - def contains(self, key): - ''' - Returns True if the BST contains 'key', False otherwise - ''' - return self.get(key) != None - - def _put(self, key, val, node): - - # If we hit the end of a branch, create a new node - if node == None: - return Node(key, val) - - # Follow left branch - if key < node.key: - node.left = self._put(key, val, node.left) - # Follow right branch - elif key > node.key: - node.right = self._put(key, val, node.right) - # Overwrite value - else: - node.val = val - - node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 - return node - - def put(self, key, val): - ''' - Add a new key-value pair. - ''' - self.root = self._put(key, val, self.root) - - def _min_node(self): - ''' - Return the node with the minimum key in the BST - ''' - min_node = self.root - # Return none if empty BST - if min_node == None: return None - - while min_node.left != None: - min_node = min_node.left - - return min_node - - def min_key(self): - ''' - Return the minimum key in the BST - ''' - min_node = self._min_node() - if min_node == None: - return None - else: - return min_node.key - - def _max_node(self): - ''' - Return the node with the maximum key in the BST - ''' - max_node = self.root - # Return none if empty BST - if max_node == None: return None - - while max_node.right != None: - max_node = max_node.right - - return max_node - - def max_key(self): - ''' - Return the maximum key in the BST - ''' - max_node = self._max_node() - if max_node == None: - return None - else: - return max_node.key - - def _floor_node(self, key, node): - ''' - Returns the node with the biggest key that is less than or equal to the - given value 'key' - ''' - if node == None: return None - - if key < node.key: - # Floor must be in left subtree - return self._floor_node(key, node.left) - - elif key > node.key: - # Floor is either in right subtree or is this node - attempt_in_right = self._floor_node(key, node.right) - if attempt_in_right == None: - return node - else: - return attempt_in_right - - else: - # Keys are equal so floor is node with this key - return node - - def floor_key(self, key): - ''' - Returns the biggest key that is less than or equal to the given value - 'key' - ''' - floor_node = self._floor_node(key, self.root) - if floor_node == None: - return None - else: - return floor_node.key - - def _ceiling_node(self, key, node): - ''' - Returns the node with the smallest key that is greater than or equal to - the given value 'key' - ''' - if node == None: - return None - - if key < node.key: - # Ceiling is either in left subtree or is this node - attempt_in_left = self._ceiling_node(key, node.left) - if attempt_in_left == None: - return node - else: - return attempt_in_left - elif key > node.key: - # Ceiling must be in right subtree - return self._ceiling_node(key, node.right) - else: - # Keys are equal so ceiling is node with this key - return node - - def ceiling_key(self, key): - ''' - Returns the smallest key that is greater than or equal to the given - value 'key' - ''' - ceiling_node = self._ceiling_node(key, self.root) - if ceiling_node == None: - return None - else: - return ceiling_node.key - - def _select_node(self, rank, node): - ''' - Return the node with rank equal to 'rank' - ''' - if node == None: - return None - - left_size = self._size(node.left) - if left_size < rank: - return self._select_node(rank - left_size - 1, node.right) - elif left_size > rank: - return self._select_node(rank, node.left) - else: - return node - - def select_key(self, rank): - ''' - Return the key with rank equal to 'rank' - ''' - select_node = self._select_node(rank, self.root) - if select_node == None: - return None - else: - return select_node.key - - def _rank(self, key, node): - if node == None: return None - - if key < node.key: - return self._rank(key, node.left) - elif key > node.key: - return self._size(node.left) + self._rank(key, node.right) + 1 - - else: - return self._size(node.left) - - def rank(self, key): - ''' - Return the number of keys less than a given 'key'. - ''' - return self._rank(key, self.root) - - def _delete(self, key, node): - if node == None: - return None - if key < node.key: - node.left = self._delete(key, node.left) - elif key > node.key: - node.right = self._delete(key, node.right) - - else: - if node.right == None: - return node.left - elif node.left == None: - return node.right - else: - old_node = node - node = self._ceiling_node(key, node.right) - node.right = self._delete_min(old_node.right) - node.left = old_node.left - node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 - return node - - def delete(self, key): - ''' - Remove the node with key equal to 'key' - ''' - self.root = self._delete(key, self.root) - - def _delete_min(self, node): - if node.left == None: - return node.right - - node.left = self._delete_min(node.left) - node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 - return node - - def delete_min(self): - ''' - Remove the key-value pair with the smallest key. - ''' - self.root = self._delete_min(self.root) - - def _delete_max(self, node): - if node.right == None: - return node.left - - node.right = self._delete_max(node.right) - node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 - return node - - def delete_max(self): - ''' - Remove the key-value pair with the largest key. - ''' - self.root = self._delete_max(self.root) - - def _keys(self, node, keys): - if node == None: - return keys - - if node.left != None: - keys = self._keys(node.left, keys) - - keys.append(node.key) - - if node.right != None: - keys = self._keys(node.right, keys) - - return keys - - def keys(self): - ''' - Return all of the keys in the BST in aschending order - ''' - keys = [] - return self._keys(self.root, keys) + def __init__(self): + self.root = None + + def _size(self, node): + if node is None: + return 0 + else: + return node.size_of_subtree + + def size(self): + ''' + Return the number of nodes in the BST + ''' + return self._size(self.root) + + def is_empty(self): + ''' + Returns True if the BST is empty, False otherwise + ''' + return self.size() == 0 + + def _get(self, key, node): + if node is None: + return None + + if key < node.key: + return self._get(key, node.left) + elif key > node.key: + return self._get(key, node.right) + else: + return node.val + + def get(self, key): + ''' + Return the value paired with 'key' + ''' + return self._get(key, self.root) + + def contains(self, key): + ''' + Returns True if the BST contains 'key', False otherwise + ''' + return self.get(key) is not None + + def _put(self, key, val, node): + + # If we hit the end of a branch, create a new node + if node is None: + return Node(key, val) + + # Follow left branch + if key < node.key: + node.left = self._put(key, val, node.left) + # Follow right branch + elif key > node.key: + node.right = self._put(key, val, node.right) + # Overwrite value + else: + node.val = val + + node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 + return node + + def put(self, key, val): + ''' + Add a new key-value pair. + ''' + self.root = self._put(key, val, self.root) + + def _min_node(self): + ''' + Return the node with the minimum key in the BST + ''' + min_node = self.root + # Return none if empty BST + if min_node is None: + return None + + while min_node.left is not None: + min_node = min_node.left + + return min_node + + def min_key(self): + ''' + Return the minimum key in the BST + ''' + min_node = self._min_node() + if min_node is None: + return None + else: + return min_node.key + + def _max_node(self): + ''' + Return the node with the maximum key in the BST + ''' + max_node = self.root + # Return none if empty BST + if max_node is None: + return None + + while max_node.right is not None: + max_node = max_node.right + + return max_node + + def max_key(self): + ''' + Return the maximum key in the BST + ''' + max_node = self._max_node() + if max_node is None: + return None + else: + return max_node.key + + def _floor_node(self, key, node): + ''' + Returns the node with the biggest key that is less than or equal to the + given value 'key' + ''' + if node is None: + return None + + if key < node.key: + # Floor must be in left subtree + return self._floor_node(key, node.left) + + elif key > node.key: + # Floor is either in right subtree or is this node + attempt_in_right = self._floor_node(key, node.right) + if attempt_in_right is None: + return node + else: + return attempt_in_right + + else: + # Keys are equal so floor is node with this key + return node + + def floor_key(self, key): + ''' + Returns the biggest key that is less than or equal to the given value + 'key' + ''' + floor_node = self._floor_node(key, self.root) + if floor_node is None: + return None + else: + return floor_node.key + + def _ceiling_node(self, key, node): + ''' + Returns the node with the smallest key that is greater than or equal to + the given value 'key' + ''' + if node is None: + return None + + if key < node.key: + # Ceiling is either in left subtree or is this node + attempt_in_left = self._ceiling_node(key, node.left) + if attempt_in_left is None: + return node + else: + return attempt_in_left + elif key > node.key: + # Ceiling must be in right subtree + return self._ceiling_node(key, node.right) + else: + # Keys are equal so ceiling is node with this key + return node + + def ceiling_key(self, key): + ''' + Returns the smallest key that is greater than or equal to the given + value 'key' + ''' + ceiling_node = self._ceiling_node(key, self.root) + if ceiling_node is None: + return None + else: + return ceiling_node.key + + def _select_node(self, rank, node): + ''' + Return the node with rank equal to 'rank' + ''' + if node is None: + return None + + left_size = self._size(node.left) + if left_size < rank: + return self._select_node(rank - left_size - 1, node.right) + elif left_size > rank: + return self._select_node(rank, node.left) + else: + return node + + def select_key(self, rank): + ''' + Return the key with rank equal to 'rank' + ''' + select_node = self._select_node(rank, self.root) + if select_node is None: + return None + else: + return select_node.key + + def _rank(self, key, node): + if node is None: + return None + + if key < node.key: + return self._rank(key, node.left) + elif key > node.key: + return self._size(node.left) + self._rank(key, node.right) + 1 + + else: + return self._size(node.left) + + def rank(self, key): + ''' + Return the number of keys less than a given 'key'. + ''' + return self._rank(key, self.root) + + def _delete(self, key, node): + if node is None: + return None + if key < node.key: + node.left = self._delete(key, node.left) + elif key > node.key: + node.right = self._delete(key, node.right) + + else: + if node.right is None: + return node.left + elif node.left is None: + return node.right + else: + old_node = node + node = self._ceiling_node(key, node.right) + node.right = self._delete_min(old_node.right) + node.left = old_node.left + node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 + return node + + def delete(self, key): + ''' + Remove the node with key equal to 'key' + ''' + self.root = self._delete(key, self.root) + + def _delete_min(self, node): + if node.left is None: + return node.right + + node.left = self._delete_min(node.left) + node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 + return node + + def delete_min(self): + ''' + Remove the key-value pair with the smallest key. + ''' + self.root = self._delete_min(self.root) + + def _delete_max(self, node): + if node.right is None: + return node.left + + node.right = self._delete_max(node.right) + node.size_of_subtree = self._size(node.left) + self._size(node.right)+1 + return node + + def delete_max(self): + ''' + Remove the key-value pair with the largest key. + ''' + self.root = self._delete_max(self.root) + + def _keys(self, node, keys): + if node is None: + return keys + + if node.left is not None: + keys = self._keys(node.left, keys) + + keys.append(node.key) + + if node.right is not None: + keys = self._keys(node.right, keys) + + return keys + + def keys(self): + ''' + Return all of the keys in the BST in aschending order + ''' + keys = [] + return self._keys(self.root, keys) diff --git a/algorithms/data_structure/digraph.py b/algorithms/data_structure/digraph.py index a7d8696..9ab0a51 100644 --- a/algorithms/data_structure/digraph.py +++ b/algorithms/data_structure/digraph.py @@ -3,8 +3,8 @@ -------------------------------------------- The Digraph class represents a directed graph of vertices which can be any hashable value. - - It supports the following two primary operations: + + It supports the following two primary operations: add_edge: add an edge to the graph O(1) adj: return list of all of the vertices adjacent to a vertex O(1) vertices: return list of all vertices in the graph O(V) @@ -14,93 +14,93 @@ edge_count: return the number of edges O(1) degree: return degree of the vertex O(1) reverse: return a reversed version of the digraph O(V+E) - + Parallel edges and self-loops are permitted. Adapted from: http://algs4.cs.princeton.edu/42directed/Digraph.java.html """ -class Digraph() : - def __init__(self): - self.__adj = {} - self.__v_count = 0 - self.__e_count = 0 - - def vertex_count(self): - """ - Returns the number of vertices in the graph. - """ - - return self.__v_count - - def edge_count(self): - """ - Returns the number of edges in the graph. - """ - - return self.__e_count - - def add_edge(self, src, dest): - """ - Adds an undirected edge 'src'-'dest' to the graph. - """ - - if src in self.__adj: - self.__adj[src].append(dest) - else: - self.__adj[src] = [dest] - self.__v_count += 1 - - if dest in self.__adj: - pass - else: - self.__adj[dest] = [] - self.__v_count += 1 - - self.__e_count += 1 - - def adj(self, src): - """ - Returns the vertices adjacent to vertex 'src'. - """ - return self.__adj[src] - - def outdegree(self, src): - """ - Returns the degree of the vertex 'src' - """ - if src in self.__adj: - return len(self.__adj[src]) - else: - raise LookupError("This vertex is not in the graph.") - - def vertices(self): - """ - Returns an iterable of all the vertices in the graph. - """ - return self.__adj.keys() - - def reverse(self): - """ - Returns the reverse of this digraph - """ - digraph_reversed = Digraph() - old_vertices = self.vertices() - - for src in old_vertices: - for dest in self.adj(src): - digraph_reversed.add_edge(dest, src) - return digraph_reversed; - - - def __str__(self): - s = [] - s.append("{0} vertices and {1} edges \n".format(self.__v_count, - self.__e_count)) - for key in self.vertices(): - s.append("{0}: ".format(key)) - for val in self.adj(key): - s.append("{0} ".format(val)) - s.append("\n") - - return "".join(s) + +class Digraph(): + def __init__(self): + self.__adj = {} + self.__v_count = 0 + self.__e_count = 0 + + def vertex_count(self): + """ + Returns the number of vertices in the graph. + """ + + return self.__v_count + + def edge_count(self): + """ + Returns the number of edges in the graph. + """ + + return self.__e_count + + def add_edge(self, src, dest): + """ + Adds an undirected edge 'src'-'dest' to the graph. + """ + + if src in self.__adj: + self.__adj[src].append(dest) + else: + self.__adj[src] = [dest] + self.__v_count += 1 + + if dest in self.__adj: + pass + else: + self.__adj[dest] = [] + self.__v_count += 1 + + self.__e_count += 1 + + def adj(self, src): + """ + Returns the vertices adjacent to vertex 'src'. + """ + return self.__adj[src] + + def outdegree(self, src): + """ + Returns the degree of the vertex 'src' + """ + if src in self.__adj: + return len(self.__adj[src]) + else: + raise LookupError("This vertex is not in the graph.") + + def vertices(self): + """ + Returns an iterable of all the vertices in the graph. + """ + return self.__adj.keys() + + def reverse(self): + """ + Returns the reverse of this digraph + """ + digraph_reversed = Digraph() + old_vertices = self.vertices() + + for src in old_vertices: + for dest in self.adj(src): + digraph_reversed.add_edge(dest, src) + return digraph_reversed + + def __str__(self): + s = [] + s.append("{0} vertices and {1} edges \n".format(self.__v_count, + self.__e_count)) + for key in self.vertices(): + s.append("{0}: ".format(key)) + for val in self.adj(key): + s.append("{0} ".format(val)) + s.append("\n") + + return "".join(s) diff --git a/algorithms/data_structure/queue.py b/algorithms/data_structure/queue.py index e2325a7..fb3a200 100644 --- a/algorithms/data_structure/queue.py +++ b/algorithms/data_structure/queue.py @@ -1,30 +1,32 @@ """ Queue data structure implemented: -------------------------------- - add : add element at last + add : add element at last remove : remove element from front - return value + return value is_empty : 1 value returned on empty 0 value returned on not empty size : return size of queue Time Complexity: O(1) """ - from collections import deque -class queue : + +class Queue: queue_list = deque([]) + def __init__(self): self.queue_list = deque([]) - def add(self,value): + + def add(self, value): self.queue_list.append(value) + def remove(self): return self.queue_list.popleft() + def is_empty(self): return not len(self.queue_list) + def size(self): return len(self.queue_list) - - - diff --git a/algorithms/data_structure/singly_linked_list.py b/algorithms/data_structure/singly_linked_list.py index 87cb40d..33b7842 100644 --- a/algorithms/data_structure/singly_linked_list.py +++ b/algorithms/data_structure/singly_linked_list.py @@ -1,74 +1,77 @@ """ Singly Linked List data structure implemented: -------------------------------- - add : add element to list + add : add element to list remove : remove element from list search : search for value in list size : return size of list Time Complexity: O(N) """ + + class Node: - def __init__(self, data=None, next=None): - self.data = data - self.next = next + def __init__(self, data=None, next=None): + self.data = data + self.next = next + + def setData(self, data): + self.data = data - def setData(self, data): - self.data = data + def getData(self): + return self.data - def getData(self): - return self.data + def setNext(self, next): + self.next = next - def setNext(self, next): - self.next = next + def getNext(self): + return self.next - def getNext(self): - return self.next class SinglyLinkedList: - def __init__(self): - self.head = None - self.size = 0 - - def add(self, value): - node = Node(value) - node.setNext(self.head) - self.head = node - self.size += 1 - - def remove(self, value): - current = self.head - previous = None - found = False - - while not found: - if current.data == value: - found = True - self.size-=1 - else: - previous = current - current = current.next - - if previous == None: # Head node - self.head = current.next - else: # None head node - previous.setNext(current.next) - - return found - - def search(self, value): - current = self.head - found = False - - while current and not found: - if current.getData() == value: - found = True - else: - current = current.next - - return found - - def size(self): - return self.size \ No newline at end of file + def __init__(self): + self.head = None + self.size = 0 + + def add(self, value): + node = Node(value) + node.setNext(self.head) + self.head = node + self.size += 1 + + def remove(self, value): + current = self.head + previous = None + found = False + + while not found: + if current.data == value: + found = True + self.size -= 1 + else: + previous = current + current = current.next + + if previous is None: # Head node + self.head = current.next + else: # None head node + previous.setNext(current.next) + + return found + + def search(self, value): + current = self.head + found = False + + while current and not found: + if current.getData() == value: + found = True + else: + current = current.next + + return found + + def size(self): + return self.size diff --git a/algorithms/data_structure/stack.py b/algorithms/data_structure/stack.py index e877e42..fc05194 100644 --- a/algorithms/data_structure/stack.py +++ b/algorithms/data_structure/stack.py @@ -1,9 +1,9 @@ """ Stack data structure implemented: -------------------------------- - add : add element at last + add : add element at last remove : remove element from last - return value + return value is_empty : 1 value returned on empty 0 value returned on not empty size : return size of stack @@ -11,17 +11,21 @@ Time Complexity: O(1) """ -class stack : + +class Stack: stack_list = [] + def __init__(self): - self.stack_list = [] - def add(self,value): + self.stack_list = [] + + def add(self, value): self.stack_list.append(value) + def remove(self): return self.stack_list.pop() + def is_empty(self): return not len(self.stack_list) + def size(self): return len(self.stack_list) - - diff --git a/algorithms/data_structure/undirected_graph.py b/algorithms/data_structure/undirected_graph.py index b4544ac..458100f 100644 --- a/algorithms/data_structure/undirected_graph.py +++ b/algorithms/data_structure/undirected_graph.py @@ -3,8 +3,8 @@ -------------------------------------------- The Undirected_Graph class represents an undirected graph of vertices which can be any hashable value. - - It supports the following two primary operations: + + It supports the following two primary operations: add_edge: add an edge to the graph O(1) adj: return list of all of the vertices adjacent to a vertex O(1) vertices: return list of all vertices in the graph O(V) @@ -13,80 +13,80 @@ vertex_count: return the number of vertices O(1) edge_count: return the number of edges O(1) degree: return degree of the vertex O(1) - + Parallel edges and self-loops are permitted. Adapted from: http://algs4.cs.princeton.edu/41undirected/Graph.java.html """ -class Undirected_Graph : - def __init__(self): - self.__adj = {} - self.__v_count = 0 - self.__e_count = 0 - - def vertex_count(self): - """ - Returns the number of vertices in the graph. - """ - - return self.__v_count - - def edge_count(self): - """ - Returns the number of edges in the graph. - """ - - return self.__e_count - - def add_edge(self, src, dest): - """ - Adds an undirected edge 'src'-'dest' to the graph. - """ - if src in self.__adj: - self.__adj[src].append(dest) - else: - self.__adj[src] = [dest] - self.__v_count += 1 - - - if dest in self.__adj: - self.__adj[dest].append(src) - else: - self.__adj[dest] = [src] - self.__v_count += 1 - - self.__e_count += 1 - - def adj(self, src): - """ - Returns the vertices adjacent to vertex 'src'. - """ - return self.__adj[src] - - def degree(self, src): - """ - Returns the degree of the vertex 'src' - """ - if src in self.__adj: - return len(self.__adj[src]) - else: - raise LookupError("This vertex is not in the graph.") - - def vertices(self): - """ - Returns an iterable of all the vertices in the graph. - """ - return self.__adj.keys() - - def __str__(self): - s = [] - s.append("{0} vertices and {1} edges \n".format(self.__v_count, - self.__e_count)) - for key in self.vertices(): - s.append("{0}: ".format(key)) - for val in self.adj(key): - s.append("{0} ".format(val)) - s.append("\n") - - return "".join(s) \ No newline at end of file + +class Undirected_Graph: + def __init__(self): + self.__adj = {} + self.__v_count = 0 + self.__e_count = 0 + + def vertex_count(self): + """ + Returns the number of vertices in the graph. + """ + + return self.__v_count + + def edge_count(self): + """ + Returns the number of edges in the graph. + """ + + return self.__e_count + + def add_edge(self, src, dest): + """ + Adds an undirected edge 'src'-'dest' to the graph. + """ + if src in self.__adj: + self.__adj[src].append(dest) + else: + self.__adj[src] = [dest] + self.__v_count += 1 + + if dest in self.__adj: + self.__adj[dest].append(src) + else: + self.__adj[dest] = [src] + self.__v_count += 1 + + self.__e_count += 1 + + def adj(self, src): + """ + Returns the vertices adjacent to vertex 'src'. + """ + return self.__adj[src] + + def degree(self, src): + """ + Returns the degree of the vertex 'src' + """ + if src in self.__adj: + return len(self.__adj[src]) + else: + raise LookupError("This vertex is not in the graph.") + + def vertices(self): + """ + Returns an iterable of all the vertices in the graph. + """ + return self.__adj.keys() + + def __str__(self): + s = [] + s.append("{0} vertices and {1} edges \n".format(self.__v_count, + self.__e_count)) + for key in self.vertices(): + s.append("{0}: ".format(key)) + for val in self.adj(key): + s.append("{0} ".format(val)) + s.append("\n") + + return "".join(s) diff --git a/algorithms/data_structure/union_find.py b/algorithms/data_structure/union_find.py index b77e29f..b019fd0 100644 --- a/algorithms/data_structure/union_find.py +++ b/algorithms/data_structure/union_find.py @@ -3,13 +3,16 @@ A Naive Implementation of union find data structure. Union Find Overview: ------------------------ - A disjoint-set data structure, also called union-find data structure implements two functions: + A disjoint-set data structure, also called union-find data structure + implements two functions: union(A, B) - merge A's set with B's set find(A) - finds what set A belongs to Navie approach: Find follows parent nodes until it reaches the root. - Union combines two trees into one by attaching the root of one to the root of the other - Time Complexity : O(N) (a highly unbalanced tree might be created, nothing better a linked-list) + Union combines two trees into one by attaching the root of one to the + root of the other + Time Complexity : O(N) (a highly unbalanced tree might be created, + nothing better a linked-list) Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure """ @@ -29,7 +32,9 @@ def make_set(self, x): if type(x) != int: raise TypeError("x must be integer") if x != self.__N: - raise ValueError("a new element must have index {0} since the total num of elements is {0}".format(self.__N)) + raise ValueError( + "a new element must have index {0}".format(self.__N) + ) self.__parent.append(x) self.__N = self.__N + 1 diff --git a/algorithms/data_structure/union_find_by_rank.py b/algorithms/data_structure/union_find_by_rank.py index ec00258..0c5190b 100644 --- a/algorithms/data_structure/union_find_by_rank.py +++ b/algorithms/data_structure/union_find_by_rank.py @@ -3,7 +3,8 @@ An implementation of union find by rank data structure. Union Find Overview: ------------------------ - A disjoint-set data structure, also called union-find data structure implements two functions: + A disjoint-set data structure, also called union-find data structure + implements two functions: union(A, B) - merge A's set with B's set find(A) - finds what set A belongs to Union by rank approach: @@ -11,6 +12,8 @@ Time Complexity : O(logn) Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure """ + + class UnionFindByRank: def __init__(self, N): if type(N) != int: @@ -19,7 +22,7 @@ def __init__(self, N): raise ValueError("N cannot be a negative integer") self.__parent = [] self.__rank = [] - self.__N = N + self.__N = N for i in range(0, N): self.__parent.append(i) self.__rank.append(0) @@ -28,7 +31,9 @@ def make_set(self, x): if type(x) != int: raise TypeError("x must be integer") if x != self.__N: - raise ValueError("a new element must have index {0} since the total num of elements is {0}".format(self.__N)) + raise ValueError( + "a new element must have index {0}".format(self.__N) + ) self.__parent.append(x) self.__rank.append(0) self.__N = self.__N + 1 @@ -66,4 +71,3 @@ def __validate_ele(self, x): raise TypeError("{0} is not an integer".format(x)) if x < 0 or x >= self.__N: raise ValueError("{0} is not in [0,{1})".format(x, self.__N)) - diff --git a/algorithms/data_structure/union_find_with_path_compression.py b/algorithms/data_structure/union_find_with_path_compression.py index a23fed9..a183b92 100644 --- a/algorithms/data_structure/union_find_with_path_compression.py +++ b/algorithms/data_structure/union_find_with_path_compression.py @@ -3,15 +3,20 @@ An implementation of union find with path compression data structure. Union Find Overview: ------------------------ - A disjoint-set data structure, also called union-find data structure implements two functions: + A disjoint-set data structure, also called union-find data structure + implements two functions: union(A, B) - merge A's set with B's set find(A) - finds what set A belongs to Union with path compression approach: - Each node visited on the way to a root node may as well be attached directly to the root node. + Each node visited on the way to a root node may as well be attached + directly to the root node. attach the smaller tree to the root of the larger tree - Time Complexity : O(a(n)), where a(n) is the inverse of the function n=f(x)=A(x,x) and A is the extremely fast-growing Ackermann function. + Time Complexity : O(a(n)), where a(n) is the inverse of the function + n=f(x)=A(x,x) and A is the extremely fast-growing Ackermann function. Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure """ + + class UnionFindWithPathCompression: def __init__(self, N): if type(N) != int: @@ -20,7 +25,7 @@ def __init__(self, N): raise ValueError("N cannot be a negative integer") self.__parent = [] self.__rank = [] - self.__N = N + self.__N = N for i in range(0, N): self.__parent.append(i) self.__rank.append(0) @@ -29,7 +34,8 @@ def make_set(self, x): if type(x) != int: raise TypeError("x must be integer") if x != self.__N: - raise ValueError("a new element must have index {0} since the total num of elements is {0}".format(self.__N)) + raise ValueError( + "a new element must have index {0}".format(self.__N)) self.__parent.append(x) self.__rank.append(0) self.__N = self.__N + 1 @@ -76,4 +82,3 @@ def __validate_ele(self, x): raise TypeError("{0} is not an integer".format(x)) if x < 0 or x >= self.__N: raise ValueError("{0} is not in [0,{1})".format(x, self.__N)) - diff --git a/algorithms/dynamic_programming/lcs.py b/algorithms/dynamic_programming/lcs.py index 9336a38..b8c81f0 100644 --- a/algorithms/dynamic_programming/lcs.py +++ b/algorithms/dynamic_programming/lcs.py @@ -7,9 +7,11 @@ Pre: two strings str1 and str2 Post: a string representing the longest subsequence common to str1 and str2 - Pseudo Code: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem + Pseudo Code: + http://en.wikipedia.org/wiki/Longest_common_subsequence_problem """ + def build_lengths_matrix(str1, str2): matrix = [[0 for j in range(len(str2)+1)] for i in range(len(str1)+1)] for i, x in enumerate(str1): @@ -20,6 +22,7 @@ def build_lengths_matrix(str1, str2): matrix[i+1][j+1] = max(matrix[i+1][j], matrix[i][j+1]) return matrix + def read_from_matrix(matrix, str1, str2): result = "" i, j = len(str1), len(str2) @@ -34,6 +37,7 @@ def read_from_matrix(matrix, str1, str2): j -= 1 return result[::-1] + def lcs(str1, str2): lengths = build_lengths_matrix(str1, str2) - return read_from_matrix(lengths, str1, str2) \ No newline at end of file + return read_from_matrix(lengths, str1, str2) diff --git a/algorithms/math/approx_cdf.py b/algorithms/math/approx_cdf.py index 23d3ece..f92417a 100644 --- a/algorithms/math/approx_cdf.py +++ b/algorithms/math/approx_cdf.py @@ -1,22 +1,23 @@ -""" - Calculates the cumulative distribution function (CDF) +""" + Calculates the cumulative distribution function (CDF) of the normal distribution based on an approximation by George Marsaglia: - Marsaglia, George (2004). "Evaluating the Normal Distribution". + Marsaglia, George (2004). "Evaluating the Normal Distribution". Journal of Statistical Software 11 (4). - + 16 digit precision for 300 iterations when x = 10. - Equation: + Equation: f(x) = 1/2 + pdf(x) * (x + (x^3/3) + (x^5/3*5) + (x^7/3*7) + ...) """ from algorithms.math import std_normal_pdf -def cdf(x, iterations = 300): + +def cdf(x, iterations=300): product = 1.0 taylor_exp = [x] - for i in range (3,iterations,2): + for i in range(3, iterations, 2): product *= i taylor_exp.append(float(x**i)/product) taylor_fact = sum(taylor_exp) diff --git a/algorithms/math/primality_test.py b/algorithms/math/primality_test.py index 036ec5a..769204a 100644 --- a/algorithms/math/primality_test.py +++ b/algorithms/math/primality_test.py @@ -1,5 +1,8 @@ from math import sqrt + from algorithms.math.sieve_eratosthenes import eratosthenes + + CACHE_LIMIT = 10 ** 6 primes_cache_list = [] primes_cache_bool = [] @@ -10,7 +13,9 @@ def is_prime(number, cache=True): return False global primes_cache_list, primes_cache_bool if cache and len(primes_cache_list) == 0: - primes_cache_list,primes_cache_bool = eratosthenes(CACHE_LIMIT, return_boolean=True) + primes_cache_list, primes_cache_bool = eratosthenes( + CACHE_LIMIT, return_boolean=True + ) for prime in primes_cache_list: primes_cache_bool[prime] = True if number < len(primes_cache_bool): @@ -31,4 +36,3 @@ def is_prime(number, cache=True): return False to_check += 1 return True - diff --git a/algorithms/math/sieve_atkin.py b/algorithms/math/sieve_atkin.py index be30bc7..f678a91 100644 --- a/algorithms/math/sieve_atkin.py +++ b/algorithms/math/sieve_atkin.py @@ -5,17 +5,18 @@ Sieve of Atkin Overview: ------------------------ - It is an optimized version of the ancient sieve of Eratosthenes - which does some preliminary work and then marks off - multiples of the square of each prime, rather than multiples of the prime itself. - It was created in 2004 by A. O. L. Atkin and Daniel J. Bernstein. + It is an optimized version of the ancient sieve of Eratosthenes + which does some preliminary work and then marks off + multiples of the square of each prime, rather than multiples of the prime + itself. It was created in 2004 by A. O. L. Atkin and Daniel J. Bernstein. Time Complexity: O(n/log log n) - Pseudocode: https://en.wikipedia.org/wiki/Sieve_of_Atkin + Pseudocode: https://en.wikipedia.org/wiki/Sieve_of_Atkin """ from math import sqrt + def atkin(limit): if limit == 2: return [2] @@ -28,9 +29,9 @@ def atkin(limit): primes = [2, 3, 5] is_prime = [False] * (limit + 1) sqrt_limit = int(sqrt(limit)) + 1 - - for x in range(1,sqrt_limit): - for y in range(1,sqrt_limit): + + for x in range(1, sqrt_limit): + for y in range(1, sqrt_limit): n = 4 * x ** 2 + y ** 2 if n <= limit and (n % 12 == 1 or n % 12 == 5): is_prime[n] = not is_prime[n] @@ -40,8 +41,8 @@ def atkin(limit): n = 3 * x ** 2 - y ** 2 if x > y and (n <= limit) and (n % 12 == 11): is_prime[n] = not is_prime[n] - - for index in range(5,sqrt_limit): + + for index in range(5, sqrt_limit): if is_prime[index]: for composite in range(index ** 2, limit, index ** 2): is_prime[composite] = False diff --git a/algorithms/math/sieve_eratosthenes.py b/algorithms/math/sieve_eratosthenes.py index 28a04f2..e4f1ff8 100644 --- a/algorithms/math/sieve_eratosthenes.py +++ b/algorithms/math/sieve_eratosthenes.py @@ -5,16 +5,17 @@ Sieve of Eratosthenes Overview: ------------------------ - Is a simple, ancient algorithm for finding all prime numbers - up to any given limit. It does so by iteratively marking as composite (i.e. not prime) - the multiples of each prime, starting with the multiples of 2. + Is a simple, ancient algorithm for finding all prime numbers + up to any given limit. It does so by iteratively marking as composite + (i.e. not prime) the multiples of each prime, starting with the multiples + of 2. - The sieve of Eratosthenes is one of the most efficient ways + The sieve of Eratosthenes is one of the most efficient ways to find all of the smaller primes (below 10 million or so). Time Complexity: O(n log log n) - Pseudocode: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + Pseudocode: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes """ diff --git a/algorithms/math/std_normal_pdf.py b/algorithms/math/std_normal_pdf.py index 62ef51f..dfa4b74 100644 --- a/algorithms/math/std_normal_pdf.py +++ b/algorithms/math/std_normal_pdf.py @@ -1,10 +1,10 @@ """ - Calculates the normal distribution's probability density + Calculates the normal distribution's probability density function (PDF). Calculates Standard normal pdf for mean=0, std_dev=1. Equation: - f(x) = 1 / sqrt(2*pi) * e^(-(x-mean)^2/ 2*std_dev^2) + f(x) = 1 / sqrt(2*pi) * e^(-(x-mean)^2/ 2*std_dev^2) """ @@ -12,8 +12,7 @@ def pdf(x, mean=0, std_dev=1): PI = 3.141592653589793 E = 2.718281828459045 - term1 = 1.0 / ( (2 * PI)**0.5 ) - term2 = E**( -1.0* (x-mean)**2.0 / 2.0*(std_dev**2.0) ) + term1 = 1.0 / ((2 * PI)**0.5) + term2 = E**(-1.0*(x-mean)**2.0 / 2.0*(std_dev**2.0)) return term1 * term2 - diff --git a/algorithms/random/mersenne_twister.py b/algorithms/random/mersenne_twister.py index 608747e..631e5bd 100644 --- a/algorithms/random/mersenne_twister.py +++ b/algorithms/random/mersenne_twister.py @@ -30,27 +30,25 @@ def seed(self, seed): n &= 0xffffffff self.state.append(n) - def randint(self): """Extract random number""" if self.index == 0: self.generate() - + y = self.state[self.index] y ^= y >> 11 y ^= (y << 7) & 0x9d2c5680 y ^= (y << 15) & 0xefc60000 y ^= y >> 18 - + self.index = (self.index + 1) % 624 return y - def generate(self): """Generate 624 new random numbers""" for i in range(624): n = self.state[i] & 0x80000000 - n += self.state[(i+1)%624] & 0x7fffffff - self.state[i] = self.state[(i+397)%624] ^ (n >> 1) - if n%2 != 0: + n += self.state[(i+1) % 624] & 0x7fffffff + self.state[i] = self.state[(i+397) % 624] ^ (n >> 1) + if n % 2 != 0: self.state[i] ^= 0x9908b0df diff --git a/algorithms/searching/depth_first_search.py b/algorithms/searching/depth_first_search.py index 336e743..e1445ed 100644 --- a/algorithms/searching/depth_first_search.py +++ b/algorithms/searching/depth_first_search.py @@ -13,13 +13,15 @@ E = Number of edges V = Number of vertices (nodes) - Pseudocode: https://en.wikipedia.org/wiki/Depth-first_search + Pseudocode: https://en.wikipedia.org/wiki/Depth-first_search """ -def dfs(graph,start,path = []): - if start not in graph or graph[start] == None or graph[start] == []: + + +def dfs(graph, start, path=[]): + if start not in graph or graph[start] is None or graph[start] == []: return None path = path + [start] for edge in graph[start]: if edge not in path: - path = dfs(graph, edge,path) + path = dfs(graph, edge, path) return path diff --git a/algorithms/sorting/gnome_sort.py b/algorithms/sorting/gnome_sort.py index 5c41fdd..e55e70c 100644 --- a/algorithms/sorting/gnome_sort.py +++ b/algorithms/sorting/gnome_sort.py @@ -5,7 +5,8 @@ Gnome Sort Overview: --------------------- - A sorting algorithm similar to insertion sort except that the element is moved to its proper place by a series of swaps. + A sorting algorithm similar to insertion sort except that the element is + moved to its proper place by a series of swaps. Time Complexity: O(n^2) diff --git a/algorithms/sorting/quick_sort_in_place.py b/algorithms/sorting/quick_sort_in_place.py index 52141b7..2d943b1 100644 --- a/algorithms/sorting/quick_sort_in_place.py +++ b/algorithms/sorting/quick_sort_in_place.py @@ -19,23 +19,25 @@ """ from random import randrange + def partition(seq, left, right, pivot_index): pivot_value = seq[pivot_index] seq[pivot_index], seq[right] = seq[right], seq[pivot_index] store_index = left - for i in range( left, right ): + for i in range(left, right): if seq[i] < pivot_value: seq[i], seq[store_index] = seq[store_index], seq[i] store_index += 1 seq[store_index], seq[right] = seq[right], seq[store_index] return store_index + def sort(seq, left, right): """in-place version of quicksort""" if len(seq) <= 1: return seq elif left < right: - #pivot = (left+right)/2 + # pivot = (left+right)/2 pivot = randrange(left, right) pivot_new_index = partition(seq, left, right, pivot) sort(seq, left, pivot_new_index - 1) diff --git a/algorithms/tests/test_data_structure.py b/algorithms/tests/test_data_structure.py index 29cbb58..7646df5 100644 --- a/algorithms/tests/test_data_structure.py +++ b/algorithms/tests/test_data_structure.py @@ -1,41 +1,55 @@ +from random import shuffle import unittest -from ..data_structure import stack,queue,union_find,union_find_by_rank,union_find_with_path_compression,digraph,singly_linked_list, undirected_graph, binary_search_tree -from random import shuffle +from ..data_structure import ( + stack, + queue, + union_find, + union_find_by_rank, + union_find_with_path_compression, + digraph, + singly_linked_list, + undirected_graph, + binary_search_tree +) + + class TestStack(unittest.TestCase): """ Test Stack Implementation """ def test_stack(self): - self.sta = stack.stack() + self.sta = stack.Stack() self.sta.add(5) self.sta.add(8) self.sta.add(10) self.sta.add(2) - self.assertEqual(self.sta.remove(),2) - self.assertEqual(self.sta.is_empty(),False) - self.assertEqual(self.sta.size(),3) + self.assertEqual(self.sta.remove(), 2) + self.assertEqual(self.sta.is_empty(), False) + self.assertEqual(self.sta.size(), 3) + class TestQueue(unittest.TestCase): """ Test Queue Implementation """ def test_queue(self): - self.que = queue.queue() + self.que = queue.Queue() self.que.add(1) self.que.add(2) self.que.add(8) self.que.add(5) self.que.add(6) - self.assertEqual(self.que.remove(),1) - self.assertEqual(self.que.size(),4) - self.assertEqual(self.que.remove(),2) - self.assertEqual(self.que.remove(),8) - self.assertEqual(self.que.remove(),5) - self.assertEqual(self.que.remove(),6) - self.assertEqual(self.que.is_empty(),True) + self.assertEqual(self.que.remove(), 1) + self.assertEqual(self.que.size(), 4) + self.assertEqual(self.que.remove(), 2) + self.assertEqual(self.que.remove(), 8) + self.assertEqual(self.que.remove(), 5) + self.assertEqual(self.que.remove(), 6) + self.assertEqual(self.que.is_empty(), True) + class TestUnionFind(unittest.TestCase): """ @@ -52,6 +66,7 @@ def test_union_find(self): self.assertEqual(self.uf.is_connected(0, 1), True) self.assertEqual(self.uf.is_connected(3, 4), True) + class TestUnionFindByRank(unittest.TestCase): """ Test Union Find Implementation @@ -78,12 +93,16 @@ def test_union_find_by_rank(self): self.assertEqual(self.uf.is_connected(3, 4), True) self.assertEqual(self.uf.is_connected(5, 3), True) + class TestUnionFindWithPathCompression(unittest.TestCase): """ Test Union Find Implementation """ def test_union_find_with_path_compression(self): - self.uf = union_find_with_path_compression.UnionFindWithPathCompression(5) + self.uf = ( + union_find_with_path_compression + .UnionFindWithPathCompression(5) + ) self.uf.make_set(5) self.uf.union(0, 1) self.uf.union(2, 3) @@ -102,6 +121,7 @@ def test_union_find_with_path_compression(self): self.assertEqual(self.uf.is_connected(3, 5), True) + class TestSinglyLinkedList(unittest.TestCase): """ Test Singly Linked List Implementation @@ -116,12 +136,13 @@ def test_singly_linked_list(self): self.assertEqual(self.sl.size, 2) self.assertEqual(self.sl.search(30), False) - self.assertEqual(self.sl.search(5),True) + self.assertEqual(self.sl.search(5), True) self.assertEqual(self.sl.search(10), True) self.assertEqual(self.sl.remove(5), True) self.assertEqual(self.sl.remove(10), True) self.assertEqual(self.sl.size, 0) + class TestUndirectedGraph(unittest.TestCase): """ Test Undirected Graph Implementation @@ -137,12 +158,12 @@ def test_undirected_graph(self): # populating self.ug1.add_edge(1, 2) - self.ug2.add_edge(1,2) - self.ug2.add_edge(1,2) + self.ug2.add_edge(1, 2) + self.ug2.add_edge(1, 2) - self.ug3.add_edge(1,2) - self.ug3.add_edge(1,2) - self.ug3.add_edge(3,1) + self.ug3.add_edge(1, 2) + self.ug3.add_edge(1, 2) + self.ug3.add_edge(3, 1) # test adj self.assertTrue(2 in self.ug1.adj(1)) @@ -201,6 +222,7 @@ def test_undirected_graph(self): self.assertEqual(self.ug2.edge_count(), 2) self.assertEqual(self.ug3.edge_count(), 3) + class TestDirectedGraph(unittest.TestCase): """ Test Undirected Graph Implementation @@ -216,14 +238,14 @@ def test_directed_graph(self): # populating self.dg1.add_edge(1, 2) - self.dg1_rev = self.dg1.reverse() # reverse + self.dg1_rev = self.dg1.reverse() # reverse - self.dg2.add_edge(1,2) - self.dg2.add_edge(1,2) + self.dg2.add_edge(1, 2) + self.dg2.add_edge(1, 2) - self.dg3.add_edge(1,2) - self.dg3.add_edge(1,2) - self.dg3.add_edge(3,1) + self.dg3.add_edge(1, 2) + self.dg3.add_edge(1, 2) + self.dg3.add_edge(3, 1) # test adj self.assertTrue(2 in self.dg1.adj(1)) @@ -298,14 +320,16 @@ def test_directed_graph(self): self.assertEqual(self.dg2.edge_count(), 2) self.assertEqual(self.dg3.edge_count(), 3) + class TestBinarySearchTree(unittest.TestCase): """ Test Binary Search Tree Implementation """ - key_val = [("a", 1), ("b", 2), ("c", 3), - ("d", 4), ("e", 5), ("f", 6), - ("g", 7), ("h", 8), ("i", 9)] - + key_val = [ + ("a", 1), ("b", 2), ("c", 3), + ("d", 4), ("e", 5), ("f", 6), + ("g", 7), ("h", 8), ("i", 9) + ] def shuffle_list(self, ls): shuffle(ls) @@ -324,24 +348,23 @@ def test_size(self): self.bst.put("one", 2) self.assertEqual(self.bst.size(), 1) - self.bst = binary_search_tree.BinarySearchTree() size = 0 for pair in self.key_val: k, v = pair self.bst.put(k, v) size += 1 - self.assertEqual(self.bst.size(), size) - + self.assertEqual(self.bst.size(), size) + shuffled = self.shuffle_list(self.key_val[:]) - + self.bst = binary_search_tree.BinarySearchTree() size = 0 for pair in shuffled: k, v = pair self.bst.put(k, v) size += 1 - self.assertEqual(self.bst.size(), size) + self.assertEqual(self.bst.size(), size) def test_is_empty(self): self.bst = binary_search_tree.BinarySearchTree() @@ -357,16 +380,15 @@ def test_get(self): # Get with a present key returns proper value self.bst.put("one", 1) self.assertEqual(self.bst.get("one"), 1) - - + self.bst = binary_search_tree.BinarySearchTree() for pair in self.key_val: k, v = pair self.bst.put(k, v) - self.assertEqual(self.bst.get(k), v) - + self.assertEqual(self.bst.get(k), v) + shuffled = self.shuffle_list(self.key_val[:]) - + self.bst = binary_search_tree.BinarySearchTree() for pair in shuffled: k, v = pair @@ -403,7 +425,6 @@ def test_put(self): self.assertEqual(self.bst.root.right, None) self.assertEqual(self.bst.root.left.key, "aaa") - self.bst = binary_search_tree.BinarySearchTree() size = 0 for pair in self.key_val: @@ -425,32 +446,30 @@ def test_put(self): self.assertEqual(self.bst.get(k), v) self.assertEqual(self.bst.size(), size) - def test_min_key(self): self.bst = binary_search_tree.BinarySearchTree() for pair in self.key_val[::-1]: k, v = pair self.bst.put(k, v) - self.assertEqual(self.bst.min_key(), k) - + self.assertEqual(self.bst.min_key(), k) + shuffled = self.shuffle_list(self.key_val[:]) - + self.bst = binary_search_tree.BinarySearchTree() for pair in shuffled: k, v = pair self.bst.put(k, v) self.assertEqual(self.bst.min_key(), "a") - def test_max_key(self): self.bst = binary_search_tree.BinarySearchTree() for pair in self.key_val: k, v = pair self.bst.put(k, v) - self.assertEqual(self.bst.max_key(), k) - + self.assertEqual(self.bst.max_key(), k) + shuffled = self.shuffle_list(self.key_val[:]) - + self.bst = binary_search_tree.BinarySearchTree() for pair in shuffled: k, v = pair @@ -463,12 +482,12 @@ def test_floor_key(self): self.bst.put("c", 3) self.bst.put("e", 5) self.bst.put("g", 7) - self.assertEqual(self.bst.floor_key("a"), "a") + self.assertEqual(self.bst.floor_key("a"), "a") self.assertEqual(self.bst.floor_key("b"), "a") - self.assertEqual(self.bst.floor_key("g"), "g") - self.assertEqual(self.bst.floor_key("h"), "g") - - self.bst = binary_search_tree.BinarySearchTree() + self.assertEqual(self.bst.floor_key("g"), "g") + self.assertEqual(self.bst.floor_key("h"), "g") + + self.bst = binary_search_tree.BinarySearchTree() self.bst.put("c", 3) self.bst.put("e", 5) self.bst.put("a", 1) @@ -476,7 +495,7 @@ def test_floor_key(self): self.assertEqual(self.bst.floor_key("a"), "a") self.assertEqual(self.bst.floor_key("b"), "a") self.assertEqual(self.bst.floor_key("g"), "g") - self.assertEqual(self.bst.floor_key("h"), "g") + self.assertEqual(self.bst.floor_key("h"), "g") def test_ceiling_key(self): self.bst = binary_search_tree.BinarySearchTree() @@ -484,24 +503,24 @@ def test_ceiling_key(self): self.bst.put("c", 3) self.bst.put("e", 5) self.bst.put("g", 7) - self.assertEqual(self.bst.ceiling_key("a"), "a") + self.assertEqual(self.bst.ceiling_key("a"), "a") self.assertEqual(self.bst.ceiling_key("b"), "c") - self.assertEqual(self.bst.ceiling_key("g"), "g") - self.assertEqual(self.bst.ceiling_key("f"), "g") - - self.bst = binary_search_tree.BinarySearchTree() + self.assertEqual(self.bst.ceiling_key("g"), "g") + self.assertEqual(self.bst.ceiling_key("f"), "g") + + self.bst = binary_search_tree.BinarySearchTree() self.bst.put("c", 3) self.bst.put("e", 5) self.bst.put("a", 1) self.bst.put("g", 7) - self.assertEqual(self.bst.ceiling_key("a"), "a") + self.assertEqual(self.bst.ceiling_key("a"), "a") self.assertEqual(self.bst.ceiling_key("b"), "c") - self.assertEqual(self.bst.ceiling_key("g"), "g") - self.assertEqual(self.bst.ceiling_key("f"), "g") + self.assertEqual(self.bst.ceiling_key("g"), "g") + self.assertEqual(self.bst.ceiling_key("f"), "g") def test_select_key(self): shuffled = self.shuffle_list(self.key_val[:]) - + self.bst = binary_search_tree.BinarySearchTree() for pair in shuffled: k, v = pair @@ -532,7 +551,6 @@ def test_rank(self): self.assertEqual(self.bst.rank("c"), 2) self.assertEqual(self.bst.rank("d"), 3) - def test_delete_min(self): self.bst = binary_search_tree.BinarySearchTree() for pair in self.key_val: @@ -544,7 +562,6 @@ def test_delete_min(self): self.bst.delete_min() self.assertEqual(self.bst.min_key(), None) - shuffled = self.shuffle_list(self.key_val[:]) self.bst = binary_search_tree.BinarySearchTree() for pair in shuffled: @@ -567,9 +584,8 @@ def test_delete_max(self): self.bst.delete_max() self.assertEqual(self.bst.max_key(), None) - shuffled = self.shuffle_list(self.key_val[:]) - + for pair in shuffled: k, v = pair self.bst.put(k, v) @@ -591,7 +607,7 @@ def test_delete(self): self.bst.put("a", 1) self.bst.delete("b") self.assertEqual(self.bst.root.key, "a") - self.assertEqual(self.bst.size(), 1) + self.assertEqual(self.bst.size(), 1) # delete key when bst only contains one key self.bst = binary_search_tree.BinarySearchTree() @@ -599,7 +615,7 @@ def test_delete(self): self.assertEqual(self.bst.root.key, "a") self.bst.delete("a") self.assertEqual(self.bst.root, None) - self.assertEqual(self.bst.size(), 0) + self.assertEqual(self.bst.size(), 0) # delete parent key when it only has a left child self.bst = binary_search_tree.BinarySearchTree() @@ -651,4 +667,7 @@ def test_keys(self): for pair in self.key_val: k, v = pair self.bst.put(k, v) - self.assertEqual(self.bst.keys(), ["a", "b", "c", "d", "e", "f", "g", "h", "i"]) + self.assertEqual( + self.bst.keys(), + ["a", "b", "c", "d", "e", "f", "g", "h", "i"] + ) diff --git a/algorithms/tests/test_dynamic_programming.py b/algorithms/tests/test_dynamic_programming.py index effae9f..80aa6aa 100644 --- a/algorithms/tests/test_dynamic_programming.py +++ b/algorithms/tests/test_dynamic_programming.py @@ -1,4 +1,5 @@ import unittest + from ..dynamic_programming.lcs import lcs @@ -6,7 +7,7 @@ class TestLCS(unittest.TestCase): """ Tests the Longest Common Subsequence of several strings """ - + def test_lcs(self): str1 = "BANANA" str2 = "ABA" @@ -19,4 +20,4 @@ def test_lcs(self): self.assertEqual(lcs(str1, str4), "NNA") self.assertEqual(lcs(str2, str3), "BA") self.assertEqual(lcs(str2, str4), "A") - self.assertEqual(lcs(str3, str4), "AD") \ No newline at end of file + self.assertEqual(lcs(str3, str4), "AD") diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index 86f6b56..67fd68c 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -1,5 +1,6 @@ -import unittest import nose +import unittest + from ..math.extended_gcd import extended_gcd from ..math.lcm import lcm from ..math.sieve_eratosthenes import eratosthenes @@ -7,6 +8,7 @@ from ..math.std_normal_pdf import pdf from ..math.approx_cdf import cdf + class TestExtendedGCD(unittest.TestCase): def test_extended_gcd(self): @@ -47,12 +49,20 @@ class TestSieveOfEratosthenes(unittest.TestCase): def test_eratosthenes(self): rv1 = eratosthenes(-10) rv2 = eratosthenes(10) - rv3 = eratosthenes(100,5) - rv4 = eratosthenes(100,-10) - self.assertEqual(rv1,[]) - self.assertEqual(rv2,[2, 3, 5, 7]) - self.assertEqual(rv3,[5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]) - self.assertEqual(rv4,[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]) + rv3 = eratosthenes(100, 5) + rv4 = eratosthenes(100, -10) + self.assertEqual(rv1, []) + self.assertEqual(rv2, [2, 3, 5, 7]) + self.assertEqual( + rv3, + [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, + 67, 71, 73, 79, 83, 89, 97] + ) + self.assertEqual( + rv4, + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97] + ) class TestSieveOfAtkin(unittest.TestCase): @@ -62,17 +72,29 @@ def test_atkin(self): rv2 = atkin(100) rv3 = atkin(1000) rv4 = atkin(-10) - self.assertEqual(rv1,[2, 3, 5, 7]) - self.assertEqual(rv2,[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]) - self.assertEqual(rv3,[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, - 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, - 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, - 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, - 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, - 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, - 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, - 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]) - self.assertEqual(rv4,[]) + self.assertEqual(rv1, [2, 3, 5, 7]) + self.assertEqual( + rv2, + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97] + ) + self.assertEqual( + rv3, + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, + 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, + 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, + 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, + 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, + 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, + 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, + 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, + 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997] + ) + self.assertEqual(rv4, []) class TestStdNormPDF(unittest.TestCase): diff --git a/algorithms/tests/test_random.py b/algorithms/tests/test_random.py index 8cb4198..def1302 100644 --- a/algorithms/tests/test_random.py +++ b/algorithms/tests/test_random.py @@ -1,4 +1,5 @@ import unittest + from ..random import mersenne_twister @@ -7,42 +8,48 @@ class TestMersenneTwister(unittest.TestCase): Tests Mersenne Twister values for several seeds comparing against expected values from C++ STL's Mersenne Twister implementation """ - + def test_mersenne_twister(self): mt = mersenne_twister.MersenneTwister() - #Test seed 1 + # Test seed 1 mt.seed(1) - self.expected = [1791095845, 4282876139, 3093770124, - 4005303368, 491263, 550290313, 1298508491, - 4290846341, 630311759, 1013994432] + self.expected = [ + 1791095845, 4282876139, 3093770124, + 4005303368, 491263, 550290313, 1298508491, + 4290846341, 630311759, 1013994432 + ] self.results = [] for i in range(10): self.results.append(mt.randint()) self.assertEqual(self.expected, self.results) - #Test seed 42 + # Test seed 42 mt.seed(42) - self.expected = [1608637542, 3421126067, 4083286876, - 787846414, 3143890026, 3348747335, - 2571218620, 2563451924, 670094950, 1914837113] + self.expected = [ + 1608637542, 3421126067, 4083286876, + 787846414, 3143890026, 3348747335, + 2571218620, 2563451924, 670094950, 1914837113 + ] self.results = [] for i in range(10): self.results.append(mt.randint()) self.assertEqual(self.expected, self.results) - #Test seed 2147483647 + # Test seed 2147483647 mt.seed(2147483647) - self.expected = [1689602031, 3831148394, 2820341149, - 2744746572, 370616153, 3004629480, - 4141996784, 3942456616, 2667712047, 1179284407] + self.expected = [ + 1689602031, 3831148394, 2820341149, + 2744746572, 370616153, 3004629480, + 4141996784, 3942456616, 2667712047, 1179284407 + ] self.results = [] for i in range(10): self.results.append(mt.randint()) self.assertEqual(self.expected, self.results) - #Test seed -1 - #Hex is used to force 32-bit -1 + # Test seed -1 + # Hex is used to force 32-bit -1 mt.seed(0xffffffff) self.expected = [419326371, 479346978, 3918654476, 2416749639, 3388880820, 2260532800, diff --git a/algorithms/tests/test_searching.py b/algorithms/tests/test_searching.py index 8994c16..b1ed0b4 100644 --- a/algorithms/tests/test_searching.py +++ b/algorithms/tests/test_searching.py @@ -1,6 +1,13 @@ """ Unit Tests for searching """ import unittest -from ..searching import binary_search, kmp_search, rabinkarp_search, bmh_search, depth_first_search + +from ..searching import ( + binary_search, + kmp_search, + rabinkarp_search, + bmh_search, + depth_first_search +) class TestBinarySearch(unittest.TestCase): @@ -32,6 +39,7 @@ def test_binarysearch(self): self.assertFalse(rv4) self.assertIs(rv5, 4) + class TestKMPSearch(unittest.TestCase): """ Tests KMP search on string "ABCDE FG ABCDEABCDEF" @@ -70,65 +78,80 @@ def test_bmhsearch(self): self.assertIs(rv1[0], 9) self.assertFalse(rv2) + class TestDepthFirstSearch(unittest.TestCase): """ Tests DFS on a graph represented by a adjacency list """ def test_dfs(self): - self.graph = {'A': ['B','C','E'], - 'B': ['A','D','F'], - 'C': ['A','G'], - 'D': ['B'], - 'F': ['B'], - 'E': ['A'], - 'G': ['C']} + self.graph = { + 'A': ['B', 'C', 'E'], + 'B': ['A', 'D', 'F'], + 'C': ['A', 'G'], + 'D': ['B'], + 'F': ['B'], + 'E': ['A'], + 'G': ['C'] + } rv1 = depth_first_search.dfs(self.graph, "A") rv2 = depth_first_search.dfs(self.graph, "G") rv1e = depth_first_search.dfs(self.graph, "Z") self.assertEqual(rv1, ['A', 'B', 'D', 'F', 'C', 'G', 'E']) self.assertEqual(rv2, ['G', 'C', 'A', 'B', 'D', 'F', 'E']) self.assertEqual(rv1e, None) - self.graph = {1:[2,3,4], - 2:[1,6,10], - 3:[1,5,10], - 4:[1,10,11], - 5:[3,10], - 6:[2,7,8,9], - 7:[6,8], - 8:[6,7], - 9:[6,10], - 10:[3,5,9,12], - 11:[4], - 12:[10]} - rv3 = depth_first_search.dfs(self.graph,1) - rv4 = depth_first_search.dfs(self.graph,5) - rv5 = depth_first_search.dfs(self.graph,6) - rv2e = depth_first_search.dfs(self.graph,99) + self.graph = { + 1: [2, 3, 4], + 2: [1, 6, 10], + 3: [1, 5, 10], + 4: [1, 10, 11], + 5: [3, 10], + 6: [2, 7, 8, 9], + 7: [6, 8], + 8: [6, 7], + 9: [6, 10], + 10: [3, 5, 9, 12], + 11: [4], + 12: [10] + } + rv3 = depth_first_search.dfs(self.graph, 1) + rv4 = depth_first_search.dfs(self.graph, 5) + rv5 = depth_first_search.dfs(self.graph, 6) + rv2e = depth_first_search.dfs(self.graph, 99) self.assertEqual(rv3, [1, 2, 6, 7, 8, 9, 10, 3, 5, 12, 4, 11]) self.assertEqual(rv4, [5, 3, 1, 2, 6, 7, 8, 9, 10, 12, 4, 11]) self.assertEqual(rv5, [6, 2, 1, 3, 5, 10, 9, 12, 4, 11, 7, 8]) self.assertEqual(rv2e, None) - self.graph = {1:[2,3,4,5,6], - 2:[1,4,7,8,9], - 3:[1,10], - 4:[1,2,11,12], - 5:[1,13,14,15], - 6:[1,15], - 7:[2], - 8:[2], - 9:[2,10], - 10:[3,9], - 11:[4], - 12:[4], - 13:[5], - 14:[5], - 15:[5,6]} - rv6 = depth_first_search.dfs(self.graph,1) - rv7 = depth_first_search.dfs(self.graph,10) - rv8 = depth_first_search.dfs(self.graph,5) - rv3e = depth_first_search.dfs(self.graph,-1) - self.assertEqual(rv6, [1, 2, 4, 11, 12, 7, 8, 9, 10, 3, 5, 13, 14, 15, 6]) - self.assertEqual(rv7, [10, 3, 1, 2, 4, 11, 12, 7, 8, 9, 5, 13, 14, 15, 6]) - self.assertEqual(rv8, [5, 1, 2, 4, 11, 12, 7, 8, 9, 10, 3, 6, 15, 13, 14]) + self.graph = { + 1: [2, 3, 4, 5, 6], + 2: [1, 4, 7, 8, 9], + 3: [1, 10], + 4: [1, 2, 11, 12], + 5: [1, 13, 14, 15], + 6: [1, 15], + 7: [2], + 8: [2], + 9: [2, 10], + 10: [3, 9], + 11: [4], + 12: [4], + 13: [5], + 14: [5], + 15: [5, 6]} + rv6 = depth_first_search.dfs(self.graph, 1) + rv7 = depth_first_search.dfs(self.graph, 10) + rv8 = depth_first_search.dfs(self.graph, 5) + rv3e = depth_first_search.dfs(self.graph, -1) + self.assertEqual( + rv6, + [1, 2, 4, 11, 12, 7, 8, 9, 10, 3, 5, 13, 14, 15, 6] + ) + self.assertEqual( + rv7, + [10, 3, 1, 2, 4, 11, 12, 7, 8, 9, 5, 13, 14, 15, 6] + ) + self.assertEqual( + rv8, + [5, 1, 2, 4, 11, 12, 7, 8, 9, 10, 3, 6, 15, 13, 14] + ) self.assertEqual(rv3e, None) diff --git a/algorithms/tests/test_sorting.py b/algorithms/tests/test_sorting.py index f80f277..36844bc 100644 --- a/algorithms/tests/test_sorting.py +++ b/algorithms/tests/test_sorting.py @@ -1,8 +1,19 @@ import random import unittest -from ..sorting import bubble_sort, selection_sort, insertion_sort, \ - merge_sort, quick_sort, heap_sort, shell_sort, comb_sort, cocktail_sort, \ - quick_sort_in_place, gnome_sort + +from ..sorting import ( + bubble_sort, + selection_sort, + insertion_sort, + merge_sort, + quick_sort, + heap_sort, + shell_sort, + comb_sort, + cocktail_sort, + quick_sort_in_place, + gnome_sort, +) class SortingAlgorithmTestCase(unittest.TestCase): @@ -80,14 +91,20 @@ class TestQuickSortInPlace(SortingAlgorithmTestCase): also tests partition function included in quick sort """ def test_quicksort_in_place(self): - self.output = quick_sort_in_place.sort(self.input, 0, - len(self.input)-1) + self.output = quick_sort_in_place.sort( + self.input, 0, + len(self.input)-1 + ) self.assertEqual(self.correct, self.output) def test_partition(self): self.seq = list(range(10)) - self.assertIs(quick_sort_in_place.partition(self.seq, 0, - len(self.seq)-1, 5), 5) + self.assertIs( + quick_sort_in_place.partition( + self.seq, 0, + len(self.seq)-1, 5), + 5 + ) class TestHeapSort(SortingAlgorithmTestCase): diff --git a/requirements.txt b/requirements.txt index e69de29..b5b7de5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,5 @@ +flake8==2.4.1 +mccabe==0.3.1 +nose==1.3.7 +pep8==1.5.7 +pyflakes==0.8.1 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..bfead2c --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 79 diff --git a/setup.py b/setup.py index a0ff991..926a944 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,8 @@ # Read in the README for the long description on PyPI def long_description(): - with open('README.rst', 'r') as f: - readme = unicode(f.read()) + with open('README.rst', 'r', 'utf-8') as f: + readme = f.read() return readme setup(name='algorithms', From dd4f6091595afa687261484f9da9c221df382ce2 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 26 Sep 2015 12:59:24 -0700 Subject: [PATCH 55/89] Fix typo in README. - Also, hopefully, fix Travis CI build --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index f378c93..5b6d40a 100644 --- a/README.rst +++ b/README.rst @@ -56,7 +56,7 @@ Algorithms implemented so far: - Cumulative Density Function (Approximation; 16 digit precision for 300 iter.) - Sieve of Eratosthenes -+**Dynamic Programming:** ++ **Dynamic Programming:** - Longest Common Subsequence From 40c8d635f0ad570a55bdf09e1a5722ac562da334 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Wed, 30 Sep 2015 21:33:07 -0700 Subject: [PATCH 56/89] Change to pytest as the test runner, fixes #100 - Update requirements - Update README - Add run_tests module to run tests - Change math tests to use builtin assertAlmostEqual --- README.rst | 6 ++--- algorithms/tests/test_math.py | 13 +++++----- requirements.txt | 3 ++- run_tests.py | 45 +++++++++++++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 13 deletions(-) mode change 100644 => 100755 run_tests.py diff --git a/README.rst b/README.rst index 5b6d40a..f5f959c 100644 --- a/README.rst +++ b/README.rst @@ -56,7 +56,7 @@ Algorithms implemented so far: - Cumulative Density Function (Approximation; 16 digit precision for 300 iter.) - Sieve of Eratosthenes -+ **Dynamic Programming:** +**Dynamic Programming:** - Longest Common Subsequence @@ -94,11 +94,11 @@ All prequisites for the algorithms are listed in the source code for each algori Tests: ------ -Nose is used as the main test runner and all Unit Tests can be run by: +Pytest is used as the main test runner and all Unit Tests can be run with: :: - $ python algorithms/run_tests.py + $ ./run_tests.py Contributing: diff --git a/algorithms/tests/test_math.py b/algorithms/tests/test_math.py index 67fd68c..af14b59 100644 --- a/algorithms/tests/test_math.py +++ b/algorithms/tests/test_math.py @@ -1,4 +1,3 @@ -import nose import unittest from ..math.extended_gcd import extended_gcd @@ -102,15 +101,15 @@ class TestStdNormPDF(unittest.TestCase): def test_pdf(self): # Calculate standard normal pdf for x=1 a = pdf(1) - nose.tools.assert_almost_equal(a, 0.24197072451914337) + self.assertAlmostEqual(a, 0.24197072451914337) # Calculate standard normal pdf for x=(-1) a = pdf(-1) - nose.tools.assert_almost_equal(a, 0.24197072451914337) + self.assertAlmostEqual(a, 0.24197072451914337) # Calculate standard normal pdf for x=13, mean=10, std_dev=1 a = pdf(x=13, mean=10, std_dev=1) - nose.tools.assert_almost_equal(a, 0.004431848411938008) + self.assertAlmostEqual(a, 0.004431848411938008) class TestApproxCdf(unittest.TestCase): @@ -118,12 +117,12 @@ class TestApproxCdf(unittest.TestCase): def test_cdf(self): # Calculate cumulative distribution function for x=1 a = cdf(1) - nose.tools.assert_almost_equal(a, 0.841344746068543) + self.assertAlmostEqual(a, 0.841344746068543) # Calculate cumulative distribution function x=0 a = cdf(0) - nose.tools.assert_almost_equal(a, 0.5) + self.assertAlmostEqual(a, 0.5) # Calculate cumulative distribution function for x=(-1) a = cdf(-1) - nose.tools.assert_almost_equal(a, 0.15865525393145702) + self.assertAlmostEqual(a, 0.15865525393145702) diff --git a/requirements.txt b/requirements.txt index b5b7de5..8e5f6a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ flake8==2.4.1 mccabe==0.3.1 -nose==1.3.7 pep8==1.5.7 +py==1.4.30 pyflakes==0.8.1 +pytest==2.8.0 diff --git a/run_tests.py b/run_tests.py old mode 100644 new mode 100755 index e2bb3e7..fe8ed0a --- a/run_tests.py +++ b/run_tests.py @@ -1,4 +1,45 @@ -import nose +#! /usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import print_function + +import os +import subprocess +import sys + +import pytest + + +FLAKE8_ARGS = ['algorithms'] + +sys.path.append(os.path.dirname(__file__)) + + +def exit_on_failure(ret, message=None): + if ret: + sys.exit(ret) + + +def flake8_main(args): + print('Running flake8 code linting') + ret = subprocess.call(['flake8'] + args) + print('flake8 failed' if ret else 'flake8 passed') + return ret + if __name__ == '__main__': - nose.main() + run_tests = True + run_flake8 = True + + try: + sys.argv.remove('--lintonly') + except ValueError: + run_tests = True + else: + run_tests = False + run_flake8 = True + + if run_tests: + exit_on_failure(pytest.main()) + + if run_flake8: + exit_on_failure(flake8_main(FLAKE8_ARGS)) From 721b3cfdc53e2d1763fa1f80ea8794d0c523b605 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Wed, 30 Sep 2015 21:53:04 -0700 Subject: [PATCH 57/89] Move tests up one directory level, fixes #107 - Remove all relative imports from the tests --- run_tests.py | 2 +- {algorithms/tests => tests}/__init__.py | 0 {algorithms/tests => tests}/test_data_structure.py | 2 +- .../tests => tests}/test_dynamic_programming.py | 2 +- {algorithms/tests => tests}/test_math.py | 12 ++++++------ {algorithms/tests => tests}/test_random.py | 2 +- {algorithms/tests => tests}/test_searching.py | 2 +- {algorithms/tests => tests}/test_shuffling.py | 3 ++- {algorithms/tests => tests}/test_sorting.py | 2 +- 9 files changed, 14 insertions(+), 13 deletions(-) rename {algorithms/tests => tests}/__init__.py (100%) rename {algorithms/tests => tests}/test_data_structure.py (99%) rename {algorithms/tests => tests}/test_dynamic_programming.py (91%) rename {algorithms/tests => tests}/test_math.py (93%) rename {algorithms/tests => tests}/test_random.py (97%) rename {algorithms/tests => tests}/test_searching.py (99%) rename {algorithms/tests => tests}/test_shuffling.py (93%) rename {algorithms/tests => tests}/test_sorting.py (99%) diff --git a/run_tests.py b/run_tests.py index fe8ed0a..041874c 100755 --- a/run_tests.py +++ b/run_tests.py @@ -9,7 +9,7 @@ import pytest -FLAKE8_ARGS = ['algorithms'] +FLAKE8_ARGS = ['algorithms', 'tests'] sys.path.append(os.path.dirname(__file__)) diff --git a/algorithms/tests/__init__.py b/tests/__init__.py similarity index 100% rename from algorithms/tests/__init__.py rename to tests/__init__.py diff --git a/algorithms/tests/test_data_structure.py b/tests/test_data_structure.py similarity index 99% rename from algorithms/tests/test_data_structure.py rename to tests/test_data_structure.py index 7646df5..8907d72 100644 --- a/algorithms/tests/test_data_structure.py +++ b/tests/test_data_structure.py @@ -1,7 +1,7 @@ from random import shuffle import unittest -from ..data_structure import ( +from algorithms.data_structure import ( stack, queue, union_find, diff --git a/algorithms/tests/test_dynamic_programming.py b/tests/test_dynamic_programming.py similarity index 91% rename from algorithms/tests/test_dynamic_programming.py rename to tests/test_dynamic_programming.py index 80aa6aa..932ea54 100644 --- a/algorithms/tests/test_dynamic_programming.py +++ b/tests/test_dynamic_programming.py @@ -1,6 +1,6 @@ import unittest -from ..dynamic_programming.lcs import lcs +from algorithms.dynamic_programming.lcs import lcs class TestLCS(unittest.TestCase): diff --git a/algorithms/tests/test_math.py b/tests/test_math.py similarity index 93% rename from algorithms/tests/test_math.py rename to tests/test_math.py index af14b59..78aa83c 100644 --- a/algorithms/tests/test_math.py +++ b/tests/test_math.py @@ -1,11 +1,11 @@ import unittest -from ..math.extended_gcd import extended_gcd -from ..math.lcm import lcm -from ..math.sieve_eratosthenes import eratosthenes -from ..math.sieve_atkin import atkin -from ..math.std_normal_pdf import pdf -from ..math.approx_cdf import cdf +from algorithms.math.extended_gcd import extended_gcd +from algorithms.math.lcm import lcm +from algorithms.math.sieve_eratosthenes import eratosthenes +from algorithms.math.sieve_atkin import atkin +from algorithms.math.std_normal_pdf import pdf +from algorithms.math.approx_cdf import cdf class TestExtendedGCD(unittest.TestCase): diff --git a/algorithms/tests/test_random.py b/tests/test_random.py similarity index 97% rename from algorithms/tests/test_random.py rename to tests/test_random.py index def1302..0dbf6b1 100644 --- a/algorithms/tests/test_random.py +++ b/tests/test_random.py @@ -1,6 +1,6 @@ import unittest -from ..random import mersenne_twister +from algorithms.random import mersenne_twister class TestMersenneTwister(unittest.TestCase): diff --git a/algorithms/tests/test_searching.py b/tests/test_searching.py similarity index 99% rename from algorithms/tests/test_searching.py rename to tests/test_searching.py index b1ed0b4..197ff4e 100644 --- a/algorithms/tests/test_searching.py +++ b/tests/test_searching.py @@ -1,7 +1,7 @@ """ Unit Tests for searching """ import unittest -from ..searching import ( +from algorithms.searching import ( binary_search, kmp_search, rabinkarp_search, diff --git a/algorithms/tests/test_shuffling.py b/tests/test_shuffling.py similarity index 93% rename from algorithms/tests/test_shuffling.py rename to tests/test_shuffling.py index 59c3f8c..230d7eb 100644 --- a/algorithms/tests/test_shuffling.py +++ b/tests/test_shuffling.py @@ -1,5 +1,6 @@ import unittest -from ..shuffling import knuth + +from algorithms.shuffling import knuth class ShufflingAlgorithmTestCase(unittest.TestCase): diff --git a/algorithms/tests/test_sorting.py b/tests/test_sorting.py similarity index 99% rename from algorithms/tests/test_sorting.py rename to tests/test_sorting.py index 36844bc..2a56f84 100644 --- a/algorithms/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -1,7 +1,7 @@ import random import unittest -from ..sorting import ( +from algorithms.sorting import ( bubble_sort, selection_sort, insertion_sort, From aafdb41a5805fbc8c75768e26e4a1762bf912ed4 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Wed, 30 Sep 2015 22:18:02 -0700 Subject: [PATCH 58/89] Rename to data_structures, fixes #106 --- algorithms/{data_structure => data_structures}/__init__.py | 0 .../{data_structure => data_structures}/binary_search_tree.py | 0 algorithms/{data_structure => data_structures}/digraph.py | 0 algorithms/{data_structure => data_structures}/queue.py | 0 .../{data_structure => data_structures}/singly_linked_list.py | 0 algorithms/{data_structure => data_structures}/stack.py | 0 .../{data_structure => data_structures}/undirected_graph.py | 0 algorithms/{data_structure => data_structures}/union_find.py | 0 .../{data_structure => data_structures}/union_find_by_rank.py | 0 .../union_find_with_path_compression.py | 0 tests/{test_data_structure.py => test_data_structures.py} | 2 +- 11 files changed, 1 insertion(+), 1 deletion(-) rename algorithms/{data_structure => data_structures}/__init__.py (100%) rename algorithms/{data_structure => data_structures}/binary_search_tree.py (100%) rename algorithms/{data_structure => data_structures}/digraph.py (100%) rename algorithms/{data_structure => data_structures}/queue.py (100%) rename algorithms/{data_structure => data_structures}/singly_linked_list.py (100%) rename algorithms/{data_structure => data_structures}/stack.py (100%) rename algorithms/{data_structure => data_structures}/undirected_graph.py (100%) rename algorithms/{data_structure => data_structures}/union_find.py (100%) rename algorithms/{data_structure => data_structures}/union_find_by_rank.py (100%) rename algorithms/{data_structure => data_structures}/union_find_with_path_compression.py (100%) rename tests/{test_data_structure.py => test_data_structures.py} (99%) diff --git a/algorithms/data_structure/__init__.py b/algorithms/data_structures/__init__.py similarity index 100% rename from algorithms/data_structure/__init__.py rename to algorithms/data_structures/__init__.py diff --git a/algorithms/data_structure/binary_search_tree.py b/algorithms/data_structures/binary_search_tree.py similarity index 100% rename from algorithms/data_structure/binary_search_tree.py rename to algorithms/data_structures/binary_search_tree.py diff --git a/algorithms/data_structure/digraph.py b/algorithms/data_structures/digraph.py similarity index 100% rename from algorithms/data_structure/digraph.py rename to algorithms/data_structures/digraph.py diff --git a/algorithms/data_structure/queue.py b/algorithms/data_structures/queue.py similarity index 100% rename from algorithms/data_structure/queue.py rename to algorithms/data_structures/queue.py diff --git a/algorithms/data_structure/singly_linked_list.py b/algorithms/data_structures/singly_linked_list.py similarity index 100% rename from algorithms/data_structure/singly_linked_list.py rename to algorithms/data_structures/singly_linked_list.py diff --git a/algorithms/data_structure/stack.py b/algorithms/data_structures/stack.py similarity index 100% rename from algorithms/data_structure/stack.py rename to algorithms/data_structures/stack.py diff --git a/algorithms/data_structure/undirected_graph.py b/algorithms/data_structures/undirected_graph.py similarity index 100% rename from algorithms/data_structure/undirected_graph.py rename to algorithms/data_structures/undirected_graph.py diff --git a/algorithms/data_structure/union_find.py b/algorithms/data_structures/union_find.py similarity index 100% rename from algorithms/data_structure/union_find.py rename to algorithms/data_structures/union_find.py diff --git a/algorithms/data_structure/union_find_by_rank.py b/algorithms/data_structures/union_find_by_rank.py similarity index 100% rename from algorithms/data_structure/union_find_by_rank.py rename to algorithms/data_structures/union_find_by_rank.py diff --git a/algorithms/data_structure/union_find_with_path_compression.py b/algorithms/data_structures/union_find_with_path_compression.py similarity index 100% rename from algorithms/data_structure/union_find_with_path_compression.py rename to algorithms/data_structures/union_find_with_path_compression.py diff --git a/tests/test_data_structure.py b/tests/test_data_structures.py similarity index 99% rename from tests/test_data_structure.py rename to tests/test_data_structures.py index 8907d72..ebd8efc 100644 --- a/tests/test_data_structure.py +++ b/tests/test_data_structures.py @@ -1,7 +1,7 @@ from random import shuffle import unittest -from algorithms.data_structure import ( +from algorithms.data_structures import ( stack, queue, union_find, From 11701382bd8b114c152e2544bf9fd3a015cdc499 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Thu, 1 Oct 2015 22:13:47 -0700 Subject: [PATCH 59/89] Add tox, fixes #96 - Change Travis CI to run tox, instead of pytest directly - Allow py35 failure since Travis doesn't have it yet - Set sudo to false to allow of containerized runs on Travis --- .travis.yml | 28 +++++++++++++++++----------- requirements.txt | 3 +++ setup.py | 3 ++- tox.ini | 8 ++++++++ 4 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index 74acec6..2123fbf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,20 @@ language: python -python: - - "2.7" - - "3.2" - - "3.3" - - "3.4" - - "3.5" + +sudo: false + +env: + - TOX_ENV=py27 + - TOX_ENV=py32 + - TOX_ENV=py33 + - TOX_ENV=py34 + - TOX_ENV=py35 + +matrix: + fast_finish: true + allow_failures: + - env: TOX_ENV=py35 + # command to install dependencies -install: "pip install -r requirements.txt" -# run the linter -before_script: - - "flake8 ." +install: pip install tox # command to run tests -script: "nosetests" +script: tox -e $TOX_ENV diff --git a/requirements.txt b/requirements.txt index 8e5f6a3..831b0fe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,9 @@ flake8==2.4.1 mccabe==0.3.1 pep8==1.5.7 +pluggy==0.3.1 py==1.4.30 pyflakes==0.8.1 pytest==2.8.0 +tox==2.1.1 +virtualenv==13.1.2 diff --git a/setup.py b/setup.py index 926a944..b6902ff 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,10 @@ +import io from setuptools import find_packages, setup # Read in the README for the long description on PyPI def long_description(): - with open('README.rst', 'r', 'utf-8') as f: + with io.open('README.rst', 'r', encoding='utf-8') as f: readme = f.read() return readme diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..96af7d9 --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py27, py32, py33, py34, py35 + +[testenv] +commands = ./run_tests.py +deps = + pytest + flake8 From e6f626bdb4b4766597fb242692e505fd4830de1c Mon Sep 17 00:00:00 2001 From: Gazolik Date: Fri, 2 Oct 2015 21:37:24 +0200 Subject: [PATCH 60/89] add fermat factorization add pollard rho factorization add trial division factorization add pollard/trial/fermat tests --- algorithms/factorization/__init__.py | 0 algorithms/factorization/fermat.py | 31 +++++++++++ algorithms/factorization/pollard_rho.py | 62 ++++++++++++++++++++++ algorithms/factorization/trial_division.py | 30 +++++++++++ tests/test_factorization.py | 39 ++++++++++++++ 5 files changed, 162 insertions(+) create mode 100644 algorithms/factorization/__init__.py create mode 100644 algorithms/factorization/fermat.py create mode 100644 algorithms/factorization/pollard_rho.py create mode 100644 algorithms/factorization/trial_division.py create mode 100644 tests/test_factorization.py diff --git a/algorithms/factorization/__init__.py b/algorithms/factorization/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/algorithms/factorization/fermat.py b/algorithms/factorization/fermat.py new file mode 100644 index 0000000..6b89ecc --- /dev/null +++ b/algorithms/factorization/fermat.py @@ -0,0 +1,31 @@ +from math import sqrt + +""" + fermat.py + + Implementation of the Fermat factorization. + + Fermat factorization Overview: + ------------------------ + Fermat's factorization method is based on the representation + of an odd integer as the difference of two squares: + N = a*a-b*b = (a-b)*(a+b) + +""" + + +def fermat(n): + if n & 1 == 0: + return [n >> 1, 2] + x = int(sqrt(n)) + if x*x == n: + return [x, x] + x += 1 + while True: + y2 = x*x-n + y = int(sqrt(y2)) + if y*y == y2: + break + else: + x += 1 + return [x-y, x+y] diff --git a/algorithms/factorization/pollard_rho.py b/algorithms/factorization/pollard_rho.py new file mode 100644 index 0000000..b2ce508 --- /dev/null +++ b/algorithms/factorization/pollard_rho.py @@ -0,0 +1,62 @@ +import random + +from algorithms.math.primality_test import is_prime +from fractions import gcd + +""" + pollard_rho.py + + Implementation of the Pollard Rho algorithm. + + Pollard Rho algorithm Overview: + ------------------------ + Pollard's rho algorithm is a special-purpose integer factorization + algorithm. + It was invented by John Pollard in 1975. + It is particularly effective for a composite + number having a small prime factor. + +""" + + +def f(x): + return x*x+1 + + +def rho(n, x1=2, x2=2): + if n % 2 == 0: + return 2 + i = 0 + while True: + x1 = f(x1) % n + x2 = f(f(x2)) % n + divisor = gcd(abs(x1-x2), n) + i += 1 + if(divisor != 1): + break + if i > 500: + x1 = random.randint(1, 10) + x2 = random.randint(1, 10) + i = 0 + return divisor + + +def pollard_rho_rec(x, factors): + if x == 1: + return + + if is_prime(x): + factors.append(x) + return + + divisor = rho(int(x), random.randint(1, 10), random.randint(1, 10)) + pollard_rho_rec(int(divisor), factors) + pollard_rho_rec(int(x/divisor), factors) + + +def pollard_rho(x): + if x == 1 or x == 0: + return [x] + factors = [] + pollard_rho_rec(x, factors) + return factors diff --git a/algorithms/factorization/trial_division.py b/algorithms/factorization/trial_division.py new file mode 100644 index 0000000..c9e5c2b --- /dev/null +++ b/algorithms/factorization/trial_division.py @@ -0,0 +1,30 @@ +from algorithms.math.sieve_eratosthenes import eratosthenes + + +""" + trial_division.py + + Implementation of the Trial division. + + Trial division Overview: + ------------------------ + Trial division is the most laborious but easiest + to understand of the integer factorization algorithms. + Try to divide a number n by all prime numbers < sqrt(n). + +""" + + +def trial_division(n): + prime_factors = [] + if n < 2: + return prime_factors + for p in eratosthenes(int(n**0.5) + 1): + if p*p > n: + break + while n % p == 0: + prime_factors.append(p) + n //= p + if n > 1: + prime_factors.append(n) + return prime_factors diff --git a/tests/test_factorization.py b/tests/test_factorization.py new file mode 100644 index 0000000..1a8be74 --- /dev/null +++ b/tests/test_factorization.py @@ -0,0 +1,39 @@ +import random +import unittest + +from algorithms.factorization.pollard_rho import pollard_rho +from algorithms.factorization.trial_division import trial_division +from algorithms.factorization.fermat import fermat + + +class TestPollardRho(unittest.TestCase): + + def test_pollard_rho(self): + x = random.randint(1, 100000000000) + factors = pollard_rho(x) + res = 1 + for j in factors: + res *= j + self.assertEqual(x, res) + + +class TestTrialDivision(unittest.TestCase): + + def test_trial_division(self): + x = random.randint(0, 10000000000) + factors = trial_division(x) + res = 1 + for i in factors: + res *= i + self.assertEqual(x, res) + + +class TestFermat(unittest.TestCase): + + def test_fermat(self): + x = random.randint(1, 100000000) + factors = fermat(x) + res = 1 + for i in factors: + res *= i + self.assertEqual(x, res) From ec99a0c7b3195c458c3efda0da8db22ccf36262c Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 3 Oct 2015 17:42:32 -0700 Subject: [PATCH 61/89] Move License to it's own file, fixes #112 --- LICENSE.rst | 16 ++++++++++++++++ README.rst | 18 ------------------ 2 files changed, 16 insertions(+), 18 deletions(-) create mode 100644 LICENSE.rst diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 0000000..f876a15 --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,16 @@ +License: +======== + +Copyright (c) 2012-215 by Nic Young + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as well as documentation, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.rst b/README.rst index f5f959c..3f97821 100644 --- a/README.rst +++ b/README.rst @@ -117,21 +117,3 @@ TODO: See `TODO.rst`_. .. _`TODO.rst`: TODO.rst - - -License: --------- - -Copyright (c) 2012 by Nic Young and contributors. See AUTHORS.rst for more details - -Some rights reserved. - -Redistribution and use in source and binary forms of the software as well as documentation, with or without modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -* The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From c6718573434e2075eeeb5b6310c1d3eefab39a0b Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 3 Oct 2015 17:56:19 -0700 Subject: [PATCH 62/89] Add codecov, fixes #97 --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2123fbf..02fe2f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,3 +18,8 @@ matrix: install: pip install tox # command to run tests script: tox -e $TOX_ENV +# codecov +before_install: + - pip install codecov +after_success: + - codecov From e89bf6146d80e50ae62e2f8fa211f44744a8e6ea Mon Sep 17 00:00:00 2001 From: Nic Young Date: Mon, 5 Oct 2015 22:20:29 -0700 Subject: [PATCH 63/89] Add codecov, fixes #97 - Move testing requirements to it's own file - Refactor run_tests script to look for coverage args - Update tox to run tests with coverage and point to new requirements - Add pytest-cov to the testing requirements - Add codecov badge to README --- .travis.yml | 14 +++++++------- README.rst | 7 +++++-- .../requirements-testing.txt | 1 + run_tests.py | 12 +++++++++++- tox.ini | 6 +++--- 5 files changed, 27 insertions(+), 13 deletions(-) rename requirements.txt => requirements/requirements-testing.txt (87%) diff --git a/.travis.yml b/.travis.yml index 02fe2f2..a97616a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,12 +14,12 @@ matrix: allow_failures: - env: TOX_ENV=py35 -# command to install dependencies -install: pip install tox -# command to run tests -script: tox -e $TOX_ENV -# codecov -before_install: - - pip install codecov +install: + - pip install tox + +script: + - tox -e $TOX_ENV + after_success: + - pip install codecov - codecov diff --git a/README.rst b/README.rst index 3f97821..b031554 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,11 @@ +Algorithms +========== + .. image:: https://travis-ci.org/nryoung/algorithms.svg?branch=master :target: https://travis-ci.org/nryoung/algorithms -Algorithms -========== +.. image:: http://codecov.io/github/nryoung/algorithms/coverage.svg?branch=master + :target: http://codecov.io/github/nryoung/algorithms?branch=master This is an attempt to build a cohesive module of algorithms in Python. diff --git a/requirements.txt b/requirements/requirements-testing.txt similarity index 87% rename from requirements.txt rename to requirements/requirements-testing.txt index 831b0fe..0187825 100644 --- a/requirements.txt +++ b/requirements/requirements-testing.txt @@ -5,5 +5,6 @@ pluggy==0.3.1 py==1.4.30 pyflakes==0.8.1 pytest==2.8.0 +pytest-cov==1.8.1 tox==2.1.1 virtualenv==13.1.2 diff --git a/run_tests.py b/run_tests.py index 041874c..73458be 100755 --- a/run_tests.py +++ b/run_tests.py @@ -27,9 +27,11 @@ def flake8_main(args): if __name__ == '__main__': + pytest_args = sys.argv[1:] run_tests = True run_flake8 = True + # Logic to run flake8 only try: sys.argv.remove('--lintonly') except ValueError: @@ -38,8 +40,16 @@ def flake8_main(args): run_tests = False run_flake8 = True + # Logic to run pytest with coverage turned on + try: + pytest_args.remove('--coverage') + except ValueError: + pass + else: + pytest_args = ['--cov', 'algorithms'] + pytest_args + if run_tests: - exit_on_failure(pytest.main()) + exit_on_failure(pytest.main(pytest_args)) if run_flake8: exit_on_failure(flake8_main(FLAKE8_ARGS)) diff --git a/tox.ini b/tox.ini index 96af7d9..3a253ba 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = py27, py32, py33, py34, py35 [testenv] -commands = ./run_tests.py +commands = ./run_tests.py --coverage + deps = - pytest - flake8 + -rrequirements/requirements-testing.txt From 3ce96cf824b6836f87a7f24a158d17e84bcee3af Mon Sep 17 00:00:00 2001 From: Nic Young Date: Wed, 7 Oct 2015 21:00:26 -0700 Subject: [PATCH 64/89] Fix codecov, fixes #97 - Update pytest and pytest-cov to latest version - Actually call coverage report when tests are run - Pin coverage to 3.7.1 because of bug with py32 --- .travis.yml | 2 +- requirements/requirements-testing.txt | 6 ++++-- run_tests.py | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index a97616a..3c3f5b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,4 +22,4 @@ script: after_success: - pip install codecov - - codecov + - codecov -e TOX_ENV diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 0187825..05bce13 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -1,10 +1,12 @@ +cov-core==1.15.0 +coverage==3.7.1 flake8==2.4.1 mccabe==0.3.1 pep8==1.5.7 pluggy==0.3.1 py==1.4.30 pyflakes==0.8.1 -pytest==2.8.0 -pytest-cov==1.8.1 +pytest==2.8.2 +pytest-cov==2.2.0 tox==2.1.1 virtualenv==13.1.2 diff --git a/run_tests.py b/run_tests.py index 73458be..6c3c8da 100755 --- a/run_tests.py +++ b/run_tests.py @@ -46,7 +46,11 @@ def flake8_main(args): except ValueError: pass else: - pytest_args = ['--cov', 'algorithms'] + pytest_args + pytest_args = [ + '--cov-report', + 'xml', + '--cov', + 'algorithms'] + pytest_args if run_tests: exit_on_failure(pytest.main(pytest_args)) From 706a539bf0b1fbcf1d051f2184826eb2b8878d72 Mon Sep 17 00:00:00 2001 From: NoahTheDuke Date: Fri, 9 Oct 2015 00:10:03 -0400 Subject: [PATCH 65/89] Added Strand sort. --- AUTHORS.rst | 1 + README.rst | 1 + TODO.rst | 1 - algorithms/sorting/strand_sort.py | 69 +++++++++++++++++++++++++++++++ tests/test_sorting.py | 11 +++++ 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 algorithms/sorting/strand_sort.py diff --git a/AUTHORS.rst b/AUTHORS.rst index dad7e08..1ee1d3d 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -27,3 +27,4 @@ List of contributors: - `kabrapratik28 `_ - `JulianGriggs `_ - `oprblackout `_ +- `NoahTheDuke `_ diff --git a/README.rst b/README.rst index b031554..39e09c1 100644 --- a/README.rst +++ b/README.rst @@ -39,6 +39,7 @@ Algorithms implemented so far: - Selection Sort - Shell Sort - Gnome Sort +- Strand Sort **Searching:** diff --git a/TODO.rst b/TODO.rst index 01fcc44..0acbd04 100644 --- a/TODO.rst +++ b/TODO.rst @@ -11,7 +11,6 @@ Below is an ever changing list of things that I would like to accomplish or impl - Binary Tree Sort - Cycle Sort - Smoothsort - - Strand Sort - Divide and Conquer - Maximum Subarray - Strassen's Matrix Multiplication diff --git a/algorithms/sorting/strand_sort.py b/algorithms/sorting/strand_sort.py new file mode 100644 index 0000000..bdff00a --- /dev/null +++ b/algorithms/sorting/strand_sort.py @@ -0,0 +1,69 @@ +""" + strand_sort.py + + Implementation of strand sort on a list and returns a sorted list. + + Strand Sort Overview: + ------------------------ + Repeatedly pulls sorted sublists out of the unsorted list and merges them + with a result array. + + Time Complexity: O(n**2) worst case + + Space Complexity: O(1) auxiliary + + Stable: Yes + + Psuedo Code: https://en.wikipedia.org/wiki/Strand_sort +""" + +from collections import deque + + +def sort(array): + if len(array) < 2: + return array + result = [] + while array: + sublist = [array.pop()] + last = sublist[0] + sub_append = sublist.append + leftovers = deque() + left_append = leftovers.append + for item in array: + if item >= last: + sub_append(item) + last = item + else: + left_append(item) + result = merge(result, sublist) + array = leftovers + return result + + +def merge(left, right): + merged_list = [] + merged_list_append = merged_list.append + + it_left = iter(left) + it_right = iter(right) + + left = next(it_left, None) + right = next(it_right, None) + + while left is not None and right is not None: + if left > right: + merged_list_append(right) + right = next(it_right, None) + else: + merged_list_append(left) + left = next(it_left, None) + + if left: + merged_list_append(left) + merged_list.extend(i for i in it_left) + else: + merged_list_append(right) + merged_list.extend(i for i in it_right) + + return merged_list diff --git a/tests/test_sorting.py b/tests/test_sorting.py index 2a56f84..7032eed 100644 --- a/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -13,6 +13,7 @@ cocktail_sort, quick_sort_in_place, gnome_sort, + strand_sort, ) @@ -155,3 +156,13 @@ class TestGnomeSort(SortingAlgorithmTestCase): def test_gnomesort(self): self.output = gnome_sort.sort(self.input) self.assertEqual(self.correct, self.output) + + +class TestStrandSort(SortingAlgorithmTestCase): + """ + Tests Strand sort on a small range from 0-9 + """ + + def test_strandsort(self): + self.output = strand_sort.sort(self.input) + self.assertEqual(self.correct, self.output) From 6e725f66ada28202fabb880dc70c02d8d647b6b7 Mon Sep 17 00:00:00 2001 From: NoahTheDuke Date: Mon, 19 Oct 2015 09:36:58 -0400 Subject: [PATCH 66/89] Improved Strand Sort speed dramatically. Now handles already sorted list in O(1) time. --- algorithms/sorting/strand_sort.py | 55 ++++++++++++++----------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/algorithms/sorting/strand_sort.py b/algorithms/sorting/strand_sort.py index bdff00a..c205c5a 100644 --- a/algorithms/sorting/strand_sort.py +++ b/algorithms/sorting/strand_sort.py @@ -17,53 +17,48 @@ Psuedo Code: https://en.wikipedia.org/wiki/Strand_sort """ -from collections import deque - def sort(array): if len(array) < 2: return array result = [] while array: - sublist = [array.pop()] + sublist = [array.pop(0)] + leftovers = [] last = sublist[0] - sub_append = sublist.append - leftovers = deque() - left_append = leftovers.append + # For speed, frequently invoked functions are assigned to locally- + # scoped variables, which greatly reduces overhead in calling them. + sublist_append = sublist.append + leftovers_append = leftovers.append for item in array: if item >= last: - sub_append(item) + sublist_append(item) last = item else: - left_append(item) + leftovers_append(item) result = merge(result, sublist) array = leftovers return result def merge(left, right): - merged_list = [] - merged_list_append = merged_list.append - - it_left = iter(left) - it_right = iter(right) - - left = next(it_left, None) - right = next(it_right, None) + if not left: + return right + if not right: + return left - while left is not None and right is not None: - if left > right: - merged_list_append(right) - right = next(it_right, None) - else: - merged_list_append(left) - left = next(it_left, None) + if left[-1] > right[-1]: + left, right = right, left - if left: - merged_list_append(left) - merged_list.extend(i for i in it_left) - else: - merged_list_append(right) - merged_list.extend(i for i in it_right) + it = iter(right) + y = next(it) + result = [] - return merged_list + for x in left: + while y < x: + result.append(y) + y = next(it) + result.append(x) + result.append(y) + result.extend(it) + return result From 138c530aa93b03e1c0a66783e7b7d45b09598351 Mon Sep 17 00:00:00 2001 From: rafeh01 Date: Sun, 25 Oct 2015 02:46:23 -0500 Subject: [PATCH 67/89] add iterative Breadth First Search. --- .gitignore | 1 + README.rst | 1 + algorithms/searching/breadth_first_search.py | 29 ++++++++++++++++ tests/test_searching.py | 35 +++++++++++++++++++- 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 algorithms/searching/breadth_first_search.py diff --git a/.gitignore b/.gitignore index e5a450e..f807b8c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build/ *~ /dist/ /*.egg-info +*.ropeproject/ diff --git a/README.rst b/README.rst index 39e09c1..90465f8 100644 --- a/README.rst +++ b/README.rst @@ -48,6 +48,7 @@ Algorithms implemented so far: - Knuth-Morris-Pratt - Rabin-Karp - Depth First Search (Recursive) +- Breadth First Search (Iterative) **Shuffling:** diff --git a/algorithms/searching/breadth_first_search.py b/algorithms/searching/breadth_first_search.py new file mode 100644 index 0000000..6b08a00 --- /dev/null +++ b/algorithms/searching/breadth_first_search.py @@ -0,0 +1,29 @@ +""" + breadth_first_search.py + + Iterative implementation of BFS algorithm on a graph. + + Breadth First Search Overview: + ------------------------ + Used to traverse trees, tree structures or graphs. + Starts at a selected node (root) and explores the nearest + neighbor branches before proceeding further. + + Time Complexity: O(E + V) + E = Number of edges + V = Number of vertices (nodes) + + Pseudocode: https://en.wikipedia.org/wiki/Breadth-first_search +""" + + +def bfs(graph, start): + if start not in graph or graph[start] is None or graph[start] == []: + return None + visited, queue = set(), [start] + while queue: + vertex = queue.pop(0) + if vertex not in visited: + visited.add(vertex) + queue.extend(graph[vertex] - visited) + return visited diff --git a/tests/test_searching.py b/tests/test_searching.py index 197ff4e..fe12b5d 100644 --- a/tests/test_searching.py +++ b/tests/test_searching.py @@ -6,7 +6,8 @@ kmp_search, rabinkarp_search, bmh_search, - depth_first_search + depth_first_search, + breadth_first_search ) @@ -155,3 +156,35 @@ def test_dfs(self): [5, 1, 2, 4, 11, 12, 7, 8, 9, 10, 3, 6, 15, 13, 14] ) self.assertEqual(rv3e, None) + + +class TestBreadthFirstSearch(unittest.TestCase): + """ + Tests DFS on a graph represented by a adjacency list + """ + def test_bfs(self): + self.graph = { + 'A': set(['B', 'C']), + 'B': set(['A', 'D', 'E']), + 'C': set(['A', 'F']), + 'D': set(['B']), + 'E': set(['B', 'F']), + 'F': set(['C', 'E']) + } + rv1 = breadth_first_search.bfs(self.graph, 'A') + self.assertEqual(rv1, {'C', 'A', 'B', 'D', 'F', 'E'}) + self.graph = { + 'A': set(['B', 'C', 'E']), + 'B': set(['A', 'D', 'F']), + 'C': set(['A', 'G']), + 'D': set(['B']), + 'F': set(['B']), + 'E': set(['A']), + 'G': set(['C']) + } + rv1 = breadth_first_search.bfs(self.graph, "A") + rv2 = breadth_first_search.bfs(self.graph, "G") + rv1e = breadth_first_search.bfs(self.graph, "Z") + self.assertEqual(rv1, set(['A', 'B', 'D', 'F', 'C', 'G', 'E'])) + self.assertEqual(rv2, set(['G', 'C', 'A', 'B', 'D', 'F', 'E'])) + self.assertEqual(rv1e, None) From da35017741703e7f6cc178c61d85a872f38c41f5 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Thu, 8 Oct 2015 22:28:35 -0700 Subject: [PATCH 68/89] Add documentation to project, fixes #98 - Add base requirements file and doc requirements - Drastically clean up README and index on docs - Add Sorting docs - Add Shuffling docs - Add random docs - Add searching docs - Add math docs - Add factorization docs - Add dynamic programming docs - Add data structures docs --- .gitignore | 1 + README.rst | 103 ++----- .../data_structures/binary_search_tree.py | 194 +++++++----- algorithms/data_structures/digraph.py | 35 +-- algorithms/data_structures/queue.py | 42 ++- .../data_structures/singly_linked_list.py | 50 ++- algorithms/data_structures/stack.py | 39 ++- .../data_structures/undirected_graph.py | 36 +-- algorithms/data_structures/union_find.py | 25 +- .../data_structures/union_find_by_rank.py | 17 +- .../union_find_with_path_compression.py | 22 +- algorithms/dynamic_programming/lcs.py | 22 +- algorithms/factorization/fermat.py | 18 +- algorithms/factorization/pollard_rho.py | 31 +- algorithms/factorization/trial_division.py | 18 +- algorithms/math/approx_cdf.py | 11 + algorithms/math/extended_gcd.py | 15 +- algorithms/math/lcm.py | 15 +- algorithms/math/primality_test.py | 15 + algorithms/math/sieve_atkin.py | 12 +- algorithms/math/sieve_eratosthenes.py | 17 +- algorithms/math/std_normal_pdf.py | 10 + algorithms/random/mersenne_twister.py | 25 +- algorithms/searching/binary_search.py | 20 +- algorithms/searching/bmh_search.py | 22 +- algorithms/searching/depth_first_search.py | 26 +- algorithms/searching/kmp_search.py | 27 +- algorithms/searching/rabinkarp_search.py | 14 +- algorithms/shuffling/knuth.py | 17 +- algorithms/sorting/bogo_sort.py | 26 +- algorithms/sorting/bubble_sort.py | 17 +- algorithms/sorting/cocktail_sort.py | 22 +- algorithms/sorting/comb_sort.py | 16 +- algorithms/sorting/gnome_sort.py | 16 +- algorithms/sorting/heap_sort.py | 29 +- algorithms/sorting/insertion_sort.py | 17 +- algorithms/sorting/merge_sort.py | 23 +- algorithms/sorting/quick_sort.py | 14 +- algorithms/sorting/quick_sort_in_place.py | 34 ++- algorithms/sorting/selection_sort.py | 16 +- algorithms/sorting/shell_sort.py | 15 +- docs/Makefile | 192 ++++++++++++ docs/algorithms.rst | 14 + docs/conf.py | 289 ++++++++++++++++++ docs/dynamic_programming.rst | 7 + docs/factorization.rst | 17 ++ docs/index.rst | 69 +++++ docs/make.bat | 263 ++++++++++++++++ docs/math.rst | 37 +++ docs/random.rst | 7 + docs/searching.rst | 27 ++ docs/shuffling.rst | 7 + docs/sorting.rst | 62 ++++ requirements.txt | 5 + requirements/requirements-documentation.txt | 18 ++ 55 files changed, 1705 insertions(+), 453 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/algorithms.rst create mode 100644 docs/conf.py create mode 100644 docs/dynamic_programming.rst create mode 100644 docs/factorization.rst create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/math.rst create mode 100644 docs/random.rst create mode 100644 docs/searching.rst create mode 100644 docs/shuffling.rst create mode 100644 docs/sorting.rst create mode 100644 requirements.txt create mode 100644 requirements/requirements-documentation.txt diff --git a/.gitignore b/.gitignore index f807b8c..d2f1a82 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build/ /dist/ /*.egg-info *.ropeproject/ +docs/_build diff --git a/README.rst b/README.rst index 90465f8..9630a80 100644 --- a/README.rst +++ b/README.rst @@ -7,94 +7,42 @@ Algorithms .. image:: http://codecov.io/github/nryoung/algorithms/coverage.svg?branch=master :target: http://codecov.io/github/nryoung/algorithms?branch=master -This is an attempt to build a cohesive module of algorithms in Python. +Algorithms is a library of algorithms and data structures implemented in Python. -The purpose of this repo is to be a learning tool for myself and others. +The main purpose of this library is to be an educational tool. You probably +shouldn't use these in production, instead, opting for the optimized versions of +these algorithms that can be found else where. -I used psuedo code from various sources and I have listed them as references in the source code of each algorithm. +You should totally check out the docs for implementation details, complexities +and further info. -Algorithms implemented so far: ------------------------------- - -**Data Structures:** - -- Queue -- Stack -- Disjoint Set -- Single Linked List -- Undirected Graph -- Digraph - -**Sorting:** - -- Bogo Sort -- Bubble Sort -- Cocktail Sort -- Comb Sort -- Heap Sort -- Insertion Sort -- Merge Sort -- Quick Sort -- In Place Quick Sort -- Selection Sort -- Shell Sort -- Gnome Sort -- Strand Sort - -**Searching:** - -- Binary Search -- Boyer-Moore-Horspool -- Knuth-Morris-Pratt -- Rabin-Karp -- Depth First Search (Recursive) -- Breadth First Search (Iterative) - -**Shuffling:** - -- Knuth/Fisher-Yates Shuffle - -**Math:** +Usage +----- -- Extended GCD -- Standard Normal Probability Density Function -- Cumulative Density Function (Approximation; 16 digit precision for 300 iter.) -- Sieve of Eratosthenes +If you want to use the algorithms in your code it is as simple as: -**Dynamic Programming:** +:: -- Longest Common Subsequence + from algorithms.sorting import bubble_sort -**Random:** + my_list = bubble_sort.sort(my_list) -- Mersenne Twister +Features +-------- +- Pseudo code, algorithm complexities and futher info with each algorithm. +- Test coverage for each algorithm and data structure. +- Super sweet documentation. Installation: ------------- -If you want to use the algorithms directly, simply +Installation is as easy as: :: $ pip install algorithms -If you want to examine the algorithms source, then you should clone this repo. - -Usage: ------- - -Once installed you can simply do the following in your program: - -:: - - from algorithms.sorting import bubble_sort - - my_list = bubble_sort.sort(my_list) - - -All prequisites for the algorithms are listed in the source code for each algorithm. - Tests: ------ @@ -109,16 +57,5 @@ Pytest is used as the main test runner and all Unit Tests can be run with: Contributing: ------------- -If there is an algorithm or data structure that you do not see, but would like to add please feel free to do a pull request. I only ask two things: - -1. For each algorithm and data structure you implement please have corresponding unit tests to prove correctness. -2. Please make sure that your module follows similar style guidelines that are laid out in the other modules. - -I want to personally thank everybody that has contributed so far and your names will be added to `AUTHORS.rst`. - - -TODO: ------ -See `TODO.rst`_. - -.. _`TODO.rst`: TODO.rst +Contributions are always welcome. Check out the contributing guidelines to get +started. diff --git a/algorithms/data_structures/binary_search_tree.py b/algorithms/data_structures/binary_search_tree.py index 1efd4a8..eb6db79 100644 --- a/algorithms/data_structures/binary_search_tree.py +++ b/algorithms/data_structures/binary_search_tree.py @@ -1,54 +1,20 @@ """ - Binary Search Tree data structure implemented: - -------------------------------- + Binary Search Tree + ------------------ The Binary Search Tree represents an ordered symbol table of generic key-value pairs. Keys must be comparable. Does not permit duplicate keys. When assocating a value with a key already present in the BST, the previous value is replaced by the new one. This implementation is for an unbalanced BST. - It supports the following primary operations: - Method Description - ----------------------------------------- - size Return size of BST - get Retrieve value for key in BST - put Add key-value pair to BST - contains Check if key is in BST - is_empty Check if BST is empty - min_key Get the minimum key in BST - max_key Get the maximum key in BST - floor_key Get the biggest key that is less than or equal to key - ceiling_key Get the smallest key that is greater than or equal to key - rank Get get the number of keys less than key - select_key Get the key with a given rank - delete_min Delete the key-value pair with minimum key from BST - delete_max Delete the key-value pair with maximum key from BST - delete Delete key-value pair with given key from BST - keys Get all keys in BST in ascending order - - Method Worst Case Balanced Tree - ----------------------------------------- - size O(1) O(1) - get O(N) O(lg N) - put O(N) O(lg N) - contains O(N) O(lg N) - is_empty O(1) O(1) - min_key O(N) O(lg N) - max_key O(N) O(lg N) - floor_key O(N) O(lg N) - ceiling_key O(N) O(lg N) - rank O(N) O(lg N) - select_key O(N) O(lg N) - delete_min O(N) O(lg N) - delete_max O(N) O(lg N) - delete O(N) O(lg N) - keys O(N) O(N) - - Adapted from: http://algs4.cs.princeton.edu/32bst + Pseudo Code: http://algs4.cs.princeton.edu/32bst """ -class Node: +class Node(object): + """ + Implementation of a Node in a Binary Search Tree. + """ def __init__(self, key=None, val=None, size_of_subtree=1): self.key = key @@ -58,7 +24,10 @@ def __init__(self, key=None, val=None, size_of_subtree=1): self.right = None -class BinarySearchTree: +class BinarySearchTree(object): + """ + Implementation of a Binary Search Tree. + """ def __init__(self): self.root = None @@ -70,15 +39,23 @@ def _size(self, node): return node.size_of_subtree def size(self): - ''' + """ Return the number of nodes in the BST - ''' + + Worst Case Complexity: O(1) + + Balanced Tree Complexity: O(1) + """ return self._size(self.root) def is_empty(self): - ''' + """ Returns True if the BST is empty, False otherwise - ''' + + Worst Case Complexity: O(1) + + Balanced Tree Complexity: O(1) + """ return self.size() == 0 def _get(self, key, node): @@ -93,15 +70,23 @@ def _get(self, key, node): return node.val def get(self, key): - ''' + """ Return the value paired with 'key' - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ return self._get(key, self.root) def contains(self, key): - ''' + """ Returns True if the BST contains 'key', False otherwise - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ return self.get(key) is not None def _put(self, key, val, node): @@ -124,15 +109,19 @@ def _put(self, key, val, node): return node def put(self, key, val): - ''' + """ Add a new key-value pair. - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ self.root = self._put(key, val, self.root) def _min_node(self): - ''' + """ Return the node with the minimum key in the BST - ''' + """ min_node = self.root # Return none if empty BST if min_node is None: @@ -144,9 +133,13 @@ def _min_node(self): return min_node def min_key(self): - ''' + """ Return the minimum key in the BST - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ min_node = self._min_node() if min_node is None: return None @@ -154,9 +147,9 @@ def min_key(self): return min_node.key def _max_node(self): - ''' + """ Return the node with the maximum key in the BST - ''' + """ max_node = self.root # Return none if empty BST if max_node is None: @@ -168,9 +161,13 @@ def _max_node(self): return max_node def max_key(self): - ''' + """ Return the maximum key in the BST - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ max_node = self._max_node() if max_node is None: return None @@ -178,10 +175,10 @@ def max_key(self): return max_node.key def _floor_node(self, key, node): - ''' + """ Returns the node with the biggest key that is less than or equal to the given value 'key' - ''' + """ if node is None: return None @@ -202,10 +199,14 @@ def _floor_node(self, key, node): return node def floor_key(self, key): - ''' + """ Returns the biggest key that is less than or equal to the given value 'key' - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ floor_node = self._floor_node(key, self.root) if floor_node is None: return None @@ -213,10 +214,10 @@ def floor_key(self, key): return floor_node.key def _ceiling_node(self, key, node): - ''' + """ Returns the node with the smallest key that is greater than or equal to the given value 'key' - ''' + """ if node is None: return None @@ -235,10 +236,14 @@ def _ceiling_node(self, key, node): return node def ceiling_key(self, key): - ''' + """ Returns the smallest key that is greater than or equal to the given value 'key' - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ ceiling_node = self._ceiling_node(key, self.root) if ceiling_node is None: return None @@ -246,9 +251,9 @@ def ceiling_key(self, key): return ceiling_node.key def _select_node(self, rank, node): - ''' + """ Return the node with rank equal to 'rank' - ''' + """ if node is None: return None @@ -261,9 +266,13 @@ def _select_node(self, rank, node): return node def select_key(self, rank): - ''' + """ Return the key with rank equal to 'rank' - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ select_node = self._select_node(rank, self.root) if select_node is None: return None @@ -283,9 +292,13 @@ def _rank(self, key, node): return self._size(node.left) def rank(self, key): - ''' + """ Return the number of keys less than a given 'key'. - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ return self._rank(key, self.root) def _delete(self, key, node): @@ -310,9 +323,13 @@ def _delete(self, key, node): return node def delete(self, key): - ''' + """ Remove the node with key equal to 'key' - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ self.root = self._delete(key, self.root) def _delete_min(self, node): @@ -324,9 +341,14 @@ def _delete_min(self, node): return node def delete_min(self): - ''' + """ Remove the key-value pair with the smallest key. - ''' + + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ self.root = self._delete_min(self.root) def _delete_max(self, node): @@ -338,9 +360,13 @@ def _delete_max(self, node): return node def delete_max(self): - ''' + """ Remove the key-value pair with the largest key. - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(lg N) + """ self.root = self._delete_max(self.root) def _keys(self, node, keys): @@ -358,8 +384,12 @@ def _keys(self, node, keys): return keys def keys(self): - ''' + """ Return all of the keys in the BST in aschending order - ''' + + Worst Case Complexity: O(N) + + Balanced Tree Complexity: O(N) + """ keys = [] return self._keys(self.root, keys) diff --git a/algorithms/data_structures/digraph.py b/algorithms/data_structures/digraph.py index 9ab0a51..f0d4d7a 100644 --- a/algorithms/data_structures/digraph.py +++ b/algorithms/data_structures/digraph.py @@ -1,23 +1,10 @@ """ - Directed Graph data structure implemented: - -------------------------------------------- + Directed Graph + -------------- The Digraph class represents a directed graph of vertices - which can be any hashable value. + which can be any hashable value. Parallel edges and self-loops are permitted. - It supports the following two primary operations: - add_edge: add an edge to the graph O(1) - adj: return list of all of the vertices adjacent to a vertex O(1) - vertices: return list of all vertices in the graph O(V) - - It also supports the following secondary operations: - vertex_count: return the number of vertices O(1) - edge_count: return the number of edges O(1) - degree: return degree of the vertex O(1) - reverse: return a reversed version of the digraph O(V+E) - - Parallel edges and self-loops are permitted. - - Adapted from: http://algs4.cs.princeton.edu/42directed/Digraph.java.html + Pseudo Code: http://algs4.cs.princeton.edu/42directed/Digraph.java.html """ @@ -30,6 +17,8 @@ def __init__(self): def vertex_count(self): """ Returns the number of vertices in the graph. + + Worst Case Complexity: O(1) """ return self.__v_count @@ -37,6 +26,8 @@ def vertex_count(self): def edge_count(self): """ Returns the number of edges in the graph. + + Worst Case Complexity: O(1) """ return self.__e_count @@ -44,6 +35,8 @@ def edge_count(self): def add_edge(self, src, dest): """ Adds an undirected edge 'src'-'dest' to the graph. + + Worst Case Complexity O(1) """ if src in self.__adj: @@ -63,12 +56,16 @@ def add_edge(self, src, dest): def adj(self, src): """ Returns the vertices adjacent to vertex 'src'. + + Worst Case Complexity: O(1) """ return self.__adj[src] def outdegree(self, src): """ Returns the degree of the vertex 'src' + + Worst Case Complexity: O(1) """ if src in self.__adj: return len(self.__adj[src]) @@ -78,12 +75,16 @@ def outdegree(self, src): def vertices(self): """ Returns an iterable of all the vertices in the graph. + + Worst Case Complexity: O(V) """ return self.__adj.keys() def reverse(self): """ Returns the reverse of this digraph + + Worst Case Complexity: O(V+E) """ digraph_reversed = Digraph() old_vertices = self.vertices() diff --git a/algorithms/data_structures/queue.py b/algorithms/data_structures/queue.py index fb3a200..ef381d4 100644 --- a/algorithms/data_structures/queue.py +++ b/algorithms/data_structures/queue.py @@ -1,14 +1,15 @@ """ - Queue data structure implemented: - -------------------------------- - add : add element at last - remove : remove element from front - return value - is_empty : 1 value returned on empty - 0 value returned on not empty - size : return size of queue - - Time Complexity: O(1) + Queue + ----- + A Queue is a linear data structure, or more abstractly a sequential + collection. The entities in the collection are kept in order and the + principal (or only) operations on the collection are the addition of + entities to the rear terminal position, known as enqueue, and removal of + entities from the front terminal position, known as dequeue. This makes the + queue a First-In-First-Out (FIFO) data structure. In a FIFO data structure, + the first element added to the queue will be the first one to be removed. + + Pseudo Code: https://en.wikipedia.org/wiki/Queue_%28abstract_data_type%29 """ from collections import deque @@ -20,13 +21,34 @@ def __init__(self): self.queue_list = deque([]) def add(self, value): + """ + Add element as the last item in the Queue. + + Worst Case Complexity: O(1) + """ self.queue_list.append(value) def remove(self): + """ + Remove element from the front of the Queue and return it's value. + + Worst Case Complexity: O(1) + """ + return self.queue_list.popleft() def is_empty(self): + """ + Returns a boolean indicating if the Queue is empty. + + Worst Case Complexity: O(1) + """ return not len(self.queue_list) def size(self): + """ + Return size of the Queue. + + Worst Case Complexity: O(1) + """ return len(self.queue_list) diff --git a/algorithms/data_structures/singly_linked_list.py b/algorithms/data_structures/singly_linked_list.py index 33b7842..5aa3d15 100644 --- a/algorithms/data_structures/singly_linked_list.py +++ b/algorithms/data_structures/singly_linked_list.py @@ -1,12 +1,14 @@ """ - Singly Linked List data structure implemented: - -------------------------------- - add : add element to list - remove : remove element from list - search : search for value in list - size : return size of list - - Time Complexity: O(N) + Singly Linked List + ------------------ + A linked list is a data structure consisting of a group of nodes which + together represent a sequence. Under the simplest form, each node is + composed of data and a reference (in other words, a link) to the next + node in the sequence; more complex variants add additional links. This + structure allows for efficient insertion or removal of elements from any + position in the sequence. + + Pseudo Code: https://en.wikipedia.org/wiki/Linked_list """ @@ -16,16 +18,16 @@ def __init__(self, data=None, next=None): self.data = data self.next = next - def setData(self, data): + def set_data(self, data): self.data = data - def getData(self): + def get_data(self): return self.data - def setNext(self, next): + def set_next(self, next): self.next = next - def getNext(self): + def get_next(self): return self.next @@ -36,12 +38,22 @@ def __init__(self): self.size = 0 def add(self, value): + """ + Add element to list + + Time Complexity: O(N) + """ node = Node(value) - node.setNext(self.head) + node.set_next(self.head) self.head = node self.size += 1 def remove(self, value): + """ + Remove element from list + + Time Complexity: O(N) + """ current = self.head previous = None found = False @@ -57,16 +69,21 @@ def remove(self, value): if previous is None: # Head node self.head = current.next else: # None head node - previous.setNext(current.next) + previous.set_next(current.next) return found def search(self, value): + """ + Search for value in list + + Time Complexity: O(N) + """ current = self.head found = False while current and not found: - if current.getData() == value: + if current.get_data() == value: found = True else: current = current.next @@ -74,4 +91,7 @@ def search(self, value): return found def size(self): + """ + Return size of list + """ return self.size diff --git a/algorithms/data_structures/stack.py b/algorithms/data_structures/stack.py index fc05194..0ba5b39 100644 --- a/algorithms/data_structures/stack.py +++ b/algorithms/data_structures/stack.py @@ -1,14 +1,12 @@ """ - Stack data structure implemented: - -------------------------------- - add : add element at last - remove : remove element from last - return value - is_empty : 1 value returned on empty - 0 value returned on not empty - size : return size of stack - - Time Complexity: O(1) + Stack + ----- + A stack or LIFO (last in, first out) is an abstract data type that serves + as a collection of elements, with two principal operations: push, which + adds an element to the collection, and pop, which removes the last element + that was added. + + Pseudo Code: https://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29 """ @@ -19,13 +17,34 @@ def __init__(self): self.stack_list = [] def add(self, value): + """ + Add element at last + + Time Complexity: O(1) + """ self.stack_list.append(value) def remove(self): + """ + Remove element from last return value + + Time Complexity: O(1) + """ + return self.stack_list.pop() def is_empty(self): + """ + 1 value returned on empty 0 value returned on not empty + + Time Complexity: O(1) + """ return not len(self.stack_list) def size(self): + """ + Return size of stack + + Time Complexity: O(1) + """ return len(self.stack_list) diff --git a/algorithms/data_structures/undirected_graph.py b/algorithms/data_structures/undirected_graph.py index 458100f..c2a6e7b 100644 --- a/algorithms/data_structures/undirected_graph.py +++ b/algorithms/data_structures/undirected_graph.py @@ -1,23 +1,11 @@ """ - Unidrected Graph data structure implemented: - -------------------------------------------- - The Undirected_Graph class represents an undirected graph of vertices - which can be any hashable value. + Unidrected Graph + ---------------- + The Undirected_Graph class represents an undirected graph of vertices + which can be any hashable value. - It supports the following two primary operations: - add_edge: add an edge to the graph O(1) - adj: return list of all of the vertices adjacent to a vertex O(1) - vertices: return list of all vertices in the graph O(V) - - It also supports the followign secondary operations: - vertex_count: return the number of vertices O(1) - edge_count: return the number of edges O(1) - degree: return degree of the vertex O(1) - - Parallel edges and self-loops are permitted. - - Adapted from: http://algs4.cs.princeton.edu/41undirected/Graph.java.html - """ + Pseudo Code: http://algs4.cs.princeton.edu/41undirected/Graph.java.html +""" class Undirected_Graph: @@ -29,6 +17,8 @@ def __init__(self): def vertex_count(self): """ Returns the number of vertices in the graph. + + Time Complexity: O(1) """ return self.__v_count @@ -36,6 +26,8 @@ def vertex_count(self): def edge_count(self): """ Returns the number of edges in the graph. + + Time Complexity: O(1) """ return self.__e_count @@ -43,6 +35,8 @@ def edge_count(self): def add_edge(self, src, dest): """ Adds an undirected edge 'src'-'dest' to the graph. + + Time Complexity: O(1) """ if src in self.__adj: self.__adj[src].append(dest) @@ -61,12 +55,16 @@ def add_edge(self, src, dest): def adj(self, src): """ Returns the vertices adjacent to vertex 'src'. + + Time Complexity: O(1) """ return self.__adj[src] def degree(self, src): """ Returns the degree of the vertex 'src' + + Time Complexity: O(1) """ if src in self.__adj: return len(self.__adj[src]) @@ -76,6 +74,8 @@ def degree(self, src): def vertices(self): """ Returns an iterable of all the vertices in the graph. + + Time Complexity: O(V) """ return self.__adj.keys() diff --git a/algorithms/data_structures/union_find.py b/algorithms/data_structures/union_find.py index b019fd0..3b13d75 100644 --- a/algorithms/data_structures/union_find.py +++ b/algorithms/data_structures/union_find.py @@ -1,18 +1,23 @@ """ - union_find.py - A Naive Implementation of union find data structure. - Union Find Overview: - ------------------------ + Union Find: + ----------- A disjoint-set data structure, also called union-find data structure implements two functions: - union(A, B) - merge A's set with B's set - find(A) - finds what set A belongs to - Navie approach: - Find follows parent nodes until it reaches the root. - Union combines two trees into one by attaching the root of one to the - root of the other + + union(A, B) - merge A's set with B's set + + find(A) - finds what set A belongs to + + + Naive approach: + + Find follows parent nodes until it reaches the root. + Union combines two trees into one by attaching the root of one to the + root of the other + Time Complexity : O(N) (a highly unbalanced tree might be created, nothing better a linked-list) + Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure """ diff --git a/algorithms/data_structures/union_find_by_rank.py b/algorithms/data_structures/union_find_by_rank.py index 0c5190b..cb8dda8 100644 --- a/algorithms/data_structures/union_find_by_rank.py +++ b/algorithms/data_structures/union_find_by_rank.py @@ -1,15 +1,18 @@ """ - union_find_by_rank.py - An implementation of union find by rank data structure. - Union Find Overview: - ------------------------ + Union Find by Rank + ------------------ A disjoint-set data structure, also called union-find data structure implements two functions: - union(A, B) - merge A's set with B's set - find(A) - finds what set A belongs to + + union(A, B) - merge A's set with B's set + + find(A) - finds what set A belongs to + Union by rank approach: - attach the smaller tree to the root of the larger tree + attach the smaller tree to the root of the larger tree + Time Complexity : O(logn) + Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure """ diff --git a/algorithms/data_structures/union_find_with_path_compression.py b/algorithms/data_structures/union_find_with_path_compression.py index a183b92..3d2fa9b 100644 --- a/algorithms/data_structures/union_find_with_path_compression.py +++ b/algorithms/data_structures/union_find_with_path_compression.py @@ -1,18 +1,22 @@ """ - union_find_with_path_compression.py - An implementation of union find with path compression data structure. - Union Find Overview: - ------------------------ + Union Find with path compression + -------------------------------- A disjoint-set data structure, also called union-find data structure implements two functions: - union(A, B) - merge A's set with B's set - find(A) - finds what set A belongs to + + union(A, B) - merge A's set with B's set + + find(A) - finds what set A belongs to + Union with path compression approach: - Each node visited on the way to a root node may as well be attached - directly to the root node. - attach the smaller tree to the root of the larger tree + + Each node visited on the way to a root node may as well be attached + directly to the root node. + attach the smaller tree to the root of the larger tree + Time Complexity : O(a(n)), where a(n) is the inverse of the function n=f(x)=A(x,x) and A is the extremely fast-growing Ackermann function. + Psuedo Code: http://en.wikipedia.org/wiki/Disjoint-set_data_structure """ diff --git a/algorithms/dynamic_programming/lcs.py b/algorithms/dynamic_programming/lcs.py index b8c81f0..6bdabf2 100644 --- a/algorithms/dynamic_programming/lcs.py +++ b/algorithms/dynamic_programming/lcs.py @@ -1,18 +1,18 @@ """ - lcs.py - - This module implements the dynamic programming solution to - the longest common subsequence algorithm. - - Pre: two strings str1 and str2 - Post: a string representing the longest subsequence common to str1 and str2 + Longest Common Sunsequence + -------------------------- + Implements the dynamic programming solution to the longest common + subsequence algorithm. Pseudo Code: - http://en.wikipedia.org/wiki/Longest_common_subsequence_problem + http://en.wikipedia.org/wiki/Longest_common_subsequence_problem """ def build_lengths_matrix(str1, str2): + """ + XXX: Needs documentation written. + """ matrix = [[0 for j in range(len(str2)+1)] for i in range(len(str1)+1)] for i, x in enumerate(str1): for j, y in enumerate(str2): @@ -24,6 +24,9 @@ def build_lengths_matrix(str1, str2): def read_from_matrix(matrix, str1, str2): + """ + XXX: Needs documentation written. + """ result = "" i, j = len(str1), len(str2) while i != 0 and j != 0: @@ -39,5 +42,8 @@ def read_from_matrix(matrix, str1, str2): def lcs(str1, str2): + """ + XXX: Needs documentation written. + """ lengths = build_lengths_matrix(str1, str2) return read_from_matrix(lengths, str1, str2) diff --git a/algorithms/factorization/fermat.py b/algorithms/factorization/fermat.py index 6b89ecc..e132557 100644 --- a/algorithms/factorization/fermat.py +++ b/algorithms/factorization/fermat.py @@ -1,20 +1,22 @@ -from math import sqrt - """ - fermat.py - - Implementation of the Fermat factorization. - - Fermat factorization Overview: - ------------------------ + Fermat Factorization + -------------------- Fermat's factorization method is based on the representation of an odd integer as the difference of two squares: + N = a*a-b*b = (a-b)*(a+b) """ +from math import sqrt def fermat(n): + """ + Factorization of the integer `n`. + + :param n: An integer to be factored. + :rtype: The factorization of `n`. + """ if n & 1 == 0: return [n >> 1, 2] x = int(sqrt(n)) diff --git a/algorithms/factorization/pollard_rho.py b/algorithms/factorization/pollard_rho.py index b2ce508..a6aaf7d 100644 --- a/algorithms/factorization/pollard_rho.py +++ b/algorithms/factorization/pollard_rho.py @@ -1,29 +1,26 @@ -import random - -from algorithms.math.primality_test import is_prime -from fractions import gcd - """ - pollard_rho.py - - Implementation of the Pollard Rho algorithm. - - Pollard Rho algorithm Overview: - ------------------------ + Pollard Rho Algorithm + --------------------- Pollard's rho algorithm is a special-purpose integer factorization - algorithm. - It was invented by John Pollard in 1975. - It is particularly effective for a composite - number having a small prime factor. + algorithm. It was invented by John Pollard in 1975. It is particularly + effective for a composite number having a small prime factor. """ +import random + +from algorithms.math.primality_test import is_prime +from fractions import gcd def f(x): + """ + """ return x*x+1 def rho(n, x1=2, x2=2): + """ + """ if n % 2 == 0: return 2 i = 0 @@ -42,6 +39,8 @@ def rho(n, x1=2, x2=2): def pollard_rho_rec(x, factors): + """ + """ if x == 1: return @@ -55,6 +54,8 @@ def pollard_rho_rec(x, factors): def pollard_rho(x): + """ + """ if x == 1 or x == 0: return [x] factors = [] diff --git a/algorithms/factorization/trial_division.py b/algorithms/factorization/trial_division.py index c9e5c2b..dc1804d 100644 --- a/algorithms/factorization/trial_division.py +++ b/algorithms/factorization/trial_division.py @@ -1,21 +1,21 @@ -from algorithms.math.sieve_eratosthenes import eratosthenes - - """ - trial_division.py - - Implementation of the Trial division. - - Trial division Overview: - ------------------------ + Trial Division + -------------- Trial division is the most laborious but easiest to understand of the integer factorization algorithms. Try to divide a number n by all prime numbers < sqrt(n). """ +from algorithms.math.sieve_eratosthenes import eratosthenes def trial_division(n): + """ + Uses trial division to find prime factors of `n`. + + :param n: An integer to factor. + :rtype: The prime factors of `n` + """ prime_factors = [] if n < 2: return prime_factors diff --git a/algorithms/math/approx_cdf.py b/algorithms/math/approx_cdf.py index f92417a..346d24f 100644 --- a/algorithms/math/approx_cdf.py +++ b/algorithms/math/approx_cdf.py @@ -1,4 +1,6 @@ """ + Approximate Cumulative Distribution Function + -------------------------------------------- Calculates the cumulative distribution function (CDF) of the normal distribution based on an approximation by George Marsaglia: Marsaglia, George (2004). "Evaluating the Normal Distribution". @@ -7,6 +9,8 @@ 16 digit precision for 300 iterations when x = 10. Equation: + + f(x) = 1/2 + pdf(x) * (x + (x^3/3) + (x^5/3*5) + (x^7/3*7) + ...) """ @@ -14,7 +18,14 @@ def cdf(x, iterations=300): + """ + Calculates the cumulative distribution function of the normal distribution. + Uses a taylor exponent to calculate this. + :param x: An integer that represents the taylor exponent. + :param iterations: An integer representing the number of iterations. + :rtype: The normal distribution + """ product = 1.0 taylor_exp = [x] for i in range(3, iterations, 2): diff --git a/algorithms/math/extended_gcd.py b/algorithms/math/extended_gcd.py index 8a15469..ec9ed66 100644 --- a/algorithms/math/extended_gcd.py +++ b/algorithms/math/extended_gcd.py @@ -1,17 +1,20 @@ """ - extended_gcd.py - - This module implements the extended greatest common divider algorithm. - - Pre: two integers a and b - Post: a tuple (x, y) where a*x + b*y = gcd(a, b) + Extended Greatest Common Divisor + -------------------------------- + Implementation of the extended greatest common divisor algorithm. Pseudo Code: http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm """ def extended_gcd(p, q): + """ + Find the greatest common divisor and returns them. + :param a: An integer. + :param b: An integer. + :rtype: A tuple representing the greatest common divisor. + """ (a, b) = (p, q) if a < 0: diff --git a/algorithms/math/lcm.py b/algorithms/math/lcm.py index 35f1c77..4801ef6 100644 --- a/algorithms/math/lcm.py +++ b/algorithms/math/lcm.py @@ -1,6 +1,19 @@ +""" + Lowest Common Multiple + ---------------------- + Simple implementation of the Lowest Common Multiple Algorithm. + + Pseudo Code: https://en.wikipedia.org/wiki/Least_common_multiple +""" + + def lcm(a, b): """ - Simple version of lcm, that does not have any dependencies + Simple version of lcm, that does not have any dependencies. + + :param a: Integer + :param b: Integer + :rtype: The lowest common multiple of integers a and b """ tmp_a = a while (tmp_a % b) != 0: diff --git a/algorithms/math/primality_test.py b/algorithms/math/primality_test.py index 769204a..e6f5c5e 100644 --- a/algorithms/math/primality_test.py +++ b/algorithms/math/primality_test.py @@ -1,3 +1,10 @@ +""" + Primality Test + -------------- + Implementation of a Primality Test that uses a cache to improve + performance. + +""" from math import sqrt from algorithms.math.sieve_eratosthenes import eratosthenes @@ -9,6 +16,14 @@ def is_prime(number, cache=True): + """ + Takes `number` and determines if it is prime. + + :param number: The integer to be tested for primality. + :param cache: A boolean to determine if a cache should be used to + improve performance. + :rtype: A boolean that signifies if `number` is prime. + """ if number < 2: return False global primes_cache_list, primes_cache_bool diff --git a/algorithms/math/sieve_atkin.py b/algorithms/math/sieve_atkin.py index f678a91..c54e416 100644 --- a/algorithms/math/sieve_atkin.py +++ b/algorithms/math/sieve_atkin.py @@ -1,10 +1,6 @@ """ - sieve_atkin.py - - Implementation of the Sieve of Eratosthenes algorithm. - - Sieve of Atkin Overview: - ------------------------ + Sieve of Atkin + -------------- It is an optimized version of the ancient sieve of Eratosthenes which does some preliminary work and then marks off multiples of the square of each prime, rather than multiples of the prime @@ -18,6 +14,10 @@ def atkin(limit): + """ + :param limit: The upper limit in which to find all primes less than this + value. + """ if limit == 2: return [2] if limit == 3: diff --git a/algorithms/math/sieve_eratosthenes.py b/algorithms/math/sieve_eratosthenes.py index e4f1ff8..ffcbbd5 100644 --- a/algorithms/math/sieve_eratosthenes.py +++ b/algorithms/math/sieve_eratosthenes.py @@ -1,10 +1,6 @@ """ - sieve_eratosthenes.py - - Implementation of the Sieve of Eratosthenes algorithm. - - Sieve of Eratosthenes Overview: - ------------------------ + Sieve of Eratosthenes + --------------------- Is a simple, ancient algorithm for finding all prime numbers up to any given limit. It does so by iteratively marking as composite (i.e. not prime) the multiples of each prime, starting with the multiples @@ -20,6 +16,15 @@ def eratosthenes(end, start=2, return_boolean=False): + """ + Finds all primes < `end`. + + :param end: An integer. The upper limit of the range to look for primes. + :param start: An integer. The start of the range to look for primes. + :param return_boolean: A boolean. Represents the type of return type. + :rtype: Depending on `return_boolean` either returns boolean and primes or + just the primes. + """ primes = [] if end < start or end < 2: return [] diff --git a/algorithms/math/std_normal_pdf.py b/algorithms/math/std_normal_pdf.py index dfa4b74..1199e2b 100644 --- a/algorithms/math/std_normal_pdf.py +++ b/algorithms/math/std_normal_pdf.py @@ -1,4 +1,6 @@ """ + Standard Normal Probability Density Function + -------------------------------------------- Calculates the normal distribution's probability density function (PDF). Calculates Standard normal pdf for mean=0, std_dev=1. @@ -9,7 +11,15 @@ def pdf(x, mean=0, std_dev=1): + """ + Calculates the normal distribution's probability density + function. + :param x: An integer. + :param mean: An integer. + :param std_dev: An integer. + :rtype: The normal distribution + """ PI = 3.141592653589793 E = 2.718281828459045 term1 = 1.0 / ((2 * PI)**0.5) diff --git a/algorithms/random/mersenne_twister.py b/algorithms/random/mersenne_twister.py index 631e5bd..6d28daf 100644 --- a/algorithms/random/mersenne_twister.py +++ b/algorithms/random/mersenne_twister.py @@ -1,10 +1,6 @@ """ - mersenne_twister.py - - Implementation of Mersenne Twister pseudo random number generator - - Mersenne Twister Overview: - --------------------------- + Mersenne Twister + ---------------- Generates high quality pseudo random integers with a long period. Used as the default random number generator for several languages (including Python). @@ -21,7 +17,11 @@ def __init__(self): self.index = 0 def seed(self, seed): - """Initialize generator""" + """ + Initialize generator. + + :param seed: An integer value to seed the generator with + """ self.state = [] self.index = 0 self.state.append(seed) @@ -31,7 +31,11 @@ def seed(self, seed): self.state.append(n) def randint(self): - """Extract random number""" + """ + Extracts a random number. + + :rtype: A random integer + """ if self.index == 0: self.generate() @@ -45,7 +49,10 @@ def randint(self): return y def generate(self): - """Generate 624 new random numbers""" + """ + Generates 624 random numbers and stores in the state list. + + """ for i in range(624): n = self.state[i] & 0x80000000 n += self.state[(i+1) % 624] & 0x7fffffff diff --git a/algorithms/searching/binary_search.py b/algorithms/searching/binary_search.py index ae76eb3..1af6514 100644 --- a/algorithms/searching/binary_search.py +++ b/algorithms/searching/binary_search.py @@ -1,11 +1,7 @@ """ - binary_search.py - - Implementation of binary search on a sorted list. - - Binary Search Overview: - ------------------------ - Recursively partitions the list until the key is found. + Binary Search + ------------- + Recursively partitions the list until the `key` is found. Time Complexity: O(lg n) @@ -15,6 +11,16 @@ def search(seq, key): + """ + Takes a list of integers and searches if the `key` is contained within + the list. + + :param seq: A list of integers + :param key: The integer to be searched for + :rtype: The index of where the `key` is located in the list. If `key` is + not found then False is returned. + """ + lo = 0 hi = len(seq) - 1 diff --git a/algorithms/searching/bmh_search.py b/algorithms/searching/bmh_search.py index 7090287..ca4371e 100644 --- a/algorithms/searching/bmh_search.py +++ b/algorithms/searching/bmh_search.py @@ -1,12 +1,8 @@ """ - bmh_search.py - - Implementation of bmh search to find a substring in a string - - BMH Search Overview: - -------------------- - Uses a bad-character shift of the rightmost character of the window to - compute shifts. + BMH Search + ---------- + Search that attempts to find a substring in a string. Uses a bad-character + shift of the rightmost character of the window to compute shifts. Time: Complexity: O(m + n), where m is the substring to be found. @@ -18,6 +14,16 @@ def search(text, pattern): + """ + Takes a string and searches if the `pattern` is substring within `text`. + + :param text: A string that will be searched. + :param pattern: A string that will be searched as a substring within + `text`. + :rtype: The indices of all occurences of where the substring `pattern` + was found in `text`. + """ + pattern_length = len(pattern) text_length = len(text) offsets = [] diff --git a/algorithms/searching/depth_first_search.py b/algorithms/searching/depth_first_search.py index e1445ed..0454434 100644 --- a/algorithms/searching/depth_first_search.py +++ b/algorithms/searching/depth_first_search.py @@ -1,16 +1,14 @@ """ - depth_first_search.py - - Recursive implementation of DFS algorithm on a graph. - - Depth First Search Overview: - ------------------------ - Used to traverse trees, tree structures or graphs. - Starts at a selected node (root) and explores the branch - as far as possible before backtracking. + Depth First Search + ------------------ + Recursive implementation of the depth first search algorithm used to + traverse trees or graphs. Starts at a selected node (root) and explores the + branch as far as possible before backtracking. Time Complexity: O(E + V) + E = Number of edges + V = Number of vertices (nodes) Pseudocode: https://en.wikipedia.org/wiki/Depth-first_search @@ -18,6 +16,16 @@ def dfs(graph, start, path=[]): + """ + Depth first search that recursively searches the path. Backtracking occurs + only when the last node in the path is visited. + + :param graph: A dictionary of nodes and edges. + :param start: The node to start the recursive search with. + :param path: A list of edges to search. + :rtype: A boolean indicating whether the node is included in the path. + + """ if start not in graph or graph[start] is None or graph[start] == []: return None path = path + [start] diff --git a/algorithms/searching/kmp_search.py b/algorithms/searching/kmp_search.py index a8f5944..094a629 100644 --- a/algorithms/searching/kmp_search.py +++ b/algorithms/searching/kmp_search.py @@ -1,11 +1,9 @@ """ - kmp_search.py - Implementation of kmp search on a sorted list. - - KMP Search Overview: - ------------------------ - Uses a prefix function to reduce the searching time. + KMP Search + ---------- + Implementation of kmp search on string. Uses a prefix function to reduce + the searching time. Time Complexity: O(n + k), where k is the substring to be found @@ -15,6 +13,17 @@ def search(string, word): + """ + Searches for occurrences of a "word" within a main "string" by employing + the observation that when a mismatch occurs, the word itself embodies + sufficient information to determine where the next match could begin, + thus bypassing re-examination of previously matched characters. + + :param string: The string to be searched. + :param word: The sub string to be searched for. + :rtype: The indices of all occurences of where the substring is found in + the string. + """ word_length = len(word) string_length = len(string) offsets = [] @@ -36,6 +45,12 @@ def search(string, word): def compute_prefix(word): + """ + Returns the prefix of the word. + + :param word: The sub string that the prefix will be computed for. + :rtype: Returns computed prefix of the word. + """ word_length = len(word) prefix = [0] * word_length k = 0 diff --git a/algorithms/searching/rabinkarp_search.py b/algorithms/searching/rabinkarp_search.py index 0c5909d..94baf15 100644 --- a/algorithms/searching/rabinkarp_search.py +++ b/algorithms/searching/rabinkarp_search.py @@ -1,10 +1,8 @@ """ - rabinkarp_search.py - Implementation of Rabin-Karp search on a given string. - Rabin-Karp Search Overview: - ------------------------ + Rabin-Karp Search + ----------------- Search for a substring in a given string, by comparing hash values of the strings. @@ -18,6 +16,14 @@ def search(s, sub): + """ + Uses hashing to find any one of a set of pattern strings in a text. + + :param s: The string to be searched. + :param sub: The substring to be searched for. + :rtype: The indices of all occurences of where the substring is found in + the string. + """ n, m = len(s), len(sub) hsub_digest = md5(sub.encode('utf-8')).digest() offsets = [] diff --git a/algorithms/shuffling/knuth.py b/algorithms/shuffling/knuth.py index 1439647..f84248f 100644 --- a/algorithms/shuffling/knuth.py +++ b/algorithms/shuffling/knuth.py @@ -1,21 +1,26 @@ """ - knuth.py - Implementation of the Fisher-Yates/Knuth shuffle - - Fisher-Yates/Knuth Overview: - ---------------------------- + Fisher-Yates/Knuth + ------------------ Randomly picks integers to swap elements in an ubiased manner. Time Complexity: O(n) + Space Complexity: O(n)n - Pseudocode: http://en.wikipedia.org/wiki/Fisher%E1%80%93Yates_shuffle + Pseudocode: http://http://rosettacode.org/wiki/Knuth_shuffle """ from random import seed, randint def shuffle(seq): + """ + Takes a list of integers and randomly swaps the elements in an unbiased + manner. + + :param seq: A list of integers + :rtype: A list of shuffled integers + """ seed() for i in reversed(range(len(seq))): j = randint(0, i) diff --git a/algorithms/sorting/bogo_sort.py b/algorithms/sorting/bogo_sort.py index 7a9ff18..e8f903a 100644 --- a/algorithms/sorting/bogo_sort.py +++ b/algorithms/sorting/bogo_sort.py @@ -1,10 +1,6 @@ """ - bogo_sort.py - - Implementation of bogo sort on a list and returns a sorted list. - - Bogo Sort Overview: - ------------------- + Bogo Sort + --------- A naive sorting that picks two elements at random and swaps them. Time Complexity: O(n * n!) @@ -13,13 +9,23 @@ Stable: No - WARNING: This algorithm may never sort the list correctly. + Psuedo code: None + + **WARNING**: This algorithm may never sort the list correctly. """ import random def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :rtype: A list of sorted integers + """ + if len(seq) == 1: return seq random.seed() @@ -35,4 +41,10 @@ def sort(seq): def is_sorted(seq): + """ + Takes a list of integers and checks if the list is in sorted order. + + :param seq: A list of integers + :rtype: Boolean + """ return all(seq[i - 1] <= seq[i] for i in range(1, len(seq))) diff --git a/algorithms/sorting/bubble_sort.py b/algorithms/sorting/bubble_sort.py index aaa823e..fc5c7b8 100644 --- a/algorithms/sorting/bubble_sort.py +++ b/algorithms/sorting/bubble_sort.py @@ -1,11 +1,7 @@ """ - bubble_sort.py - - Implementation of bubble sort on a list and returns a sorted list. - - Bubble Sort Overview: - --------------------- - A naive sorting that compares and swaps adjacent elements + Bubble Sort + ----------- + A naive sorting that compares and swaps adjacent elements. Time Complexity: O(n**2) @@ -19,6 +15,13 @@ def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :rtype: A list of sorted integers + """ L = len(seq) for _ in range(L): for n in range(1, L): diff --git a/algorithms/sorting/cocktail_sort.py b/algorithms/sorting/cocktail_sort.py index 6ca6756..635da03 100644 --- a/algorithms/sorting/cocktail_sort.py +++ b/algorithms/sorting/cocktail_sort.py @@ -1,13 +1,8 @@ """ - cocktail_sort.py - - Implementation of cocktail sort (aka bidirectional bubble sort, - or the happy hour sort) on a list. - - Cocktail Sort Overview: - ------------------------ - Walk the list bidirectionally, swapping neighbors if one should come - before/after the other. + Cocktail Sort + ------------- + A bidirectional bubble sort. Walks the elements bidirectionally, swapping + neighbors if one should come before/after the other. Time Complexity: O(n**2) @@ -16,10 +11,19 @@ Stable: Yes Psuedo Code: http://en.wikipedia.org/wiki/Cocktail_sort + """ def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :rtype: A list of sorted integers + """ + lower_bound = -1 upper_bound = len(seq) - 1 swapped = True diff --git a/algorithms/sorting/comb_sort.py b/algorithms/sorting/comb_sort.py index a50379b..ede33d1 100644 --- a/algorithms/sorting/comb_sort.py +++ b/algorithms/sorting/comb_sort.py @@ -1,10 +1,6 @@ """ - comb_sort.py - - Implementation of comb sort on a list and returns a sorted list. - - Comb Sort Overview: - ------------------- + Comb Sort + --------- Improves on bubble sort by using a gap sequence to remove turtles. Time Complexity: O(n**2) @@ -19,6 +15,14 @@ def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :rtype: A list of sorted integers + """ + gap = len(seq) swap = True diff --git a/algorithms/sorting/gnome_sort.py b/algorithms/sorting/gnome_sort.py index e55e70c..6c4d178 100644 --- a/algorithms/sorting/gnome_sort.py +++ b/algorithms/sorting/gnome_sort.py @@ -1,14 +1,10 @@ """ - gnome_sort.py - - Implementation of gnome sort on a list and returns a sorted list. - - Gnome Sort Overview: - --------------------- + Gnome Sort + ---------- A sorting algorithm similar to insertion sort except that the element is moved to its proper place by a series of swaps. - Time Complexity: O(n^2) + Time Complexity: O(n**2) Space Complexity: O(1) auxillary @@ -20,7 +16,13 @@ def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + :param seq: A list of integers + :rtype: A list of sorted integers + """ i = 1 last = 0 while i < len(seq): diff --git a/algorithms/sorting/heap_sort.py b/algorithms/sorting/heap_sort.py index d9b1ddb..d10729c 100644 --- a/algorithms/sorting/heap_sort.py +++ b/algorithms/sorting/heap_sort.py @@ -1,10 +1,6 @@ """ - heap_sort.py - - Implementation of heap sort on a list and returns a sorted list. - - Heap Sort Overview: - ------------------- + Heap Sort + --------- Uses the max heap data structure implemented in a list. Time Complexity: O(n log n) @@ -19,6 +15,15 @@ def max_heapify(seq, i, n): + """ + The function of max_heapify is to let the value at seq[i] "float down" in + the max-heap so that the subtree rooted at index i becomes a max-heap. + + :param seq: A list of integers + :param i: An integer that is an index in to the list that represents the + root of a subtree that max heapify is called on. + :param n: length of the list + """ l = 2 * i + 1 r = 2 * i + 2 @@ -35,12 +40,24 @@ def max_heapify(seq, i, n): def build_heap(seq): + """ + Continously calls max_heapify on the list for each subtree. + + :param seq: A list of integers + """ n = len(seq) - 1 for i in range(n//2, -1, -1): max_heapify(seq, i, n) def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :rtype: A list of sorted integers + """ build_heap(seq) heap_size = len(seq) - 1 for x in range(heap_size, 0, -1): diff --git a/algorithms/sorting/insertion_sort.py b/algorithms/sorting/insertion_sort.py index 91d2167..24d4360 100644 --- a/algorithms/sorting/insertion_sort.py +++ b/algorithms/sorting/insertion_sort.py @@ -1,11 +1,7 @@ """ - insertion_sort.py - - Implemenation of insertion sort on a list and returns a sorted list. - - Insertion Sort Overview: - ------------------------ - Uses insertion of elements in to the list to sort the list. + Insertion Sort + -------------- + A sort that uses the insertion of elements in to the list to sort the list. Time Complexity: O(n**2) @@ -19,6 +15,13 @@ def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :rtype: A list of integers + """ for n in range(1, len(seq)): item = seq[n] hole = n diff --git a/algorithms/sorting/merge_sort.py b/algorithms/sorting/merge_sort.py index 6ec22a5..9cdaa83 100644 --- a/algorithms/sorting/merge_sort.py +++ b/algorithms/sorting/merge_sort.py @@ -1,10 +1,6 @@ """ - merge_sort.py - - Implementation of merge sort on a list and returns a sorted list. - - Merge Sort Overview: - ------------------------ + Merge Sort + ---------- Uses divide and conquer to recursively divide and sort the list Time Complexity: O(n log n) @@ -19,6 +15,14 @@ def merge(left, right): + """ + Takes two sorted sub lists and merges them in to a single sorted sub list + and returns it. + + :param left: A list of sorted integers + :param right: A list of sorted integers + :rtype: A list of sorted integers + """ result = [] n, m = 0, 0 while n < len(left) and m < len(right): @@ -35,6 +39,13 @@ def merge(left, right): def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :rtype: A list of sorted integers + """ if len(seq) <= 1: return seq diff --git a/algorithms/sorting/quick_sort.py b/algorithms/sorting/quick_sort.py index 5f74794..2888102 100644 --- a/algorithms/sorting/quick_sort.py +++ b/algorithms/sorting/quick_sort.py @@ -1,10 +1,6 @@ """ - quick_sort.py - - Implementation of quick sort on a list and returns a sorted list. - - Quick Sort Overview: - ------------------------ + Quick Sort + ---------- Uses partitioning to recursively divide and sort the list Time Complexity: O(n**2) worst case @@ -19,7 +15,13 @@ def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + :param seq: A list of integers + :rtype: A list of sorted integers + """ if len(seq) <= 1: return seq else: diff --git a/algorithms/sorting/quick_sort_in_place.py b/algorithms/sorting/quick_sort_in_place.py index 2d943b1..512ead0 100644 --- a/algorithms/sorting/quick_sort_in_place.py +++ b/algorithms/sorting/quick_sort_in_place.py @@ -1,11 +1,6 @@ """ - quick_sort_in_place.py - - Implementation of quick sort on a list and returns a sorted list. - In-place version. - - Quick Sort Overview: - ------------------------ + Quick Sort in Place + ------------------- Uses partitioning to recursively divide and sort the list Time Complexity: O(n**2) worst case @@ -14,13 +9,24 @@ Stable: No - Psuedo Code: http://en.wikipedia.org/wiki/Quicksort#In-place_version + Psuedo Code: http://rosettacode.org/wiki/Quick_Sort """ from random import randrange def partition(seq, left, right, pivot_index): + """ + Reorders the slice with values lower than the pivot at the left side, + and values bigger than it at the right side. + Also returns the store index. + + :param seq: A list of integers + :param left: An integer representing left index + :param right: An integer representing left index + :param pivot_index: An integer that we're pivoting off + :rtype: An stored_index integer + """ pivot_value = seq[pivot_index] seq[pivot_index], seq[right] = seq[right], seq[pivot_index] store_index = left @@ -33,11 +39,19 @@ def partition(seq, left, right, pivot_index): def sort(seq, left, right): - """in-place version of quicksort""" + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :param left: An integer representing the beginning index + :param right: An integer representing the end index + :rtype: A list of sorted integers + """ + if len(seq) <= 1: return seq elif left < right: - # pivot = (left+right)/2 pivot = randrange(left, right) pivot_new_index = partition(seq, left, right, pivot) sort(seq, left, pivot_new_index - 1) diff --git a/algorithms/sorting/selection_sort.py b/algorithms/sorting/selection_sort.py index 5e4abdc..881054c 100644 --- a/algorithms/sorting/selection_sort.py +++ b/algorithms/sorting/selection_sort.py @@ -1,11 +1,7 @@ """ - selection_sort.py - - Implementation of selection sort on a list and returns a sorted list. - - Selection Sort Overview: - ------------------------ - Uses in-place comparision to sort the list + Selection Sort + -------------- + A sorting that uses in-place comparison. Time Complexity: O(n**2) @@ -19,7 +15,13 @@ def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + :param seq: A list of integers + :rtype: A list of sorted integers + """ for i in range(0, len(seq)): iMin = i for j in range(i+1, len(seq)): diff --git a/algorithms/sorting/shell_sort.py b/algorithms/sorting/shell_sort.py index 40c2647..6b2346e 100644 --- a/algorithms/sorting/shell_sort.py +++ b/algorithms/sorting/shell_sort.py @@ -1,10 +1,6 @@ """ - shell_sort.py - - Implementation of shell sort on an list and returns a sorted list. - - Shell Sort Overview: - ------------------------ + Shell Sort + ---------- Comparision sort that sorts far away elements first to sort the list Time Complexity: O(n**2) @@ -19,6 +15,13 @@ def sort(seq): + """ + Takes a list of integers and sorts them in ascending order. This sorted + list is then returned. + + :param seq: A list of integers + :rtype: A list of sorted integers + """ gaps = [x for x in range(len(seq) // 2, 0, -1)] diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..4a75883 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,192 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/algorithms.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/algorithms.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/algorithms" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/algorithms" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/algorithms.rst b/docs/algorithms.rst new file mode 100644 index 0000000..bed6ece --- /dev/null +++ b/docs/algorithms.rst @@ -0,0 +1,14 @@ +Algorithms +========== + +.. toctree:: + :maxdepth: 2 + + data_structures + dynamic_programming + factorization + math + random + searching + shuffling + sorting diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..22e2f80 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# algorithms documentation build configuration file, created by +# sphinx-quickstart on Thu Oct 8 22:36:00 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('..')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'algorithms' +copyright = '2015, Nic Young' +author = 'Nic Young' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'algorithmsdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'algorithms.tex', 'algorithms Documentation', + 'Nic Young', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'algorithms', 'algorithms Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'algorithms', 'algorithms Documentation', + author, 'algorithms', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/dynamic_programming.rst b/docs/dynamic_programming.rst new file mode 100644 index 0000000..f031664 --- /dev/null +++ b/docs/dynamic_programming.rst @@ -0,0 +1,7 @@ +Dynamic Programming +=================== + +.. automodule:: algorithms.dynamic_programming.lcs + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/factorization.rst b/docs/factorization.rst new file mode 100644 index 0000000..608785d --- /dev/null +++ b/docs/factorization.rst @@ -0,0 +1,17 @@ +Factorization +============= + +.. automodule:: algorithms.factorization.fermat + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.factorization.pollard_rho + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.factorization.trial_division + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..099d1e4 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,69 @@ +.. algorithms documentation master file, created by + sphinx-quickstart on Thu Oct 8 22:36:00 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Algorithms +========== + +Algorithms is a library of algorithms and data structures implemented in Python. + +The main purpose of this library is to be an educational tool. You probably +shouldn't use these in production, instead, opting for the optimized versions of +these algorithms that can be found else where. + +You should totally check out the docs for implementation details, complexities +and further info. + +Usage +----- + +If you want to use the algorithms in your code it is as simple as: + +:: + + from algorithms.sorting import bubble_sort + + my_list = bubble_sort.sort(my_list) + +Features +-------- + +- Pseudo code, algorithm complexities and futher info with each algorithm. +- Test coverage for each algorithm and data structure. +- Super sweet documentation. + +Installation: +------------- + +Installation is as easy as: + +:: + + $ pip install algorithms + + +Tests: +------ + +Pytest is used as the main test runner and all Unit Tests can be run with: + +:: + + $ ./run_tests.py + + +Contributing: +------------- + +Contributions are always welcome. Check out the contributing guidelines to get +started. + + +Table of Contents: +------------------ + +.. toctree:: + :maxdepth: 2 + + algorithms diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..7ec5491 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\algorithms.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\algorithms.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/docs/math.rst b/docs/math.rst new file mode 100644 index 0000000..dca4977 --- /dev/null +++ b/docs/math.rst @@ -0,0 +1,37 @@ +Math +==== + +.. automodule:: algorithms.math.approx_cdf + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.math.extended_gcd + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.math.lcm + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.math.primality_test + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.math.sieve_atkin + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.math.sieve_eratosthenes + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.math.std_normal_pdf + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/random.rst b/docs/random.rst new file mode 100644 index 0000000..18f3fbb --- /dev/null +++ b/docs/random.rst @@ -0,0 +1,7 @@ +Random +====== + +.. automodule:: algorithms.random.mersenne_twister + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/searching.rst b/docs/searching.rst new file mode 100644 index 0000000..474a236 --- /dev/null +++ b/docs/searching.rst @@ -0,0 +1,27 @@ +Searching +========= + +.. automodule:: algorithms.searching.binary_search + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.searching.bmh_search + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.searching.depth_first_search + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.searching.kmp_search + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.searching.rabinkarp_search + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/shuffling.rst b/docs/shuffling.rst new file mode 100644 index 0000000..058b5cc --- /dev/null +++ b/docs/shuffling.rst @@ -0,0 +1,7 @@ +Shuffling +========= + +.. automodule:: algorithms.shuffling.knuth + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/sorting.rst b/docs/sorting.rst new file mode 100644 index 0000000..d0f1746 --- /dev/null +++ b/docs/sorting.rst @@ -0,0 +1,62 @@ +Sorting +======= + +.. automodule:: algorithms.sorting.bogo_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.bubble_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.cocktail_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.comb_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.gnome_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.heap_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.insertion_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.merge_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.quick_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.quick_sort_in_place + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.selection_sort + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.sorting.shell_sort + :members: + :undoc-members: + :show-inheritance: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..878c520 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +# This will install *all* of the requirements. If you only want to install +# a subset of the requirements check out the requirements directory + +-r requirements/requirements-testing.txt +-r requirements/requirements-documentation.txt diff --git a/requirements/requirements-documentation.txt b/requirements/requirements-documentation.txt new file mode 100644 index 0000000..6c957a5 --- /dev/null +++ b/requirements/requirements-documentation.txt @@ -0,0 +1,18 @@ +alabaster==0.7.6 +argh==0.26.1 +Babel==2.1.1 +docutils==0.12 +Jinja2==2.8 +livereload==2.4.0 +MarkupSafe==0.23 +pathtools==0.1.2 +Pygments==2.0.2 +pytz==2015.6 +PyYAML==3.11 +six==1.10.0 +snowballstemmer==1.2.0 +Sphinx==1.3.1 +sphinx-autobuild==0.5.2 +sphinx-rtd-theme==0.1.9 +tornado==4.2.1 +watchdog==0.8.3 From 209094cd4ef139de9bc1f62b085737549bba6653 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 26 Dec 2015 15:53:17 -0800 Subject: [PATCH 69/89] Add docs link to README --- README.rst | 7 +++++-- docs/index.rst | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9630a80..27a41f5 100644 --- a/README.rst +++ b/README.rst @@ -13,7 +13,7 @@ The main purpose of this library is to be an educational tool. You probably shouldn't use these in production, instead, opting for the optimized versions of these algorithms that can be found else where. -You should totally check out the docs for implementation details, complexities +You should totally check out the `docs`_ for implementation details, complexities and further info. Usage @@ -32,7 +32,7 @@ Features - Pseudo code, algorithm complexities and futher info with each algorithm. - Test coverage for each algorithm and data structure. -- Super sweet documentation. +- Super sweet `documentation`_. Installation: ------------- @@ -59,3 +59,6 @@ Contributing: Contributions are always welcome. Check out the contributing guidelines to get started. + +.. _`docs`: http://algorithms.readthedocs.org/en/latest/ +.. _`documentation`: http://algorithms.readthedocs.org/en/latest/ diff --git a/docs/index.rst b/docs/index.rst index 099d1e4..8ec52ab 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,7 +12,7 @@ The main purpose of this library is to be an educational tool. You probably shouldn't use these in production, instead, opting for the optimized versions of these algorithms that can be found else where. -You should totally check out the docs for implementation details, complexities +You should totally check out the `docs`_ for implementation details, complexities and further info. Usage @@ -30,8 +30,8 @@ Features -------- - Pseudo code, algorithm complexities and futher info with each algorithm. -- Test coverage for each algorithm and data structure. -- Super sweet documentation. +- Test coverage for each algorithm and data structure. +- Super sweet `documentation`_. Installation: ------------- @@ -59,6 +59,8 @@ Contributing: Contributions are always welcome. Check out the contributing guidelines to get started. +.. _`docs`: http://algorithms.readthedocs.org/en/latest/ +.. _`documentation`: http://algorithms.readthedocs.org/en/latest/ Table of Contents: ------------------ From 0750695cb751b6381180ecb6b562d676e55c7d41 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 26 Dec 2015 16:09:12 -0800 Subject: [PATCH 70/89] Add docs badge to README --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 27a41f5..d6f876a 100644 --- a/README.rst +++ b/README.rst @@ -7,6 +7,9 @@ Algorithms .. image:: http://codecov.io/github/nryoung/algorithms/coverage.svg?branch=master :target: http://codecov.io/github/nryoung/algorithms?branch=master +.. image:: https://readthedocs.org/projects/algorithms/badge/?version=latest + :target: http://algorithms.readthedocs.org/en/latest/?badge=latest + Algorithms is a library of algorithms and data structures implemented in Python. The main purpose of this library is to be an educational tool. You probably From ed1fdc1d4e95bc1979dfe7f9e3d5f42776af701f Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 26 Dec 2015 16:15:55 -0800 Subject: [PATCH 71/89] Add data structure docs to repo. I forgot to add this file for tracking. --- docs/data_structures.rst | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/data_structures.rst diff --git a/docs/data_structures.rst b/docs/data_structures.rst new file mode 100644 index 0000000..ab58d3a --- /dev/null +++ b/docs/data_structures.rst @@ -0,0 +1,47 @@ +Data Structures +=============== + +.. automodule:: algorithms.data_structures.binary_search_tree + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.data_structures.digraph + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.data_structures.queue + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.data_structures.singly_linked_list + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.data_structures.stack + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.data_structures.undirected_graph + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.data_structures.union_find + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.data_structures.union_find_by_rank + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: algorithms.data_structures.union_find_with_path_compression + :members: + :undoc-members: + :show-inheritance: From 939ecb9cd3b0346817c447eec804994c2ce09d10 Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sat, 26 Dec 2015 17:15:54 -0800 Subject: [PATCH 72/89] Update version to 1.0, McLovin is finally here. - Add pypi version badge, fixes #128 --- README.rst | 3 +++ setup.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d6f876a..3b4acde 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,9 @@ Algorithms .. image:: https://readthedocs.org/projects/algorithms/badge/?version=latest :target: http://algorithms.readthedocs.org/en/latest/?badge=latest +.. image:: https://badge.fury.io/py/algorithms.svg + :target: https://badge.fury.io/py/algorithms + Algorithms is a library of algorithms and data structures implemented in Python. The main purpose of this library is to be an educational tool. You probably diff --git a/setup.py b/setup.py index b6902ff..480a0ff 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ def long_description(): return readme setup(name='algorithms', - version='0.1', + version='1.0', description='module of algorithms for Python', long_description=long_description(), url='https://github.com/nryoung/algorithms', From 2c8d1b3ee5000687fc08c33c07b6e1f60b1a2b5e Mon Sep 17 00:00:00 2001 From: Nic Young Date: Sun, 7 Feb 2016 14:23:10 -0800 Subject: [PATCH 73/89] Drop virtualenv version to < 14 since it no longer supports py3.2 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3c3f5b7..482ebae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,8 @@ matrix: - env: TOX_ENV=py35 install: - - pip install tox + # virtualenv>=14.0.0 has dropped Python 3.2 support + - travis_retry pip install "virtualenv<14.0.0" "tox>=1.9" script: - tox -e $TOX_ENV From bea5b858061859bd801928dc7379faeafc983767 Mon Sep 17 00:00:00 2001 From: Khalid GHIBOUB Date: Sat, 6 Feb 2016 11:28:19 +0100 Subject: [PATCH 74/89] Add Suffix and LCP arrays algorithms --- .travis.yml | 3 +- algorithms/data_structures/lcp_array.py | 99 +++++++++++++++++++++++++ docs/sorting.rst | 5 ++ tests/test_data_structures.py | 45 ++++++++++- 4 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 algorithms/data_structures/lcp_array.py diff --git a/.travis.yml b/.travis.yml index 3c3f5b7..482ebae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,8 @@ matrix: - env: TOX_ENV=py35 install: - - pip install tox + # virtualenv>=14.0.0 has dropped Python 3.2 support + - travis_retry pip install "virtualenv<14.0.0" "tox>=1.9" script: - tox -e $TOX_ENV diff --git a/algorithms/data_structures/lcp_array.py b/algorithms/data_structures/lcp_array.py new file mode 100644 index 0000000..6252b2d --- /dev/null +++ b/algorithms/data_structures/lcp_array.py @@ -0,0 +1,99 @@ +import math + +""" + Suffix Array + ------------------ + In computer science, a suffix array is a sorted array of all suffixes + of a string. It is a data structure used, among others, in full text + indices, data compression algorithms and within the field + of bioinformatics. + + for more info : http://algs4.cs.princeton.edu/63suffix/ + Complexity : + worst case : O(n log(n)) +""" + + +def suffix_array(t): + """ + Suffix array of a string t + :param t: the string to extract suffix array from + :return (s_array, rank): return a tuple that contain the suffix array + and the rank array which is the reversed version of the suffix array + """ + length = len(t) + rank = [0] * length + s_array = [0] * length + tuple_array = [0] * length + iterations = int(math.log(length, 2)) + 1 + size = 1 + + for i, t in enumerate(t): + s_array[i] = ord(t) + + for _ in range(iterations): + for i, ele in enumerate(tuple_array): + if i + size < length: + tuple_array[i] = ((s_array[i], s_array[i + size]), i) + else: + tuple_array[i] = ((s_array[i], -1), i) + tuple_array.sort() + s_array[tuple_array[0][1]] = 0 + for i in range(1, len(tuple_array)): + cls, idx = tuple_array[i] + if cls == tuple_array[i - 1][0]: + s_array[idx] = s_array[tuple_array[i - 1][1]] + else: + s_array[idx] = s_array[tuple_array[i - 1][1]] + 1 + size *= 2 + + for i, p in enumerate(s_array): + rank[p] = i + + return s_array, rank + + +""" + LCP Array + ------------------ + the longest common prefix array (LCP array) is an auxiliary data + structure to the suffix array. It stores the lengths of the longest + common prefixes (LCPs) between all pairs of consecutive suffixes + in a sorted suffix array. + + I use Kasai's algorithm in implementation : + Pseudo Code: http://algs4.cs.princeton.edu/32bst + + Complexity : + worst case :O(n) + +""" + + +def lcp_array(t_str, s_array, rank): + """ + + :param t_str: the string to calculate the lcp array for + :param s_array: the suffix array of the string + :param rank: the suffix array reversed + :return: the lcp array + """ + t_length = len(t_str) + lcp = [0] * t_length + last_lcp = 1 + + for i, ele in enumerate(s_array): + last_lcp = last_lcp - 1 if last_lcp > 1 else 0 + if ele == t_length - 1: + last_lcp = 0 + lcp[ele] = last_lcp + continue + n_suffix = rank[ele + 1] + + while i + last_lcp < t_length \ + and n_suffix + last_lcp < t_length \ + and t_str[i + last_lcp] == t_str[n_suffix + last_lcp]: + last_lcp += 1 + lcp[ele] = last_lcp + + return lcp diff --git a/docs/sorting.rst b/docs/sorting.rst index d0f1746..55c51c7 100644 --- a/docs/sorting.rst +++ b/docs/sorting.rst @@ -60,3 +60,8 @@ Sorting :members: :undoc-members: :show-inheritance: + +.. automodule:: algorithms.data_structures.lcp_array + :members: + :undoc-members: + :show-inheritance: diff --git a/tests/test_data_structures.py b/tests/test_data_structures.py index ebd8efc..a92d058 100644 --- a/tests/test_data_structures.py +++ b/tests/test_data_structures.py @@ -10,7 +10,8 @@ digraph, singly_linked_list, undirected_graph, - binary_search_tree + binary_search_tree, + lcp_array ) @@ -671,3 +672,45 @@ def test_keys(self): self.bst.keys(), ["a", "b", "c", "d", "e", "f", "g", "h", "i"] ) + + +class TestLCPSuffixArrays(unittest.TestCase): + def setUp(self): + super(TestLCPSuffixArrays, self).setUp() + self.case_1 = "aaaaaa" + self.s_array_1 = [5, 4, 3, 2, 1, 0] + self.rank_1 = [5, 4, 3, 2, 1, 0] + self.lcp_1 = [1, 2, 3, 4, 5, 0] + + self.case_2 = "abcabcdd" + self.s_array_2 = [0, 2, 4, 1, 3, 5, 7, 6] + self.rank_2 = [0, 3, 1, 4, 2, 5, 7, 6] + self.lcp_2 = [3, 0, 2, 0, 1, 0, 1, 0] + + self.case_3 = "kmckirrrmppp" + self.s_array_3 = [3, 4, 0, 2, 1, 11, 10, 9, 5, 8, 7, 6] + self.rank_3 = [2, 4, 3, 0, 1, 8, 11, 10, 9, 7, 6, 5] + self.lcp_3 = [0, 0, 1, 0, 1, 0, 1, 2, 0, 1, 2, 0] + + def test_lcp_array(self): + lcp = lcp_array.lcp_array(self.case_1, self.s_array_1, self.rank_1) + self.assertEqual(lcp, self.lcp_1) + + lcp = lcp_array.lcp_array(self.case_2, self.s_array_2, self.rank_2) + self.assertEqual(lcp, self.lcp_2) + + lcp = lcp_array.lcp_array(self.case_3, self.s_array_3, self.rank_3) + self.assertEqual(lcp, self.lcp_3) + + def test_suffix_array(self): + s_array, rank = lcp_array.suffix_array(self.case_1) + self.assertEqual(s_array, self.s_array_1) + self.assertEqual(rank, self.rank_1) + + s_array, rank = lcp_array.suffix_array(self.case_2) + self.assertEqual(s_array, self.s_array_2) + self.assertEqual(rank, self.rank_2) + + s_array, rank = lcp_array.suffix_array(self.case_3) + self.assertEqual(s_array, self.s_array_3) + self.assertEqual(rank, self.rank_3) From f63f98b5632b1630f7b3dde572f3831a90b97c25 Mon Sep 17 00:00:00 2001 From: karandesai-96 Date: Sun, 7 Feb 2016 16:31:45 +0530 Subject: [PATCH 75/89] Update .gitignore to ignore Pycharm's .idea directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d2f1a82..84034a4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ build/ /*.egg-info *.ropeproject/ docs/_build +.idea/* \ No newline at end of file From b7d6daebb355572fe728f385ff2ed3d50432a926 Mon Sep 17 00:00:00 2001 From: karandesai-96 Date: Sat, 9 Apr 2016 08:41:00 +0530 Subject: [PATCH 76/89] Reorder existing tests and add missing tests. - Add test for BogoSort and PrimalityTest. - Reorder tests in other files alphabetically. - Align all files according to PEP8 guidelines. --- tests/test_data_structures.py | 979 +++++++++++++++++----------------- tests/test_factorization.py | 22 +- tests/test_math.py | 86 +-- tests/test_searching.py | 114 ++-- tests/test_shuffling.py | 2 +- tests/test_sorting.py | 101 ++-- 6 files changed, 664 insertions(+), 640 deletions(-) diff --git a/tests/test_data_structures.py b/tests/test_data_structures.py index a92d058..98fdc82 100644 --- a/tests/test_data_structures.py +++ b/tests/test_data_structures.py @@ -2,226 +2,369 @@ import unittest from algorithms.data_structures import ( - stack, + binary_search_tree, + digraph, queue, + singly_linked_list, + stack, + undirected_graph, union_find, union_find_by_rank, union_find_with_path_compression, - digraph, - singly_linked_list, - undirected_graph, - binary_search_tree, lcp_array ) -class TestStack(unittest.TestCase): +class TestBinarySearchTree(unittest.TestCase): """ - Test Stack Implementation + Test Binary Search Tree Implementation """ - def test_stack(self): - self.sta = stack.Stack() - self.sta.add(5) - self.sta.add(8) - self.sta.add(10) - self.sta.add(2) + key_val = [ + ("a", 1), ("b", 2), ("c", 3), + ("d", 4), ("e", 5), ("f", 6), + ("g", 7), ("h", 8), ("i", 9) + ] - self.assertEqual(self.sta.remove(), 2) - self.assertEqual(self.sta.is_empty(), False) - self.assertEqual(self.sta.size(), 3) + def shuffle_list(self, ls): + shuffle(ls) + return ls + def test_size(self): + # Size starts at 0 + self.bst = binary_search_tree.BinarySearchTree() + self.assertEqual(self.bst.size(), 0) + # Doing a put increases the size to 1 + self.bst.put("one", 1) + self.assertEqual(self.bst.size(), 1) + # Putting a key that is already in doesn't change size + self.bst.put("one", 1) + self.assertEqual(self.bst.size(), 1) + self.bst.put("one", 2) + self.assertEqual(self.bst.size(), 1) -class TestQueue(unittest.TestCase): - """ - Test Queue Implementation - """ - def test_queue(self): - self.que = queue.Queue() - self.que.add(1) - self.que.add(2) - self.que.add(8) - self.que.add(5) - self.que.add(6) + self.bst = binary_search_tree.BinarySearchTree() + size = 0 + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + size += 1 + self.assertEqual(self.bst.size(), size) - self.assertEqual(self.que.remove(), 1) - self.assertEqual(self.que.size(), 4) - self.assertEqual(self.que.remove(), 2) - self.assertEqual(self.que.remove(), 8) - self.assertEqual(self.que.remove(), 5) - self.assertEqual(self.que.remove(), 6) - self.assertEqual(self.que.is_empty(), True) + shuffled = self.shuffle_list(self.key_val[:]) + self.bst = binary_search_tree.BinarySearchTree() + size = 0 + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + size += 1 + self.assertEqual(self.bst.size(), size) -class TestUnionFind(unittest.TestCase): - """ - Test Union Find Implementation - """ - def test_union_find(self): - self.uf = union_find.UnionFind(4) - self.uf.make_set(4) - self.uf.union(1, 0) - self.uf.union(3, 4) + def test_is_empty(self): + self.bst = binary_search_tree.BinarySearchTree() + self.assertTrue(self.bst.is_empty()) + self.bst.put("a", 1) + self.assertFalse(self.bst.is_empty()) - self.assertEqual(self.uf.find(1), 0) - self.assertEqual(self.uf.find(3), 4) - self.assertEqual(self.uf.is_connected(0, 1), True) - self.assertEqual(self.uf.is_connected(3, 4), True) + def test_get(self): + self.bst = binary_search_tree.BinarySearchTree() + # Getting a key not in BST returns None + self.assertEqual(self.bst.get("one"), None) + # Get with a present key returns proper value + self.bst.put("one", 1) + self.assertEqual(self.bst.get("one"), 1) -class TestUnionFindByRank(unittest.TestCase): - """ - Test Union Find Implementation - """ - def test_union_find_by_rank(self): - self.uf = union_find_by_rank.UnionFindByRank(6) - self.uf.make_set(6) - self.uf.union(1, 0) - self.uf.union(3, 4) - self.uf.union(2, 4) - self.uf.union(5, 2) - self.uf.union(6, 5) + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.get(k), v) - self.assertEqual(self.uf.find(1), 1) - self.assertEqual(self.uf.find(3), 3) - # test tree is created by rank - self.uf.union(5, 0) - self.assertEqual(self.uf.find(2), 3) - self.assertEqual(self.uf.find(5), 3) - self.assertEqual(self.uf.find(6), 3) - self.assertEqual(self.uf.find(0), 3) + shuffled = self.shuffle_list(self.key_val[:]) - self.assertEqual(self.uf.is_connected(0, 1), True) - self.assertEqual(self.uf.is_connected(3, 4), True) - self.assertEqual(self.uf.is_connected(5, 3), True) + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.get(k), v) + def test_contains(self): + self.bst = binary_search_tree.BinarySearchTree() + self.assertFalse(self.bst.contains("a")) + self.bst.put("a", 1) + self.assertTrue(self.bst.contains("a")) -class TestUnionFindWithPathCompression(unittest.TestCase): - """ - Test Union Find Implementation - """ - def test_union_find_with_path_compression(self): - self.uf = ( - union_find_with_path_compression - .UnionFindWithPathCompression(5) - ) - self.uf.make_set(5) - self.uf.union(0, 1) - self.uf.union(2, 3) - self.uf.union(1, 3) - self.uf.union(4, 5) - self.assertEqual(self.uf.find(1), 0) - self.assertEqual(self.uf.find(3), 0) - self.assertEqual(self.uf.parent(3), 2) - self.assertEqual(self.uf.parent(5), 4) - self.assertEqual(self.uf.is_connected(3, 5), False) - self.assertEqual(self.uf.is_connected(4, 5), True) - self.assertEqual(self.uf.is_connected(2, 3), True) - # test tree is created by path compression - self.uf.union(5, 3) - self.assertEqual(self.uf.parent(3), 0) + def test_put(self): + self.bst = binary_search_tree.BinarySearchTree() - self.assertEqual(self.uf.is_connected(3, 5), True) + # When BST is empty first put becomes root + self.bst.put("bbb", 1) + self.assertEqual(self.bst.root.key, "bbb") + self.assertEqual(self.bst.root.left, None) + # Adding a key greater than root doesn't update the left tree + # but does update the right + self.bst.put("ccc", 2) + self.assertEqual(self.bst.root.key, "bbb") + self.assertEqual(self.bst.root.left, None) + self.assertEqual(self.bst.root.right.key, "ccc") -class TestSinglyLinkedList(unittest.TestCase): - """ - Test Singly Linked List Implementation - """ + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("bbb", 1) + # Adding a key less than root doesn't update the right tree + # but does update the left + self.bst.put("aaa", 2) + self.assertEqual(self.bst.root.key, "bbb") + self.assertEqual(self.bst.root.right, None) + self.assertEqual(self.bst.root.left.key, "aaa") - def test_singly_linked_list(self): - self.sl = singly_linked_list.SinglyLinkedList() - self.sl.add(10) - self.sl.add(5) - self.sl.add(30) - self.sl.remove(30) + self.bst = binary_search_tree.BinarySearchTree() + size = 0 + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + size += 1 + self.assertEqual(self.bst.get(k), v) + self.assertEqual(self.bst.size(), size) - self.assertEqual(self.sl.size, 2) - self.assertEqual(self.sl.search(30), False) - self.assertEqual(self.sl.search(5), True) - self.assertEqual(self.sl.search(10), True) - self.assertEqual(self.sl.remove(5), True) - self.assertEqual(self.sl.remove(10), True) - self.assertEqual(self.sl.size, 0) + self.bst = binary_search_tree.BinarySearchTree() + shuffled = self.shuffle_list(self.key_val[:]) -class TestUndirectedGraph(unittest.TestCase): - """ - Test Undirected Graph Implementation - """ - def test_undirected_graph(self): + size = 0 + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + size += 1 + self.assertEqual(self.bst.get(k), v) + self.assertEqual(self.bst.size(), size) - # init - self.ug0 = undirected_graph.Undirected_Graph() - self.ug1 = undirected_graph.Undirected_Graph() - self.ug2 = undirected_graph.Undirected_Graph() - self.ug3 = undirected_graph.Undirected_Graph() + def test_min_key(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val[::-1]: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.min_key(), k) - # populating - self.ug1.add_edge(1, 2) + shuffled = self.shuffle_list(self.key_val[:]) - self.ug2.add_edge(1, 2) - self.ug2.add_edge(1, 2) + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.min_key(), "a") - self.ug3.add_edge(1, 2) - self.ug3.add_edge(1, 2) - self.ug3.add_edge(3, 1) + def test_max_key(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.max_key(), k) - # test adj - self.assertTrue(2 in self.ug1.adj(1)) - self.assertEqual(len(self.ug1.adj(1)), 1) - self.assertTrue(1 in self.ug1.adj(2)) - self.assertEqual(len(self.ug1.adj(1)), 1) + shuffled = self.shuffle_list(self.key_val[:]) - self.assertTrue(2 in self.ug2.adj(1)) - self.assertEqual(len(self.ug2.adj(1)), 2) - self.assertTrue(1 in self.ug2.adj(2)) - self.assertEqual(len(self.ug2.adj(1)), 2) + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.max_key(), "i") - self.assertTrue(2 in self.ug3.adj(1)) - self.assertTrue(3 in self.ug3.adj(1)) - self.assertEqual(len(self.ug3.adj(1)), 3) - self.assertTrue(1 in self.ug3.adj(2)) - self.assertEqual(len(self.ug3.adj(2)), 2) - self.assertTrue(1 in self.ug3.adj(3)) - self.assertEqual(len(self.ug3.adj(3)), 1) + def test_floor_key(self): + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.put("c", 3) + self.bst.put("e", 5) + self.bst.put("g", 7) + self.assertEqual(self.bst.floor_key("a"), "a") + self.assertEqual(self.bst.floor_key("b"), "a") + self.assertEqual(self.bst.floor_key("g"), "g") + self.assertEqual(self.bst.floor_key("h"), "g") - # test degree - self.assertEqual(self.ug1.degree(1), 1) - self.assertEqual(self.ug1.degree(2), 1) - self.assertEqual(self.ug2.degree(1), 2) - self.assertEqual(self.ug2.degree(2), 2) - self.assertEqual(self.ug3.degree(1), 3) - self.assertEqual(self.ug3.degree(2), 2) - self.assertEqual(self.ug3.degree(3), 1) + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("c", 3) + self.bst.put("e", 5) + self.bst.put("a", 1) + self.bst.put("g", 7) + self.assertEqual(self.bst.floor_key("a"), "a") + self.assertEqual(self.bst.floor_key("b"), "a") + self.assertEqual(self.bst.floor_key("g"), "g") + self.assertEqual(self.bst.floor_key("h"), "g") - # test vertices - self.assertEqual(list(self.ug0.vertices()), []) - self.assertEqual(len(self.ug0.vertices()), 0) + def test_ceiling_key(self): + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.put("c", 3) + self.bst.put("e", 5) + self.bst.put("g", 7) + self.assertEqual(self.bst.ceiling_key("a"), "a") + self.assertEqual(self.bst.ceiling_key("b"), "c") + self.assertEqual(self.bst.ceiling_key("g"), "g") + self.assertEqual(self.bst.ceiling_key("f"), "g") - self.assertTrue(1 in self.ug1.vertices()) - self.assertTrue(2 in self.ug1.vertices()) - self.assertEqual(len(self.ug1.vertices()), 2) + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("c", 3) + self.bst.put("e", 5) + self.bst.put("a", 1) + self.bst.put("g", 7) + self.assertEqual(self.bst.ceiling_key("a"), "a") + self.assertEqual(self.bst.ceiling_key("b"), "c") + self.assertEqual(self.bst.ceiling_key("g"), "g") + self.assertEqual(self.bst.ceiling_key("f"), "g") - self.assertTrue(1 in self.ug2.vertices()) - self.assertTrue(2 in self.ug2.vertices()) - self.assertEqual(len(self.ug2.vertices()), 2) + def test_select_key(self): + shuffled = self.shuffle_list(self.key_val[:]) - self.assertTrue(1 in self.ug3.vertices()) - self.assertTrue(2 in self.ug3.vertices()) - self.assertTrue(3 in self.ug3.vertices()) - self.assertEqual(len(self.ug3.vertices()), 3) + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + self.assertEqual(self.bst.select_key(0), "a") + self.assertEqual(self.bst.select_key(1), "b") + self.assertEqual(self.bst.select_key(2), "c") - # test vertex_count - self.assertEqual(self.ug0.vertex_count(), 0) - self.assertEqual(self.ug1.vertex_count(), 2) - self.assertEqual(self.ug2.vertex_count(), 2) - self.assertEqual(self.ug3.vertex_count(), 3) + def test_rank(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) - # test edge_count - self.assertEqual(self.ug0.edge_count(), 0) - self.assertEqual(self.ug1.edge_count(), 1) - self.assertEqual(self.ug2.edge_count(), 2) - self.assertEqual(self.ug3.edge_count(), 3) + self.assertEqual(self.bst.rank("a"), 0) + self.assertEqual(self.bst.rank("b"), 1) + self.assertEqual(self.bst.rank("c"), 2) + self.assertEqual(self.bst.rank("d"), 3) + + shuffled = self.shuffle_list(self.key_val[:]) + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + + self.assertEqual(self.bst.rank("a"), 0) + self.assertEqual(self.bst.rank("b"), 1) + self.assertEqual(self.bst.rank("c"), 2) + self.assertEqual(self.bst.rank("d"), 3) + + def test_delete_min(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + for i in range(self.bst.size() - 1): + self.bst.delete_min() + self.assertEqual(self.bst.min_key(), self.key_val[i+1][0]) + self.bst.delete_min() + self.assertEqual(self.bst.min_key(), None) + + shuffled = self.shuffle_list(self.key_val[:]) + self.bst = binary_search_tree.BinarySearchTree() + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + for i in range(self.bst.size() - 1): + self.bst.delete_min() + self.assertEqual(self.bst.min_key(), self.key_val[i+1][0]) + self.bst.delete_min() + self.assertEqual(self.bst.min_key(), None) + + def test_delete_max(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + for i in range(self.bst.size() - 1, 0, -1): + self.bst.delete_max() + self.assertEqual(self.bst.max_key(), self.key_val[i-1][0]) + self.bst.delete_max() + self.assertEqual(self.bst.max_key(), None) + + shuffled = self.shuffle_list(self.key_val[:]) + + for pair in shuffled: + k, v = pair + self.bst.put(k, v) + for i in range(self.bst.size() - 1, 0, -1): + self.bst.delete_max() + self.assertEqual(self.bst.max_key(), self.key_val[i-1][0]) + self.bst.delete_max() + self.assertEqual(self.bst.max_key(), None) + + def test_delete(self): + # delete key from an empty bst + self.bst = binary_search_tree.BinarySearchTree() + self.bst.delete("a") + self.assertEqual(self.bst.root, None) + self.assertEqual(self.bst.size(), 0) + + # delete key not present in bst + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.delete("b") + self.assertEqual(self.bst.root.key, "a") + self.assertEqual(self.bst.size(), 1) + + # delete key when bst only contains one key + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.assertEqual(self.bst.root.key, "a") + self.bst.delete("a") + self.assertEqual(self.bst.root, None) + self.assertEqual(self.bst.size(), 0) + + # delete parent key when it only has a left child + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("b", 2) + self.bst.put("a", 1) + self.assertEqual(self.bst.root.left.key, "a") + self.bst.delete("b") + self.assertEqual(self.bst.root.key, "a") + self.assertEqual(self.bst.size(), 1) + + # delete parent key when it only has a right child + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.put("b", 2) + self.assertEqual(self.bst.root.right.key, "b") + self.bst.delete("a") + self.assertEqual(self.bst.root.key, "b") + self.assertEqual(self.bst.size(), 1) + + # delete left child key + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("b", 2) + self.bst.put("a", 1) + self.assertEqual(self.bst.root.left.key, "a") + self.bst.delete("a") + self.assertEqual(self.bst.root.key, "b") + self.assertEqual(self.bst.size(), 1) + + # delete right child key + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("a", 1) + self.bst.put("b", 2) + self.assertEqual(self.bst.root.right.key, "b") + self.bst.delete("b") + self.assertEqual(self.bst.root.key, "a") + self.assertEqual(self.bst.size(), 1) + + # delete parent key when it has a left and right child + self.bst = binary_search_tree.BinarySearchTree() + self.bst.put("b", 2) + self.bst.put("a", 1) + self.bst.put("c", 3) + self.bst.delete("b") + self.assertEqual(self.bst.root.key, "c") + self.assertEqual(self.bst.size(), 2) + + def test_keys(self): + self.bst = binary_search_tree.BinarySearchTree() + for pair in self.key_val: + k, v = pair + self.bst.put(k, v) + self.assertEqual( + self.bst.keys(), + ["a", "b", "c", "d", "e", "f", "g", "h", "i"] + ) class TestDirectedGraph(unittest.TestCase): @@ -322,356 +465,214 @@ def test_directed_graph(self): self.assertEqual(self.dg3.edge_count(), 3) -class TestBinarySearchTree(unittest.TestCase): +class TestQueue(unittest.TestCase): """ - Test Binary Search Tree Implementation + Test Queue Implementation """ - key_val = [ - ("a", 1), ("b", 2), ("c", 3), - ("d", 4), ("e", 5), ("f", 6), - ("g", 7), ("h", 8), ("i", 9) - ] + def test_queue(self): + self.que = queue.Queue() + self.que.add(1) + self.que.add(2) + self.que.add(8) + self.que.add(5) + self.que.add(6) - def shuffle_list(self, ls): - shuffle(ls) - return ls - - def test_size(self): - # Size starts at 0 - self.bst = binary_search_tree.BinarySearchTree() - self.assertEqual(self.bst.size(), 0) - # Doing a put increases the size to 1 - self.bst.put("one", 1) - self.assertEqual(self.bst.size(), 1) - # Putting a key that is already in doesn't change size - self.bst.put("one", 1) - self.assertEqual(self.bst.size(), 1) - self.bst.put("one", 2) - self.assertEqual(self.bst.size(), 1) - - self.bst = binary_search_tree.BinarySearchTree() - size = 0 - for pair in self.key_val: - k, v = pair - self.bst.put(k, v) - size += 1 - self.assertEqual(self.bst.size(), size) - - shuffled = self.shuffle_list(self.key_val[:]) - - self.bst = binary_search_tree.BinarySearchTree() - size = 0 - for pair in shuffled: - k, v = pair - self.bst.put(k, v) - size += 1 - self.assertEqual(self.bst.size(), size) - - def test_is_empty(self): - self.bst = binary_search_tree.BinarySearchTree() - self.assertTrue(self.bst.is_empty()) - self.bst.put("a", 1) - self.assertFalse(self.bst.is_empty()) - - def test_get(self): - self.bst = binary_search_tree.BinarySearchTree() - # Getting a key not in BST returns None - self.assertEqual(self.bst.get("one"), None) - - # Get with a present key returns proper value - self.bst.put("one", 1) - self.assertEqual(self.bst.get("one"), 1) - - self.bst = binary_search_tree.BinarySearchTree() - for pair in self.key_val: - k, v = pair - self.bst.put(k, v) - self.assertEqual(self.bst.get(k), v) - - shuffled = self.shuffle_list(self.key_val[:]) - - self.bst = binary_search_tree.BinarySearchTree() - for pair in shuffled: - k, v = pair - self.bst.put(k, v) - self.assertEqual(self.bst.get(k), v) - - def test_contains(self): - self.bst = binary_search_tree.BinarySearchTree() - self.assertFalse(self.bst.contains("a")) - self.bst.put("a", 1) - self.assertTrue(self.bst.contains("a")) - - def test_put(self): - self.bst = binary_search_tree.BinarySearchTree() - - # When BST is empty first put becomes root - self.bst.put("bbb", 1) - self.assertEqual(self.bst.root.key, "bbb") - self.assertEqual(self.bst.root.left, None) - - # Adding a key greater than root doesn't update the left tree - # but does update the right - self.bst.put("ccc", 2) - self.assertEqual(self.bst.root.key, "bbb") - self.assertEqual(self.bst.root.left, None) - self.assertEqual(self.bst.root.right.key, "ccc") - - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("bbb", 1) - # Adding a key less than root doesn't update the right tree - # but does update the left - self.bst.put("aaa", 2) - self.assertEqual(self.bst.root.key, "bbb") - self.assertEqual(self.bst.root.right, None) - self.assertEqual(self.bst.root.left.key, "aaa") + self.assertEqual(self.que.remove(), 1) + self.assertEqual(self.que.size(), 4) + self.assertEqual(self.que.remove(), 2) + self.assertEqual(self.que.remove(), 8) + self.assertEqual(self.que.remove(), 5) + self.assertEqual(self.que.remove(), 6) + self.assertEqual(self.que.is_empty(), True) - self.bst = binary_search_tree.BinarySearchTree() - size = 0 - for pair in self.key_val: - k, v = pair - self.bst.put(k, v) - size += 1 - self.assertEqual(self.bst.get(k), v) - self.assertEqual(self.bst.size(), size) - self.bst = binary_search_tree.BinarySearchTree() +class TestSinglyLinkedList(unittest.TestCase): + """ + Test Singly Linked List Implementation + """ - shuffled = self.shuffle_list(self.key_val[:]) + def test_singly_linked_list(self): + self.sl = singly_linked_list.SinglyLinkedList() + self.sl.add(10) + self.sl.add(5) + self.sl.add(30) + self.sl.remove(30) - size = 0 - for pair in shuffled: - k, v = pair - self.bst.put(k, v) - size += 1 - self.assertEqual(self.bst.get(k), v) - self.assertEqual(self.bst.size(), size) + self.assertEqual(self.sl.size, 2) + self.assertEqual(self.sl.search(30), False) + self.assertEqual(self.sl.search(5), True) + self.assertEqual(self.sl.search(10), True) + self.assertEqual(self.sl.remove(5), True) + self.assertEqual(self.sl.remove(10), True) + self.assertEqual(self.sl.size, 0) - def test_min_key(self): - self.bst = binary_search_tree.BinarySearchTree() - for pair in self.key_val[::-1]: - k, v = pair - self.bst.put(k, v) - self.assertEqual(self.bst.min_key(), k) - shuffled = self.shuffle_list(self.key_val[:]) +class TestStack(unittest.TestCase): + """ + Test Stack Implementation + """ + def test_stack(self): + self.sta = stack.Stack() + self.sta.add(5) + self.sta.add(8) + self.sta.add(10) + self.sta.add(2) - self.bst = binary_search_tree.BinarySearchTree() - for pair in shuffled: - k, v = pair - self.bst.put(k, v) - self.assertEqual(self.bst.min_key(), "a") + self.assertEqual(self.sta.remove(), 2) + self.assertEqual(self.sta.is_empty(), False) + self.assertEqual(self.sta.size(), 3) - def test_max_key(self): - self.bst = binary_search_tree.BinarySearchTree() - for pair in self.key_val: - k, v = pair - self.bst.put(k, v) - self.assertEqual(self.bst.max_key(), k) - shuffled = self.shuffle_list(self.key_val[:]) +class TestUndirectedGraph(unittest.TestCase): + """ + Test Undirected Graph Implementation + """ + def test_undirected_graph(self): - self.bst = binary_search_tree.BinarySearchTree() - for pair in shuffled: - k, v = pair - self.bst.put(k, v) - self.assertEqual(self.bst.max_key(), "i") + # init + self.ug0 = undirected_graph.Undirected_Graph() + self.ug1 = undirected_graph.Undirected_Graph() + self.ug2 = undirected_graph.Undirected_Graph() + self.ug3 = undirected_graph.Undirected_Graph() - def test_floor_key(self): - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("a", 1) - self.bst.put("c", 3) - self.bst.put("e", 5) - self.bst.put("g", 7) - self.assertEqual(self.bst.floor_key("a"), "a") - self.assertEqual(self.bst.floor_key("b"), "a") - self.assertEqual(self.bst.floor_key("g"), "g") - self.assertEqual(self.bst.floor_key("h"), "g") + # populating + self.ug1.add_edge(1, 2) - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("c", 3) - self.bst.put("e", 5) - self.bst.put("a", 1) - self.bst.put("g", 7) - self.assertEqual(self.bst.floor_key("a"), "a") - self.assertEqual(self.bst.floor_key("b"), "a") - self.assertEqual(self.bst.floor_key("g"), "g") - self.assertEqual(self.bst.floor_key("h"), "g") + self.ug2.add_edge(1, 2) + self.ug2.add_edge(1, 2) - def test_ceiling_key(self): - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("a", 1) - self.bst.put("c", 3) - self.bst.put("e", 5) - self.bst.put("g", 7) - self.assertEqual(self.bst.ceiling_key("a"), "a") - self.assertEqual(self.bst.ceiling_key("b"), "c") - self.assertEqual(self.bst.ceiling_key("g"), "g") - self.assertEqual(self.bst.ceiling_key("f"), "g") + self.ug3.add_edge(1, 2) + self.ug3.add_edge(1, 2) + self.ug3.add_edge(3, 1) - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("c", 3) - self.bst.put("e", 5) - self.bst.put("a", 1) - self.bst.put("g", 7) - self.assertEqual(self.bst.ceiling_key("a"), "a") - self.assertEqual(self.bst.ceiling_key("b"), "c") - self.assertEqual(self.bst.ceiling_key("g"), "g") - self.assertEqual(self.bst.ceiling_key("f"), "g") + # test adj + self.assertTrue(2 in self.ug1.adj(1)) + self.assertEqual(len(self.ug1.adj(1)), 1) + self.assertTrue(1 in self.ug1.adj(2)) + self.assertEqual(len(self.ug1.adj(1)), 1) - def test_select_key(self): - shuffled = self.shuffle_list(self.key_val[:]) + self.assertTrue(2 in self.ug2.adj(1)) + self.assertEqual(len(self.ug2.adj(1)), 2) + self.assertTrue(1 in self.ug2.adj(2)) + self.assertEqual(len(self.ug2.adj(1)), 2) - self.bst = binary_search_tree.BinarySearchTree() - for pair in shuffled: - k, v = pair - self.bst.put(k, v) - self.assertEqual(self.bst.select_key(0), "a") - self.assertEqual(self.bst.select_key(1), "b") - self.assertEqual(self.bst.select_key(2), "c") + self.assertTrue(2 in self.ug3.adj(1)) + self.assertTrue(3 in self.ug3.adj(1)) + self.assertEqual(len(self.ug3.adj(1)), 3) + self.assertTrue(1 in self.ug3.adj(2)) + self.assertEqual(len(self.ug3.adj(2)), 2) + self.assertTrue(1 in self.ug3.adj(3)) + self.assertEqual(len(self.ug3.adj(3)), 1) - def test_rank(self): - self.bst = binary_search_tree.BinarySearchTree() - for pair in self.key_val: - k, v = pair - self.bst.put(k, v) + # test degree + self.assertEqual(self.ug1.degree(1), 1) + self.assertEqual(self.ug1.degree(2), 1) + self.assertEqual(self.ug2.degree(1), 2) + self.assertEqual(self.ug2.degree(2), 2) + self.assertEqual(self.ug3.degree(1), 3) + self.assertEqual(self.ug3.degree(2), 2) + self.assertEqual(self.ug3.degree(3), 1) - self.assertEqual(self.bst.rank("a"), 0) - self.assertEqual(self.bst.rank("b"), 1) - self.assertEqual(self.bst.rank("c"), 2) - self.assertEqual(self.bst.rank("d"), 3) + # test vertices + self.assertEqual(list(self.ug0.vertices()), []) + self.assertEqual(len(self.ug0.vertices()), 0) - shuffled = self.shuffle_list(self.key_val[:]) - self.bst = binary_search_tree.BinarySearchTree() - for pair in shuffled: - k, v = pair - self.bst.put(k, v) + self.assertTrue(1 in self.ug1.vertices()) + self.assertTrue(2 in self.ug1.vertices()) + self.assertEqual(len(self.ug1.vertices()), 2) - self.assertEqual(self.bst.rank("a"), 0) - self.assertEqual(self.bst.rank("b"), 1) - self.assertEqual(self.bst.rank("c"), 2) - self.assertEqual(self.bst.rank("d"), 3) + self.assertTrue(1 in self.ug2.vertices()) + self.assertTrue(2 in self.ug2.vertices()) + self.assertEqual(len(self.ug2.vertices()), 2) - def test_delete_min(self): - self.bst = binary_search_tree.BinarySearchTree() - for pair in self.key_val: - k, v = pair - self.bst.put(k, v) - for i in range(self.bst.size() - 1): - self.bst.delete_min() - self.assertEqual(self.bst.min_key(), self.key_val[i+1][0]) - self.bst.delete_min() - self.assertEqual(self.bst.min_key(), None) + self.assertTrue(1 in self.ug3.vertices()) + self.assertTrue(2 in self.ug3.vertices()) + self.assertTrue(3 in self.ug3.vertices()) + self.assertEqual(len(self.ug3.vertices()), 3) - shuffled = self.shuffle_list(self.key_val[:]) - self.bst = binary_search_tree.BinarySearchTree() - for pair in shuffled: - k, v = pair - self.bst.put(k, v) - for i in range(self.bst.size() - 1): - self.bst.delete_min() - self.assertEqual(self.bst.min_key(), self.key_val[i+1][0]) - self.bst.delete_min() - self.assertEqual(self.bst.min_key(), None) + # test vertex_count + self.assertEqual(self.ug0.vertex_count(), 0) + self.assertEqual(self.ug1.vertex_count(), 2) + self.assertEqual(self.ug2.vertex_count(), 2) + self.assertEqual(self.ug3.vertex_count(), 3) - def test_delete_max(self): - self.bst = binary_search_tree.BinarySearchTree() - for pair in self.key_val: - k, v = pair - self.bst.put(k, v) - for i in range(self.bst.size() - 1, 0, -1): - self.bst.delete_max() - self.assertEqual(self.bst.max_key(), self.key_val[i-1][0]) - self.bst.delete_max() - self.assertEqual(self.bst.max_key(), None) + # test edge_count + self.assertEqual(self.ug0.edge_count(), 0) + self.assertEqual(self.ug1.edge_count(), 1) + self.assertEqual(self.ug2.edge_count(), 2) + self.assertEqual(self.ug3.edge_count(), 3) - shuffled = self.shuffle_list(self.key_val[:]) - for pair in shuffled: - k, v = pair - self.bst.put(k, v) - for i in range(self.bst.size() - 1, 0, -1): - self.bst.delete_max() - self.assertEqual(self.bst.max_key(), self.key_val[i-1][0]) - self.bst.delete_max() - self.assertEqual(self.bst.max_key(), None) +class TestUnionFind(unittest.TestCase): + """ + Test Union Find Implementation + """ + def test_union_find(self): + self.uf = union_find.UnionFind(4) + self.uf.make_set(4) + self.uf.union(1, 0) + self.uf.union(3, 4) - def test_delete(self): - # delete key from an empty bst - self.bst = binary_search_tree.BinarySearchTree() - self.bst.delete("a") - self.assertEqual(self.bst.root, None) - self.assertEqual(self.bst.size(), 0) + self.assertEqual(self.uf.find(1), 0) + self.assertEqual(self.uf.find(3), 4) + self.assertEqual(self.uf.is_connected(0, 1), True) + self.assertEqual(self.uf.is_connected(3, 4), True) - # delete key not present in bst - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("a", 1) - self.bst.delete("b") - self.assertEqual(self.bst.root.key, "a") - self.assertEqual(self.bst.size(), 1) - # delete key when bst only contains one key - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("a", 1) - self.assertEqual(self.bst.root.key, "a") - self.bst.delete("a") - self.assertEqual(self.bst.root, None) - self.assertEqual(self.bst.size(), 0) +class TestUnionFindByRank(unittest.TestCase): + """ + Test Union Find Implementation + """ + def test_union_find_by_rank(self): + self.uf = union_find_by_rank.UnionFindByRank(6) + self.uf.make_set(6) + self.uf.union(1, 0) + self.uf.union(3, 4) + self.uf.union(2, 4) + self.uf.union(5, 2) + self.uf.union(6, 5) - # delete parent key when it only has a left child - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("b", 2) - self.bst.put("a", 1) - self.assertEqual(self.bst.root.left.key, "a") - self.bst.delete("b") - self.assertEqual(self.bst.root.key, "a") - self.assertEqual(self.bst.size(), 1) + self.assertEqual(self.uf.find(1), 1) + self.assertEqual(self.uf.find(3), 3) + # test tree is created by rank + self.uf.union(5, 0) + self.assertEqual(self.uf.find(2), 3) + self.assertEqual(self.uf.find(5), 3) + self.assertEqual(self.uf.find(6), 3) + self.assertEqual(self.uf.find(0), 3) - # delete parent key when it only has a right child - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("a", 1) - self.bst.put("b", 2) - self.assertEqual(self.bst.root.right.key, "b") - self.bst.delete("a") - self.assertEqual(self.bst.root.key, "b") - self.assertEqual(self.bst.size(), 1) + self.assertEqual(self.uf.is_connected(0, 1), True) + self.assertEqual(self.uf.is_connected(3, 4), True) + self.assertEqual(self.uf.is_connected(5, 3), True) - # delete left child key - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("b", 2) - self.bst.put("a", 1) - self.assertEqual(self.bst.root.left.key, "a") - self.bst.delete("a") - self.assertEqual(self.bst.root.key, "b") - self.assertEqual(self.bst.size(), 1) - # delete right child key - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("a", 1) - self.bst.put("b", 2) - self.assertEqual(self.bst.root.right.key, "b") - self.bst.delete("b") - self.assertEqual(self.bst.root.key, "a") - self.assertEqual(self.bst.size(), 1) +class TestUnionFindWithPathCompression(unittest.TestCase): + """ + Test Union Find Implementation + """ + def test_union_find_with_path_compression(self): + self.uf = ( + union_find_with_path_compression + .UnionFindWithPathCompression(5) + ) - # delete parent key when it has a left and right child - self.bst = binary_search_tree.BinarySearchTree() - self.bst.put("b", 2) - self.bst.put("a", 1) - self.bst.put("c", 3) - self.bst.delete("b") - self.assertEqual(self.bst.root.key, "c") - self.assertEqual(self.bst.size(), 2) + self.uf.make_set(5) + self.uf.union(0, 1) + self.uf.union(2, 3) + self.uf.union(1, 3) + self.uf.union(4, 5) + self.assertEqual(self.uf.find(1), 0) + self.assertEqual(self.uf.find(3), 0) + self.assertEqual(self.uf.parent(3), 2) + self.assertEqual(self.uf.parent(5), 4) + self.assertEqual(self.uf.is_connected(3, 5), False) + self.assertEqual(self.uf.is_connected(4, 5), True) + self.assertEqual(self.uf.is_connected(2, 3), True) + # test tree is created by path compression + self.uf.union(5, 3) + self.assertEqual(self.uf.parent(3), 0) - def test_keys(self): - self.bst = binary_search_tree.BinarySearchTree() - for pair in self.key_val: - k, v = pair - self.bst.put(k, v) - self.assertEqual( - self.bst.keys(), - ["a", "b", "c", "d", "e", "f", "g", "h", "i"] - ) + self.assertEqual(self.uf.is_connected(3, 5), True) class TestLCPSuffixArrays(unittest.TestCase): diff --git a/tests/test_factorization.py b/tests/test_factorization.py index 1a8be74..4033e1f 100644 --- a/tests/test_factorization.py +++ b/tests/test_factorization.py @@ -6,6 +6,17 @@ from algorithms.factorization.fermat import fermat +class TestFermat(unittest.TestCase): + + def test_fermat(self): + x = random.randint(1, 100000000) + factors = fermat(x) + res = 1 + for i in factors: + res *= i + self.assertEqual(x, res) + + class TestPollardRho(unittest.TestCase): def test_pollard_rho(self): @@ -26,14 +37,3 @@ def test_trial_division(self): for i in factors: res *= i self.assertEqual(x, res) - - -class TestFermat(unittest.TestCase): - - def test_fermat(self): - x = random.randint(1, 100000000) - factors = fermat(x) - res = 1 - for i in factors: - res *= i - self.assertEqual(x, res) diff --git a/tests/test_math.py b/tests/test_math.py index 78aa83c..fc23463 100644 --- a/tests/test_math.py +++ b/tests/test_math.py @@ -1,11 +1,28 @@ import unittest +from algorithms.math.approx_cdf import cdf from algorithms.math.extended_gcd import extended_gcd from algorithms.math.lcm import lcm -from algorithms.math.sieve_eratosthenes import eratosthenes +from algorithms.math.primality_test import is_prime from algorithms.math.sieve_atkin import atkin +from algorithms.math.sieve_eratosthenes import eratosthenes from algorithms.math.std_normal_pdf import pdf -from algorithms.math.approx_cdf import cdf + + +class TestApproxCdf(unittest.TestCase): + + def test_cdf(self): + # Calculate cumulative distribution function for x=1 + a = cdf(1) + self.assertAlmostEqual(a, 0.841344746068543) + + # Calculate cumulative distribution function x=0 + a = cdf(0) + self.assertAlmostEqual(a, 0.5) + + # Calculate cumulative distribution function for x=(-1) + a = cdf(-1) + self.assertAlmostEqual(a, 0.15865525393145702) class TestExtendedGCD(unittest.TestCase): @@ -43,25 +60,15 @@ def test_lcm(self): self.assertEqual(r, r2) -class TestSieveOfEratosthenes(unittest.TestCase): - - def test_eratosthenes(self): - rv1 = eratosthenes(-10) - rv2 = eratosthenes(10) - rv3 = eratosthenes(100, 5) - rv4 = eratosthenes(100, -10) - self.assertEqual(rv1, []) - self.assertEqual(rv2, [2, 3, 5, 7]) - self.assertEqual( - rv3, - [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, - 67, 71, 73, 79, 83, 89, 97] - ) - self.assertEqual( - rv4, - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, - 61, 67, 71, 73, 79, 83, 89, 97] - ) +class TestPrimalityTest(unittest.TestCase): + def test_is_prime(self): + self.assertIs(is_prime(3), True) + self.assertIs(is_prime(15), False) + self.assertIs(is_prime(20), False) + self.assertIs(is_prime(37), True) + self.assertIs(is_prime(63), False) + self.assertIs(is_prime(87), False) + self.assertIs(is_prime(103), True) class TestSieveOfAtkin(unittest.TestCase): @@ -96,6 +103,27 @@ def test_atkin(self): self.assertEqual(rv4, []) +class TestSieveOfEratosthenes(unittest.TestCase): + + def test_eratosthenes(self): + rv1 = eratosthenes(-10) + rv2 = eratosthenes(10) + rv3 = eratosthenes(100, 5) + rv4 = eratosthenes(100, -10) + self.assertEqual(rv1, []) + self.assertEqual(rv2, [2, 3, 5, 7]) + self.assertEqual( + rv3, + [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, + 67, 71, 73, 79, 83, 89, 97] + ) + self.assertEqual( + rv4, + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97] + ) + + class TestStdNormPDF(unittest.TestCase): def test_pdf(self): @@ -110,19 +138,3 @@ def test_pdf(self): # Calculate standard normal pdf for x=13, mean=10, std_dev=1 a = pdf(x=13, mean=10, std_dev=1) self.assertAlmostEqual(a, 0.004431848411938008) - - -class TestApproxCdf(unittest.TestCase): - - def test_cdf(self): - # Calculate cumulative distribution function for x=1 - a = cdf(1) - self.assertAlmostEqual(a, 0.841344746068543) - - # Calculate cumulative distribution function x=0 - a = cdf(0) - self.assertAlmostEqual(a, 0.5) - - # Calculate cumulative distribution function for x=(-1) - a = cdf(-1) - self.assertAlmostEqual(a, 0.15865525393145702) diff --git a/tests/test_searching.py b/tests/test_searching.py index fe12b5d..ec040bb 100644 --- a/tests/test_searching.py +++ b/tests/test_searching.py @@ -3,11 +3,11 @@ from algorithms.searching import ( binary_search, - kmp_search, - rabinkarp_search, bmh_search, + breadth_first_search, depth_first_search, - breadth_first_search + kmp_search, + rabinkarp_search ) @@ -41,32 +41,6 @@ def test_binarysearch(self): self.assertIs(rv5, 4) -class TestKMPSearch(unittest.TestCase): - """ - Tests KMP search on string "ABCDE FG ABCDEABCDEF" - """ - - def test_kmpsearch(self): - self.string = "ABCDE FG ABCDEABCDEF" - rv1 = kmp_search.search(self.string, "ABCDEA") - rv2 = kmp_search.search(self.string, "ABCDER") - self.assertIs(rv1[0], 9) - self.assertFalse(rv2) - - -class TestRabinKarpSearch(unittest.TestCase): - """ - Tests Rabin-Karp search on string "ABCDEFGHIJKLMNOP" - """ - - def test_rabinkarpsearch(self): - self.string = "ABCDEFGHIJKLMNOP" - rv1 = rabinkarp_search.search(self.string, "MNOP") - rv2 = rabinkarp_search.search(self.string, "BCA") - self.assertIs(rv1[0], 12) - self.assertFalse(rv2) - - class TestBMHSearch(unittest.TestCase): """ Tests BMH search on string "ABCDE FG ABCDEABCDEF" @@ -80,6 +54,38 @@ def test_bmhsearch(self): self.assertFalse(rv2) +class TestBreadthFirstSearch(unittest.TestCase): + """ + Tests DFS on a graph represented by a adjacency list + """ + def test_bfs(self): + self.graph = { + 'A': {'B', 'C'}, + 'B': {'A', 'D', 'E'}, + 'C': {'A', 'F'}, + 'D': {'B'}, + 'E': {'B', 'F'}, + 'F': {'C', 'E'} + } + rv1 = breadth_first_search.bfs(self.graph, 'A') + self.assertEqual(rv1, {'C', 'A', 'B', 'D', 'F', 'E'}) + self.graph = { + 'A': {'B', 'C', 'E'}, + 'B': {'A', 'D', 'F'}, + 'C': {'A', 'G'}, + 'D': {'B'}, + 'F': {'B'}, + 'E': {'A'}, + 'G': {'C'} + } + rv1 = breadth_first_search.bfs(self.graph, "A") + rv2 = breadth_first_search.bfs(self.graph, "G") + rv1e = breadth_first_search.bfs(self.graph, "Z") + self.assertEqual(rv1, {'A', 'B', 'D', 'F', 'C', 'G', 'E'}) + self.assertEqual(rv2, {'G', 'C', 'A', 'B', 'D', 'F', 'E'}) + self.assertEqual(rv1e, None) + + class TestDepthFirstSearch(unittest.TestCase): """ Tests DFS on a graph represented by a adjacency list @@ -158,33 +164,27 @@ def test_dfs(self): self.assertEqual(rv3e, None) -class TestBreadthFirstSearch(unittest.TestCase): +class TestKMPSearch(unittest.TestCase): """ - Tests DFS on a graph represented by a adjacency list + Tests KMP search on string "ABCDE FG ABCDEABCDEF" """ - def test_bfs(self): - self.graph = { - 'A': set(['B', 'C']), - 'B': set(['A', 'D', 'E']), - 'C': set(['A', 'F']), - 'D': set(['B']), - 'E': set(['B', 'F']), - 'F': set(['C', 'E']) - } - rv1 = breadth_first_search.bfs(self.graph, 'A') - self.assertEqual(rv1, {'C', 'A', 'B', 'D', 'F', 'E'}) - self.graph = { - 'A': set(['B', 'C', 'E']), - 'B': set(['A', 'D', 'F']), - 'C': set(['A', 'G']), - 'D': set(['B']), - 'F': set(['B']), - 'E': set(['A']), - 'G': set(['C']) - } - rv1 = breadth_first_search.bfs(self.graph, "A") - rv2 = breadth_first_search.bfs(self.graph, "G") - rv1e = breadth_first_search.bfs(self.graph, "Z") - self.assertEqual(rv1, set(['A', 'B', 'D', 'F', 'C', 'G', 'E'])) - self.assertEqual(rv2, set(['G', 'C', 'A', 'B', 'D', 'F', 'E'])) - self.assertEqual(rv1e, None) + + def test_kmpsearch(self): + self.string = "ABCDE FG ABCDEABCDEF" + rv1 = kmp_search.search(self.string, "ABCDEA") + rv2 = kmp_search.search(self.string, "ABCDER") + self.assertIs(rv1[0], 9) + self.assertFalse(rv2) + + +class TestRabinKarpSearch(unittest.TestCase): + """ + Tests Rabin-Karp search on string "ABCDEFGHIJKLMNOP" + """ + + def test_rabinkarpsearch(self): + self.string = "ABCDEFGHIJKLMNOP" + rv1 = rabinkarp_search.search(self.string, "MNOP") + rv2 = rabinkarp_search.search(self.string, "BCA") + self.assertIs(rv1[0], 12) + self.assertFalse(rv2) diff --git a/tests/test_shuffling.py b/tests/test_shuffling.py index 230d7eb..72acbc5 100644 --- a/tests/test_shuffling.py +++ b/tests/test_shuffling.py @@ -22,6 +22,6 @@ def test_knuthshuffle(self): for i in self.sorted: if i == self.shuffle[i]: - self.not_shuffled = self.not_shuffled + 1 + self.not_shuffled += 1 self.assertGreater(5, self.not_shuffled) diff --git a/tests/test_sorting.py b/tests/test_sorting.py index 7032eed..4bbc8b0 100644 --- a/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -2,17 +2,18 @@ import unittest from algorithms.sorting import ( + bogo_sort, bubble_sort, - selection_sort, + cocktail_sort, + comb_sort, + gnome_sort, + heap_sort, insertion_sort, merge_sort, quick_sort, - heap_sort, - shell_sort, - comb_sort, - cocktail_sort, quick_sort_in_place, - gnome_sort, + selection_sort, + shell_sort, strand_sort, ) @@ -28,6 +29,16 @@ def setUp(self): self.correct = list(range(10)) +class TestBogoSort(SortingAlgorithmTestCase): + """ + Tests Bogo sort on a small range from 0-9 + """ + + def test_bogosort(self): + self.output = bogo_sort.sort(self.input) + self.assertEqual(self.correct, self.input) + + class TestBubbleSort(SortingAlgorithmTestCase): """ Tests Bubble sort on a small range from 0-9 @@ -38,13 +49,43 @@ def test_bubblesort(self): self.assertEqual(self.correct, self.output) -class TestSelectionSort(SortingAlgorithmTestCase): +class TestCocktailSort(SortingAlgorithmTestCase): """ - Tests Selection sort on a small range from 0-9 + Tests Cocktail sort on a small range from 0-9 """ - def test_selectionsort(self): - self.output = selection_sort.sort(self.input) + def test_cocktailsort(self): + self.output = cocktail_sort.sort(self.input) + self.assertEqual(self.correct, self.output) + + +class TestCombSort(SortingAlgorithmTestCase): + """ + Tests Comb sort on a small range from 0-9 + """ + + def test_combsort(self): + self.output = comb_sort.sort(self.input) + self.assertEqual(self.correct, self.output) + + +class TestGnomeSort(SortingAlgorithmTestCase): + """ + Tests Gnome sort on a small range from 0-9 + """ + + def test_gnomesort(self): + self.output = gnome_sort.sort(self.input) + self.assertEqual(self.correct, self.output) + + +class TestHeapSort(SortingAlgorithmTestCase): + """ + Test Heap sort on a small range from 0-9 + """ + + def test_heapsort(self): + self.output = heap_sort.sort(self.input) self.assertEqual(self.correct, self.output) @@ -53,7 +94,7 @@ class TestInsertionSort(SortingAlgorithmTestCase): Tests Insertion sort on a small range from 0-9 """ - def test_selectionsort(self): + def test_insertionsort(self): self.output = insertion_sort.sort(self.input) self.assertEqual(self.correct, self.output) @@ -108,13 +149,13 @@ def test_partition(self): ) -class TestHeapSort(SortingAlgorithmTestCase): +class TestSelectionort(SortingAlgorithmTestCase): """ - Test Heap sort on a small range from 0-9 + Test Selection sort on a small range from 0-9 """ - def test_heapsort(self): - self.output = heap_sort.sort(self.input) + def test_selectionsort(self): + self.output = selection_sort.sort(self.input) self.assertEqual(self.correct, self.output) @@ -128,36 +169,6 @@ def test_shellsort(self): self.assertEqual(self.correct, self.output) -class TestCombSort(SortingAlgorithmTestCase): - """ - Test Comb sort on a small range from 0-9 - """ - - def test_combsort(self): - self.output = comb_sort.sort(self.input) - self.assertEqual(self.correct, self.output) - - -class TestCocktailSort(SortingAlgorithmTestCase): - """ - Tests Cocktail sort on a small range from 0-9 - """ - - def test_cocktailsort(self): - self.output = cocktail_sort.sort(self.input) - self.assertEqual(self.correct, self.output) - - -class TestGnomeSort(SortingAlgorithmTestCase): - """ - Tests Gnome sort on a small range from 0-9 - """ - - def test_gnomesort(self): - self.output = gnome_sort.sort(self.input) - self.assertEqual(self.correct, self.output) - - class TestStrandSort(SortingAlgorithmTestCase): """ Tests Strand sort on a small range from 0-9 From 79d3a934b063f38717249ab83078572039a38257 Mon Sep 17 00:00:00 2001 From: yavinash Date: Fri, 22 Apr 2016 15:26:20 +0530 Subject: [PATCH 77/89] Using Size() method to check stack empty, removed unnecessary class variable declaration --- algorithms/data_structures/stack.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/algorithms/data_structures/stack.py b/algorithms/data_structures/stack.py index 0ba5b39..56e687b 100644 --- a/algorithms/data_structures/stack.py +++ b/algorithms/data_structures/stack.py @@ -11,7 +11,6 @@ class Stack: - stack_list = [] def __init__(self): self.stack_list = [] @@ -39,7 +38,7 @@ def is_empty(self): Time Complexity: O(1) """ - return not len(self.stack_list) + return not self.size() def size(self): """ From a71b89cc87b631f37ab90864c2e53fe9424f8bd7 Mon Sep 17 00:00:00 2001 From: Jon Miller Date: Fri, 29 Apr 2016 23:38:05 -0700 Subject: [PATCH 78/89] Remove unnecessary class variable --- algorithms/data_structures/queue.py | 1 - 1 file changed, 1 deletion(-) diff --git a/algorithms/data_structures/queue.py b/algorithms/data_structures/queue.py index ef381d4..64c3198 100644 --- a/algorithms/data_structures/queue.py +++ b/algorithms/data_structures/queue.py @@ -15,7 +15,6 @@ class Queue: - queue_list = deque([]) def __init__(self): self.queue_list = deque([]) From 033e31ecde15834094e135b929e0fee468b202af Mon Sep 17 00:00:00 2001 From: Jon Miller Date: Sat, 30 Apr 2016 00:03:10 -0700 Subject: [PATCH 79/89] Ignore .cache/ directory generated during testing --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 84034a4..f4b321e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ build/ /*.egg-info *.ropeproject/ docs/_build -.idea/* \ No newline at end of file +.idea/* +.cache/ From 02a975356d6a6b36cc565e8f4b771497867f09dd Mon Sep 17 00:00:00 2001 From: Jon Miller Date: Wed, 4 May 2016 23:46:29 -0500 Subject: [PATCH 80/89] Add test case for Pollard's Rho at zero to bump test coverage --- tests/test_factorization.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_factorization.py b/tests/test_factorization.py index 4033e1f..4181118 100644 --- a/tests/test_factorization.py +++ b/tests/test_factorization.py @@ -27,6 +27,14 @@ def test_pollard_rho(self): res *= j self.assertEqual(x, res) + def test_pollard_rho_x_is_zero(self): + x = 0 + factors = pollard_rho(x) + res = 1 + for j in factors: + res *= j + self.assertEqual(x, res) + class TestTrialDivision(unittest.TestCase): From 6c0fba49b7b2ec40a3f7e25d14f69a8225defcc5 Mon Sep 17 00:00:00 2001 From: Siarhei Padlozny Date: Thu, 5 May 2016 14:52:58 +0300 Subject: [PATCH 81/89] Add ternary search and tests for it --- algorithms/searching/ternary_search.py | 22 ++++++++++++++++++++++ docs/searching.rst | 5 +++++ tests/test_searching.py | 19 ++++++++++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 algorithms/searching/ternary_search.py diff --git a/algorithms/searching/ternary_search.py b/algorithms/searching/ternary_search.py new file mode 100644 index 0000000..6cb51de --- /dev/null +++ b/algorithms/searching/ternary_search.py @@ -0,0 +1,22 @@ +""" + Ternary search + --------------- + Finds the maximum of unimodal function fn() within [left, right] + To find the minimum, revert the if/else statement or revert the comparison. + + Time Complexity: O(log(n)) + +""" + + +def search(fn, left, right, precision): + while abs(right - left) > precision: + left_third = left + (right - left) / 3 + right_third = right - (right - left) / 3 + + if fn(left_third) < fn(right_third): + left = left_third + else: + right = right_third + + return (left + right) / 2 diff --git a/docs/searching.rst b/docs/searching.rst index 474a236..5ac96c7 100644 --- a/docs/searching.rst +++ b/docs/searching.rst @@ -25,3 +25,8 @@ Searching :members: :undoc-members: :show-inheritance: + +.. automodule:: algorithms.searching.ternary_search + :members: + :undoc-members: + :show-inheritance: diff --git a/tests/test_searching.py b/tests/test_searching.py index ec040bb..a3ee914 100644 --- a/tests/test_searching.py +++ b/tests/test_searching.py @@ -1,5 +1,6 @@ """ Unit Tests for searching """ import unittest +import math from algorithms.searching import ( binary_search, @@ -7,7 +8,8 @@ breadth_first_search, depth_first_search, kmp_search, - rabinkarp_search + rabinkarp_search, + ternary_search ) @@ -188,3 +190,18 @@ def test_rabinkarpsearch(self): rv2 = rabinkarp_search.search(self.string, "BCA") self.assertIs(rv1[0], 12) self.assertFalse(rv2) + + +class TestTernarySearch(unittest.TestCase): + """ + Tests teranry search algorithm on unimodal functions + """ + + def test_terarysearch(self): + self.function1 = lambda x: -(x - 2) ** 2 + self.function2 = lambda x: math.cos(x) + self.eps = 1e-6 + rv1 = ternary_search.search(self.function1, -2.0, 2.0, self.eps) + rv2 = ternary_search.search(self.function2, -2.0, 2.0, self.eps) + self.assertAlmostEqual(rv1, 2.0, 6) + self.assertAlmostEqual(rv2, 0.0, 6) From f5a0306530a45846ae21ab8a49b28b1533e18811 Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Tue, 24 May 2016 00:14:21 -0500 Subject: [PATCH 82/89] fix error for removing not existing node in single linked list and refactor code --- .../data_structures/singly_linked_list.py | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/algorithms/data_structures/singly_linked_list.py b/algorithms/data_structures/singly_linked_list.py index 5aa3d15..c08a0d8 100644 --- a/algorithms/data_structures/singly_linked_list.py +++ b/algorithms/data_structures/singly_linked_list.py @@ -48,47 +48,42 @@ def add(self, value): self.head = node self.size += 1 - def remove(self, value): - """ - Remove element from list - - Time Complexity: O(N) - """ + def _search_node(self, value, remove=False): current = self.head previous = None - found = False - while not found: + while current: if current.data == value: - found = True - self.size -= 1 + break else: previous = current current = current.next - if previous is None: # Head node - self.head = current.next - else: # None head node - previous.set_next(current.next) + if remove and current: + if previous is None: # Head node + self.head = current.next + else: # None head node + previous.set_next(current.next) + self.size -= 1 - return found + return current is not None - def search(self, value): + def remove(self, value): """ - Search for value in list + Remove element from list Time Complexity: O(N) """ - current = self.head - found = False - while current and not found: - if current.get_data() == value: - found = True - else: - current = current.next + return self._search_node(value, True) - return found + def search(self, value): + """ + Search for value in list + + Time Complexity: O(N) + """ + return self._search_node(value) def size(self): """ From 8131d6e83d8f9ac88a8c3bfb74e9ed7abccc3136 Mon Sep 17 00:00:00 2001 From: david watson Date: Mon, 15 Aug 2016 18:59:20 -0400 Subject: [PATCH 83/89] Correct spelling error in documentation This fixes the spelling error in the documentation here: http://algorithms.readthedocs.io/en/latest/data_structures.html#unidrected-graph --- algorithms/data_structures/undirected_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/data_structures/undirected_graph.py b/algorithms/data_structures/undirected_graph.py index c2a6e7b..2c70e36 100644 --- a/algorithms/data_structures/undirected_graph.py +++ b/algorithms/data_structures/undirected_graph.py @@ -1,5 +1,5 @@ """ - Unidrected Graph + Undirected Graph ---------------- The Undirected_Graph class represents an undirected graph of vertices which can be any hashable value. From 35de2f333a7249d7da5ca9452e726ce7abe1da9e Mon Sep 17 00:00:00 2001 From: Robert DeSimone Date: Sat, 20 Aug 2016 19:30:40 -0400 Subject: [PATCH 84/89] Update lcs.py Fix typo in comment header --- algorithms/dynamic_programming/lcs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/dynamic_programming/lcs.py b/algorithms/dynamic_programming/lcs.py index 6bdabf2..f386b7a 100644 --- a/algorithms/dynamic_programming/lcs.py +++ b/algorithms/dynamic_programming/lcs.py @@ -1,5 +1,5 @@ """ - Longest Common Sunsequence + Longest Common Subsequence -------------------------- Implements the dynamic programming solution to the longest common subsequence algorithm. From 406e669d544d42ff5bceac6e9e643d860128162d Mon Sep 17 00:00:00 2001 From: Joe Green Date: Sun, 16 Oct 2016 12:23:15 +0530 Subject: [PATCH 85/89] Update queue.py Removed the first as it became unessary because __init__ itself makes a deque object Made the deque object as private so as to protect from unessary alteration from users --- algorithms/data_structures/queue.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/algorithms/data_structures/queue.py b/algorithms/data_structures/queue.py index ef381d4..2f9cf22 100644 --- a/algorithms/data_structures/queue.py +++ b/algorithms/data_structures/queue.py @@ -15,10 +15,9 @@ class Queue: - queue_list = deque([]) def __init__(self): - self.queue_list = deque([]) + self._queue_list = deque([]) def add(self, value): """ @@ -26,7 +25,7 @@ def add(self, value): Worst Case Complexity: O(1) """ - self.queue_list.append(value) + self._queue_list.append(value) def remove(self): """ @@ -35,7 +34,7 @@ def remove(self): Worst Case Complexity: O(1) """ - return self.queue_list.popleft() + return self._queue_list.popleft() def is_empty(self): """ @@ -43,7 +42,7 @@ def is_empty(self): Worst Case Complexity: O(1) """ - return not len(self.queue_list) + return not len(self._queue_list) def size(self): """ @@ -51,4 +50,4 @@ def size(self): Worst Case Complexity: O(1) """ - return len(self.queue_list) + return len(self._queue_list) From 576d79606901e9d8d760ee1fb2285220ddeaa672 Mon Sep 17 00:00:00 2001 From: Joe Green Date: Sun, 16 Oct 2016 12:25:16 +0530 Subject: [PATCH 86/89] Update queue.py Renamed _queue_list to _queue as 1. It is more meaningful in this context 2. Deque object is not a list object --- algorithms/data_structures/queue.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/algorithms/data_structures/queue.py b/algorithms/data_structures/queue.py index 2f9cf22..1570c04 100644 --- a/algorithms/data_structures/queue.py +++ b/algorithms/data_structures/queue.py @@ -17,7 +17,7 @@ class Queue: def __init__(self): - self._queue_list = deque([]) + self._queue = deque([]) def add(self, value): """ @@ -25,7 +25,7 @@ def add(self, value): Worst Case Complexity: O(1) """ - self._queue_list.append(value) + self._queue.append(value) def remove(self): """ @@ -34,7 +34,7 @@ def remove(self): Worst Case Complexity: O(1) """ - return self._queue_list.popleft() + return self._queue.popleft() def is_empty(self): """ @@ -42,7 +42,7 @@ def is_empty(self): Worst Case Complexity: O(1) """ - return not len(self._queue_list) + return not len(self._queue) def size(self): """ @@ -50,4 +50,4 @@ def size(self): Worst Case Complexity: O(1) """ - return len(self._queue_list) + return len(self._queue) From 88e4b9df2277bef1007d1b1548f4dff52fa29ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Guilherme=20Farias=20Duda?= Date: Thu, 12 Jan 2017 12:16:54 -0300 Subject: [PATCH 87/89] Fixing typo on LCS --- algorithms/dynamic_programming/lcs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/algorithms/dynamic_programming/lcs.py b/algorithms/dynamic_programming/lcs.py index 6bdabf2..f386b7a 100644 --- a/algorithms/dynamic_programming/lcs.py +++ b/algorithms/dynamic_programming/lcs.py @@ -1,5 +1,5 @@ """ - Longest Common Sunsequence + Longest Common Subsequence -------------------------- Implements the dynamic programming solution to the longest common subsequence algorithm. From 13160cd3774eab40f1037f986bf725494e43542a Mon Sep 17 00:00:00 2001 From: Canux Date: Wed, 18 Jan 2017 18:07:15 +0800 Subject: [PATCH 88/89] fix the logic about bubble sort. the range for n should be 1 to length-i. No need to compare from first to last every time. --- algorithms/sorting/bubble_sort.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/algorithms/sorting/bubble_sort.py b/algorithms/sorting/bubble_sort.py index fc5c7b8..b8b0964 100644 --- a/algorithms/sorting/bubble_sort.py +++ b/algorithms/sorting/bubble_sort.py @@ -23,8 +23,8 @@ def sort(seq): :rtype: A list of sorted integers """ L = len(seq) - for _ in range(L): - for n in range(1, L): + for i in range(L): + for n in range(1, L - i): if seq[n] < seq[n - 1]: seq[n - 1], seq[n] = seq[n], seq[n - 1] return seq From 636a4519832849dcb6516f2ef78428872b3bb81c Mon Sep 17 00:00:00 2001 From: Nic Young Date: Fri, 13 Apr 2018 22:22:20 -0600 Subject: [PATCH 89/89] chore: updated readme.rst --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 3b4acde..7964a88 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,6 @@ +**This repository is no longer maintained, but is being kept around for educational purposes. If you want a more complete algorithms repo check out: https://github.com/keon/algorithms** +===== + Algorithms ==========