From 30b71153feacd415ff82310345ff579ff76cc14f Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Mon, 23 Oct 2017 19:04:57 +0300 Subject: [PATCH 01/34] author --- pygorithm/data_structures/trie.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pygorithm/data_structures/trie.py b/pygorithm/data_structures/trie.py index 5637cc5..c9b2dee 100644 --- a/pygorithm/data_structures/trie.py +++ b/pygorithm/data_structures/trie.py @@ -1,7 +1,6 @@ -''' -Node class to create a node -for trie -''' +""" +Author: MrDupin +""" class Node: def __init__(self, v, p=None, w=False): From 08fba2bd0adc1225c1c29a637bcda413d2a38957 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Tue, 24 Oct 2017 15:45:40 +0300 Subject: [PATCH 02/34] update dp init --- pygorithm/dynamic_programming/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pygorithm/dynamic_programming/__init__.py b/pygorithm/dynamic_programming/__init__.py index 4a7d594..b3dd710 100644 --- a/pygorithm/dynamic_programming/__init__.py +++ b/pygorithm/dynamic_programming/__init__.py @@ -3,8 +3,10 @@ """ from . import binary_knapsack from . import lis +from . import min_cost_path __all__ = [ 'binary_knapsack', - 'lis' + 'lis', + 'min_cost_path' ] From 75a721261a3f16ded767343cd184057da36e7b2b Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Tue, 24 Oct 2017 15:56:22 +0300 Subject: [PATCH 03/34] test for min_cost_path --- tests/test_dynamic_programming.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_dynamic_programming.py b/tests/test_dynamic_programming.py index 6489fc2..e6c6327 100644 --- a/tests/test_dynamic_programming.py +++ b/tests/test_dynamic_programming.py @@ -3,7 +3,8 @@ from pygorithm.dynamic_programming import ( binary_knapsack, - lis + lis, + min_cost_path ) @@ -21,5 +22,14 @@ def test_lis(self): self.assertEqual(ans[0], 5) self.assertEqual(ans[1], [10, 22, 33, 50, 60]) +class TestMinCostPath(unittest.TestCase): + def test_min_cost_path(self): + matrix = [[5, 3, 10, 17, 1], + [4, 2, 9, 8, 5], + [11, 12, 3, 9, 6], + [1, 3, 4, 2, 10], + [7, 11, 13, 7, 3]] + self.assertEqual(min_cost_path.find_path(matrix), 38) + if __name__ == '__main__': unittest.main() From 7c2908201cab353a2a77bc396249da02acfd8ce6 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Tue, 24 Oct 2017 15:58:04 +0300 Subject: [PATCH 04/34] Create min_cost_path.py --- .../dynamic_programming/min_cost_path.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 pygorithm/dynamic_programming/min_cost_path.py diff --git a/pygorithm/dynamic_programming/min_cost_path.py b/pygorithm/dynamic_programming/min_cost_path.py new file mode 100644 index 0000000..ad01edf --- /dev/null +++ b/pygorithm/dynamic_programming/min_cost_path.py @@ -0,0 +1,51 @@ +""" +Author: MrDupin +Created At: 25th August 2017 +""" +import inspect + +#Path(i, j) = min(Path(i-1, j), Path(i, j-1) + Matrix(i, j) + + +def calculate_path(i, j, matrix, s): + if(s[i][j] > 0): + #We have already calculated solution for i,j; return it. + return s[i][j] + + m1 = calculate_path(i-1, j, matrix, s) + matrix[i][j] #Optimal solution for i-1, j (top) + m2 = calculate_path(i, j-1, matrix, s) + matrix[i][j] #Optimal solution for i, j-1 (left) + + #Store and return the optimal (minimum) solution + if(m1 < m2): + s[i][j] = m1 + return m1 + else: + s[i][j] = m2 + return m2 + + +def find_path(matrix): + l = len(matrix); + #Initialize solution array. + #A node of i, j in solution has an equivalent node of i, j in matrix + s = [[0 for i in range(l)] for j in range(l)]; + + #Initialize first node as its matrix equivalent + s[0][0] = matrix[0][0] + + #Initialize first column as the matrix equivalent + the above solution + for i in range(1, l): + s[i][0] = matrix[i][0] + s[i-1][0] + + #Initialize first row as the matrix equivalent + the left solution + for j in range(1, l): + s[0][j] = matrix[0][j] + s[0][j-1] + + return calculate_path(l-1, l-1, matrix, s) + + +def get_code(): + """ + returns the code for the min cost path function + """ + return inspect.getsource(calculate_path) From cbf44050d244e6ecb7a897f053ba8c5ec5d662f9 Mon Sep 17 00:00:00 2001 From: Ian Doarn Date: Tue, 31 Oct 2017 08:55:46 -0500 Subject: [PATCH 05/34] Implemented Radix Sorting, thanks @ashaywalke Implemented Radix Sorting unittests --- pygorithm/sorting/__init__.py | 2 ++ pygorithm/sorting/radix_sort.py | 38 +++++++++++++++++++++++++++++++++ tests/test_sorting.py | 14 +++++++++++- 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 pygorithm/sorting/radix_sort.py diff --git a/pygorithm/sorting/__init__.py b/pygorithm/sorting/__init__.py index 0781eea..e91f6f0 100644 --- a/pygorithm/sorting/__init__.py +++ b/pygorithm/sorting/__init__.py @@ -8,6 +8,7 @@ from . import counting_sort from . import insertion_sort from . import merge_sort +from . import radix_sort from . import selection_sort from . import shell_sort @@ -19,6 +20,7 @@ 'insertion_sort', 'merge_sort', 'quick_sort', + 'radix_sort', 'selection_sort', 'shell_sort' ] diff --git a/pygorithm/sorting/radix_sort.py b/pygorithm/sorting/radix_sort.py new file mode 100644 index 0000000..bc4fe4b --- /dev/null +++ b/pygorithm/sorting/radix_sort.py @@ -0,0 +1,38 @@ +""" +Author: Ian Doarn +Date: 31st Oct 2017 + +Reference: + https://stackoverflow.com/questions/35419229/python-radix-sort +""" + + +def sort(_list, base=10): + """ + Radix Sort + + :param _list: array to sort + :param base: base radix number + :return: sorted list + """ + # TODO: comment this + + result_list = [] + power = 0 + while _list: + bs = [[] for _ in range(base)] + for x in _list: + bs[x // base ** power % base].append(x) + _list = [] + for b in bs: + for x in b: + if x < base ** (power + 1): + result_list.append(x) + else: + _list.append(x) + power += 1 + return result_list + + +if __name__ == '__main__': + print(sort([170, 45, 75, 90, 802, 24, 2, 66])) diff --git a/tests/test_sorting.py b/tests/test_sorting.py index 7f08153..89c6157 100644 --- a/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -10,7 +10,8 @@ counting_sort, bucket_sort, shell_sort, - heap_sort) + heap_sort, + radix_sort) class TestSortingAlgorithm(unittest.TestCase): @@ -105,5 +106,16 @@ def test_heap_sort(self): self.alphaResult = heap_sort.sort(self.alphaArray) self.assertEqual(self.alphaResult, self.sorted_alpha_array) + +class TestRadixSort(TestSortingAlgorithm): + def test_radix_sort(self): + self.result = radix_sort.sort(self.array) + self.assertEqual(self.result, self.sorted_array) + + # TODO: Fix radix sort to sort alphabetically + # self.alphaResult = radix_sort.sort(self.alphaArray) + # self.assertEqual(self.alphaResult, self.sorted_alpha_array) + + if __name__ == '__main__': unittest.main() From 38c99009e7663afdc886681410a53fdd3dd6248a Mon Sep 17 00:00:00 2001 From: Ian Doarn Date: Tue, 31 Oct 2017 09:01:36 -0500 Subject: [PATCH 06/34] Updated CONTRIBUTORS.md Updated __init__.py --- CONTIRBUTORS.md | 1 + pygorithm/__init__.py | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CONTIRBUTORS.md b/CONTIRBUTORS.md index ff19646..5f53af9 100644 --- a/CONTIRBUTORS.md +++ b/CONTIRBUTORS.md @@ -17,3 +17,4 @@ - Sharad '[sharadbhat](https://github.com/sharadbhat)' Bhat - Alexey '[aesee](https://github.com/aesee)' Sarapulov - Anthony '[MrDupin](https://github.com/MrDupin)' Marakis + - Ashey '[asheywalke](https://github.com/ashaywalke)' Walke diff --git a/pygorithm/__init__.py b/pygorithm/__init__.py index 8117acc..05bf36e 100644 --- a/pygorithm/__init__.py +++ b/pygorithm/__init__.py @@ -30,6 +30,7 @@ Sharad 'sharadbhat' Bhat Alexey 'aesee' Sarapulov Anthony 'MrDupin' Marakis +Ashey 'asheywalke' Walke """ @@ -57,7 +58,8 @@ "Emil 'Skeen' Madsen", "Ian 'IanDoarn' Doarn", "Timothy 'Tjstretchalot' Moore", - "Sharad 'sharadbhat' Bhat" + "Sharad 'sharadbhat' Bhat", + "Ashey 'asheywalke' Walke" ] __all__ = [ From d39d762211c78c1faed539eee7fc1fad0e6623d4 Mon Sep 17 00:00:00 2001 From: ashu01 Date: Sat, 30 Dec 2017 08:01:39 +0530 Subject: [PATCH 07/34] fixed shadow --- pygorithm/fibonacci/memoization.py | 15 +++++++-------- pygorithm/fibonacci/recursion.py | 12 ++++++------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/pygorithm/fibonacci/memoization.py b/pygorithm/fibonacci/memoization.py index 6374aa4..8a522e9 100644 --- a/pygorithm/fibonacci/memoization.py +++ b/pygorithm/fibonacci/memoization.py @@ -2,7 +2,6 @@ Fibonacci implementation through cache. """ import inspect -# TODO: Fix shadowed parameter names def get_sequence(n): @@ -11,20 +10,20 @@ def get_sequence(n): """ cache = {0: 0, 1: 1} - def fib(n): + def fib(num): """ Return Fibonacci value by specified number as integer. """ - if n in cache: - return cache[n] - cache[n] = fib(n - 1) + fib(n - 2) - return cache[n] + if num in cache: + return cache[num] + cache[num] = fib(num - 1) + fib(num - 2) + return cache[num] - def sequence(n): + def sequence(num): """ Return sequence of Fibonacci values as list. """ - return [fib(value) for value in range(n + 1)] + return [fib(value) for value in range(num + 1)] return sequence(n) diff --git a/pygorithm/fibonacci/recursion.py b/pygorithm/fibonacci/recursion.py index bf14730..beeee4c 100644 --- a/pygorithm/fibonacci/recursion.py +++ b/pygorithm/fibonacci/recursion.py @@ -8,20 +8,20 @@ def get_sequence(n): """ Return Fibonacci sequence from zero to specified number as list. """ - def fib(n): + def fib(num): """ Return Fibonacci value by specified number as integer. """ - if n <= 1: - return n + if num <= 1: + return num - return fib(n - 1) + fib(n - 2) + return fib(num - 1) + fib(num - 2) - def sequence(n): + def sequence(num): """ Return sequence of Fibonacci values as list. """ - return [fib(value) for value in range(n + 1)] + return [fib(value) for value in range(num + 1)] return sequence(n) From 26d5e47012442adf0437737765e4ca54ad9a514d Mon Sep 17 00:00:00 2001 From: ashu01 Date: Sat, 30 Dec 2017 08:02:22 +0530 Subject: [PATCH 08/34] added missing import --- pygorithm/dynamic_programming/lis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygorithm/dynamic_programming/lis.py b/pygorithm/dynamic_programming/lis.py index ae8ff5a..3a111a5 100644 --- a/pygorithm/dynamic_programming/lis.py +++ b/pygorithm/dynamic_programming/lis.py @@ -2,7 +2,7 @@ Author: Omkar Pathak Created At: 25th August 2017 """ - +import inspect def longest_increasing_subsequence(_list): """ From b8d54def554354b68751afa0aa986a33de72741a Mon Sep 17 00:00:00 2001 From: Dat Quoc Ngo Date: Thu, 1 Mar 2018 10:42:58 -0600 Subject: [PATCH 09/34] Add insert and count-nodes I added insert and counting-node functions for BinaryTree and BinarySearchTree --- pygorithm/data_structures/tree.py | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pygorithm/data_structures/tree.py b/pygorithm/data_structures/tree.py index 2967bc7..1a9c96b 100644 --- a/pygorithm/data_structures/tree.py +++ b/pygorithm/data_structures/tree.py @@ -72,6 +72,16 @@ def __init__(self): self._pre_order = [] self._post_order = [] + def insert(self, data): + """ + insert data to root or create a root node + """ + if self.root: + self.root.set_data(data) + else: + self.root = Node() + self.root.set_data(data) + def inorder(self, root): """ in this we traverse first to the leftmost node, @@ -117,6 +127,24 @@ def postorder(self, root): self._post_order.append(root.get_data()) return self._post_order + def number_of_nodes(self, root): + """ + counting number of nodes + """ + # need testing + left_number = 0; + right_number = 0; + + #number of nodes left side + if root.get_left(): + left_number = self.number_of_nodes(root.get_left()) + + #numbeof nodes right side + if root.get_right(): + right_number = self.number_of_nodes(root.get_right()) + + return left_number + right_number + 1 + @staticmethod def get_code(): """ @@ -359,6 +387,24 @@ def postorder(self): if self.root is not None: return self.root.postorder(self.root) + def number_of_nodes(self, root): + """ + counting number of nodes + """ + # need testing + left_number = 0; + right_number = 0; + + #number of nodes left side + if root.get_left(): + left_number = self.number_of_nodes(root.get_left()) + + #numbeof nodes right side + if root.get_right(): + right_number = self.number_of_nodes(root.get_right()) + + return left_number + right_number + 1 + @staticmethod def get_code(): """ From dfb83dfe55c21d9dc9f51482f733fd7bc036dff5 Mon Sep 17 00:00:00 2001 From: Omkar Pathak Date: Tue, 18 Sep 2018 09:25:24 +0530 Subject: [PATCH 10/34] Updated README.rst --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 43a41db..c25b876 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,10 @@ Pygorithm ========= +.. image:: http://pepy.tech/badge/pygorithm + :target: http://pepy.tech/project/pygorithm + :alt: Downloads + .. image:: https://readthedocs.org/projects/pygorithm/badge/?version=latest :target: http://pygorithm.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status From 345d371be024461bc6adcab68a896301903f2014 Mon Sep 17 00:00:00 2001 From: tsehori <10086302+tsehori@users.noreply.github.com> Date: Tue, 2 Oct 2018 00:07:40 +0300 Subject: [PATCH 11/34] Update heap_sort.py Added explanation for how heap-sort works and corresponds to the method heapify(). --- pygorithm/sorting/heap_sort.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pygorithm/sorting/heap_sort.py b/pygorithm/sorting/heap_sort.py index 9b02024..ffc177f 100644 --- a/pygorithm/sorting/heap_sort.py +++ b/pygorithm/sorting/heap_sort.py @@ -13,11 +13,15 @@ def sort(_list): """ heap sort algorithm + Create the heap using heapify(). + This is an implementation of max-heap, so after bullding the heap, the max element is at the top (_list[0]). + We move it to the end of the list (_list[end]), which will later become the sorted list. + After moving this element to the end, we take the element in the end to the top and shift it down to its right location in the heap. + We proceed to do the same for all elements in the heap, such that in the end we're left with the sorted list. :param _list: list of values to sort :return: sorted values """ - # TODO: Add description of how this works! # create the heap heapify(_list) From e3f20690f55818999dc5474c3a6445749fdaec4a Mon Sep 17 00:00:00 2001 From: Omkar Pathak Date: Thu, 15 Nov 2018 12:17:04 +0530 Subject: [PATCH 12/34] Update README.rst --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.rst b/README.rst index c25b876..1f1e000 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,11 @@ Pygorithm ========= + +.. image:: https://img.shields.io/packagist/l/doctrine/orm.svg + :target: https://github.com/OmkarPathak/pygorithm/blob/master/LICENSE + :alt: Packagist + .. image:: http://pepy.tech/badge/pygorithm :target: http://pepy.tech/project/pygorithm :alt: Downloads @@ -13,6 +18,10 @@ Pygorithm .. image:: https://img.shields.io/badge/Python-3.6-brightgreen.svg :target: https://github.com/OmkarPathak/pygorithm :alt: Python 3.6 + +.. image:: https://img.shields.io/badge/Say%20Thanks-%F0%9F%A6%89-1EAEDB.svg + :target: https://saythanks.io/to/OmkarPathak + :alt: Say Thanks! | A Python module to learn all the major algorithms on the go! | Purely for educational purposes From 20546c9c9b80211ebe3609f51f1c45429909e0bf Mon Sep 17 00:00:00 2001 From: Omkar Pathak Date: Thu, 15 Nov 2018 13:44:36 +0530 Subject: [PATCH 13/34] Update README.rst --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 1f1e000..1528dd4 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,10 @@ Pygorithm .. image:: https://img.shields.io/badge/Say%20Thanks-%F0%9F%A6%89-1EAEDB.svg :target: https://saythanks.io/to/OmkarPathak :alt: Say Thanks! + +.. image:: https://img.shields.io/github/contributors/omkarpathak/pygorithm.svg + :target: https://github.com/OmkarPathak/pygorithm/graphs/contributors + :alt: Contributors | A Python module to learn all the major algorithms on the go! | Purely for educational purposes From 24210ed4fc6cc2ef165aa3fbe7dbd1c813a69783 Mon Sep 17 00:00:00 2001 From: Sunil Joshi Date: Tue, 30 Jul 2019 12:07:55 +0530 Subject: [PATCH 14/34] Issue#88 - Fixed (#89) --- pygorithm/data_structures/linked_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygorithm/data_structures/linked_list.py b/pygorithm/data_structures/linked_list.py index 0107b62..2df8cce 100644 --- a/pygorithm/data_structures/linked_list.py +++ b/pygorithm/data_structures/linked_list.py @@ -50,7 +50,7 @@ def _search(self, node, data): return False if node.data == data: return node - return self._search(node.get_next(), data) + return self._search(node.next, data) def get_data(self): """ From 0be25396340fd002f08e024dc90a0bb9ed0f1792 Mon Sep 17 00:00:00 2001 From: SAYAN SANYAL <37104041+s-sanyal@users.noreply.github.com> Date: Sun, 6 Oct 2019 06:50:57 +0530 Subject: [PATCH 15/34] Added Longest Common Subsequence Algorithm (#91) --- pygorithm/dynamic_programming/lcs.py | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 pygorithm/dynamic_programming/lcs.py diff --git a/pygorithm/dynamic_programming/lcs.py b/pygorithm/dynamic_programming/lcs.py new file mode 100644 index 0000000..cdbba95 --- /dev/null +++ b/pygorithm/dynamic_programming/lcs.py @@ -0,0 +1,48 @@ +""" +A subsequence is a sequence that can be derived from another +sequence by deleting some or no elements without changing the +order of the remaining elements. + +For example, 'abd' is a subsequence of 'abcd' whereas 'adc' is not + +Given 2 strings containing lowercase english alphabets, find the length +of the Longest Common Subsequence (L.C.S.). + +Example: + Input: 'abcdgh' + 'aedfhr' + Output: 3 + + Explanation: The longest subsequence common to both the string is "adh" + +Time Complexity : O(M*N) +Space Complexity : O(M*N), where M and N are the lengths of the 1st and 2nd string +respectively. + +""" + + +def longest_common_subsequence(s1, s2): + """ + :param s1: string + :param s2: string + :return: int + """ + m = len(s1) + n = len(s2) + + dp = [[0] * (n + 1) for i in range(m + 1)] + """ + dp[i][j] : contains length of LCS of s1[0..i-1] and s2[0..j-1] + """ + + for i in range(m + 1): + for j in range(n + 1): + if i == 0 or j == 0: + dp[i][j] = 0 + elif s1[i - 1] == s2[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + 1 + else: + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + + return dp[m][n] From dffa590e7ad28b2bb991dd84ea0aadca6d9ae50c Mon Sep 17 00:00:00 2001 From: Abhay Raj Singh Date: Sun, 6 Oct 2019 16:05:26 +0000 Subject: [PATCH 16/34] Optimised and simplified lcs.py (#92) --- pygorithm/dynamic_programming/lcs.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pygorithm/dynamic_programming/lcs.py b/pygorithm/dynamic_programming/lcs.py index cdbba95..e30e6ee 100644 --- a/pygorithm/dynamic_programming/lcs.py +++ b/pygorithm/dynamic_programming/lcs.py @@ -28,19 +28,16 @@ def longest_common_subsequence(s1, s2): :param s2: string :return: int """ - m = len(s1) - n = len(s2) + m, n = len(s1), len(s2) - dp = [[0] * (n + 1) for i in range(m + 1)] + dp = [[0] * (n + 1)] * (m + 1) """ dp[i][j] : contains length of LCS of s1[0..i-1] and s2[0..j-1] """ - for i in range(m + 1): - for j in range(n + 1): - if i == 0 or j == 0: - dp[i][j] = 0 - elif s1[i - 1] == s2[j - 1]: + for i in range(1, m + 1): + for j in range(1, n + 1): + if s1[i - 1] == s2[j - 1]: dp[i][j] = dp[i - 1][j - 1] + 1 else: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) From 9fab4749ca13ec0649d3ec6ed87596ded1d16c88 Mon Sep 17 00:00:00 2001 From: Abhay Raj Singh Date: Mon, 7 Oct 2019 04:31:21 +0000 Subject: [PATCH 17/34] Fixed sieve (#93) * Fixed sieve - Algo now printing n when if prime - updated tests * Removed extra 'this' --- pygorithm/math/sieve_of_eratosthenes.py | 4 ++-- tests/test_math.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pygorithm/math/sieve_of_eratosthenes.py b/pygorithm/math/sieve_of_eratosthenes.py index 4aa4d52..0b877ee 100644 --- a/pygorithm/math/sieve_of_eratosthenes.py +++ b/pygorithm/math/sieve_of_eratosthenes.py @@ -29,7 +29,7 @@ def sieve_of_eratosthenes(n): p = 2 while p * p <= n: - # if p is not marked as False, this it is a prime + # if p is not marked as False, it is a prime if primes[p]: # mark all the multiples of number as False for i in range(p * 2, n + 1, p): @@ -37,7 +37,7 @@ def sieve_of_eratosthenes(n): p += 1 # getting all primes - primes = [element for element in range(2, n) if primes[element]] + primes = [element for element in range(2, n + 1) if primes[element]] return primes diff --git a/tests/test_math.py b/tests/test_math.py index 983e5e2..66c764c 100644 --- a/tests/test_math.py +++ b/tests/test_math.py @@ -17,7 +17,7 @@ def test_lcm_using_gcd(self): class TestSieveOfEratosthenes(unittest.TestCase): def test_sieve_of_eratosthenes(self): - self.assertEqual(sieve_of_eratosthenes.sieve_of_eratosthenes(10), [2, 3, 5, 7]) + self.assertEqual(sieve_of_eratosthenes.sieve_of_eratosthenes(11), [2, 3, 5, 7, 11]) class TestFactorial(unittest.TestCase): def test_factorial(self): From cb83b18c84ebc2de2f784869aa320f685636f9ef Mon Sep 17 00:00:00 2001 From: rqj <9392566+rqj@users.noreply.github.com> Date: Tue, 15 Oct 2019 07:27:28 +0300 Subject: [PATCH 18/34] Changing implementation for weighted graphs (#94) * Suggestion for changes in the WeightedGraph class * Suggestion for changes in the WeightedGraph class --- pygorithm/data_structures/graph.py | 50 +++++++++++++++++------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/pygorithm/data_structures/graph.py b/pygorithm/data_structures/graph.py index e477c66..2b6cbf0 100644 --- a/pygorithm/data_structures/graph.py +++ b/pygorithm/data_structures/graph.py @@ -6,7 +6,6 @@ import inspect import math - class Graph(object): """Graph object Creates the graph @@ -37,16 +36,23 @@ def get_code(self): """ return inspect.getsource(Graph) - class WeightedGraph(object): """WeightedGraph object A graph with a numerical value (weight) on edges """ def __init__(self): - self.edges_weighted = [] self.vertexes = set() - self.forest = None + self.graph = {} + self._forest = None + + def get_weight(self, u, v): + """ + Returns the weight of an edge between vertexes u and v. + If there isnt one: return None. + """ + return self.graph.get((u,v), self.graph.get((v,u), None)) + def add_edge(self, u, v, weight): """ @@ -54,39 +60,41 @@ def add_edge(self, u, v, weight): :param v: to vertex - type : integer :param weight: weight of the edge - type : numeric """ - edge = ((u, v), weight) - self.edges_weighted.append(edge) - self.vertexes.update((u, v)) + if self.get_weight(u, v) != None: + print("Such edge already exists!") + else: + self.vertexes.update((u, v)) + self.graph[(u,v)] = weight def print_graph(self): """ Print the graph :return: None """ - for (u, v), weight in self.edges_weighted: - print("%d -> %d weight: %d" % (u, v, weight)) + for (u, v) in self.graph: + print("%d -> %d weight: %d" % (u, v, self.graph[(u, v)])) - def __set_of(self, vertex): + def _set_of(self, vertex): """ Helper method :param vertex: :return: """ - for tree in self.forest: + for tree in self._forest: if vertex in tree: return tree return None - def __union(self, u_set, v_set): + def _union(self, u_set, v_set): """ Helper method :param u_set: :param v_set: :return: """ - self.forest.remove(u_set) - self.forest.remove(v_set) - self.forest.append(v_set + u_set) + self._forest.remove(u_set) + self._forest.remove(v_set) + self._forest.append(v_set + u_set) def kruskal_mst(self): """ @@ -96,14 +104,14 @@ def kruskal_mst(self): Author: Michele De Vita """ # sort by weight - self.edges_weighted.sort(key=lambda pair: pair[1]) + self.graph = {k: self.graph[k] for k in sorted(self.graph, key=self.graph.get, reverse=False)} edges_explored = [] - self.forest = [[v] for v in self.vertexes] - for (u, v), weight in self.edges_weighted: - u_set, v_set = self.__set_of(u), self.__set_of(v) + self._forest = [[v] for v in self.vertexes] + for (u, v) in self.graph: + u_set, v_set = self._set_of(u), self._set_of(v) if u_set != v_set: - self.__union(u_set, v_set) - edges_explored.append(((u, v), weight)) + self._union(u_set, v_set) + edges_explored.append(((u, v), self.graph[u, v])) return edges_explored # TODO: Is this necessary? From 953c2d48d4cf51861ad55cc07da27d88f73e374d Mon Sep 17 00:00:00 2001 From: Pratik Narola <35926958+Pratiknarola@users.noreply.github.com> Date: Tue, 15 Oct 2019 20:41:05 +0530 Subject: [PATCH 19/34] added few sorting algorithms (#95) * Create brick_sort.py * Create tim_sort.py * Create cocktail_sort.py * Create gnome_sort.py * Update cocktail_sort.py * Update cocktail_sort.py * Update cocktail_sort.py * Update cocktail_sort.py * Update cocktail_sort.py * Update gnome_sort.py * Update tim_sort.py * Update tim_sort.py --- pygorithm/sorting/brick_sort.py | 17 ++++++++ pygorithm/sorting/cocktail_sort.py | 57 ++++++++++++++++++++++++ pygorithm/sorting/gnome_sort.py | 32 ++++++++++++++ pygorithm/sorting/tim_sort.py | 69 ++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 pygorithm/sorting/brick_sort.py create mode 100644 pygorithm/sorting/cocktail_sort.py create mode 100644 pygorithm/sorting/gnome_sort.py create mode 100644 pygorithm/sorting/tim_sort.py diff --git a/pygorithm/sorting/brick_sort.py b/pygorithm/sorting/brick_sort.py new file mode 100644 index 0000000..ae4f879 --- /dev/null +++ b/pygorithm/sorting/brick_sort.py @@ -0,0 +1,17 @@ +def brick_sort(arr, n): + # Initially array is unsorted + isSorted = 0 + while isSorted == 0: + isSorted = 1 + temp = 0 + for i in range(1, n-1, 2): + if arr[i] > arr[i+1]: + arr[i], arr[i+1] = arr[i+1], arr[i] + isSorted = 0 + + for i in range(0, n-1, 2): + if arr[i] > arr[i+1]: + arr[i], arr[i+1] = arr[i+1], arr[i] + isSorted = 0 + + return diff --git a/pygorithm/sorting/cocktail_sort.py b/pygorithm/sorting/cocktail_sort.py new file mode 100644 index 0000000..02f84ec --- /dev/null +++ b/pygorithm/sorting/cocktail_sort.py @@ -0,0 +1,57 @@ +''' +Created by: Pratik Narola (https://github.com/Pratiknarola) +last modified: 14-10-2019 +''' + +''' + +Cocktail Sort is a variation of Bubble sort. +The Bubble sort algorithm always traverses elements from left +and moves the largest element to its correct position in first iteration +and second largest in second iteration and so on. +Cocktail Sort traverses through a given array in both directions alternatively. + +''' + +def cocktail_sort(a): + n = len(a) + swapped = True + start = 0 + end = n-1 + while swapped: + + # reset the swapped flag on entering the loop, + # because it might be true from a previous + # iteration. + swapped = False + + # loop from left to right same as the bubble + # sort + for i in range(start, end): + if a[i] > a[i + 1]: + a[i], a[i + 1] = a[i + 1], a[i] + swapped = True + + # if nothing moved, then array is sorted. + if not swapped: + break + + # otherwise, reset the swapped flag so that it + # can be used in the next stage + swapped = False + + # move the end point back by one, because + # item at the end is in its rightful spot + end -= 1 + + # from right to left, doing the same + # comparison as in the previous stage + for i in range(end-1, start-1, -1): + if a[i] > a[i + 1]: + a[i], a[i + 1] = a[i + 1], a[i] + swapped = True + + # increase the starting point, because + # the last stage would have moved the next + # smallest number to its rightful spot. + start = start + 1 diff --git a/pygorithm/sorting/gnome_sort.py b/pygorithm/sorting/gnome_sort.py new file mode 100644 index 0000000..742257b --- /dev/null +++ b/pygorithm/sorting/gnome_sort.py @@ -0,0 +1,32 @@ +''' +Created by: Pratik Narola (https://github.com/Pratiknarola) +last modified: 14-10-2019 +''' + +''' + +Gnome Sort also called Stupid sort is based on the concept of a Garden Gnome sorting his flower pots. +A garden gnome sorts the flower pots by the following method- + +He looks at the flower pot next to him and the previous one; +if they are in the right order he steps one pot forward, otherwise he swaps them and steps one pot backwards. +If there is no previous pot (he is at the starting of the pot line), he steps forwards; +if there is no pot next to him (he is at the end of the pot line), he is done. + +''' + + + +# A function to sort the given list using Gnome sort +def gnome_sort( arr, n): + index = 0 + while index < n: + if index == 0: + index = index + 1 + if arr[index] >= arr[index - 1]: + index = index + 1 + else: + arr[index], arr[index-1] = arr[index-1], arr[index] + index = index - 1 + + return arr diff --git a/pygorithm/sorting/tim_sort.py b/pygorithm/sorting/tim_sort.py new file mode 100644 index 0000000..a9d5f28 --- /dev/null +++ b/pygorithm/sorting/tim_sort.py @@ -0,0 +1,69 @@ +from insertion_sort import sort +# iterative Timsort function to sort the +# array[0...n-1] (similar to merge sort) +def tim_sort(arr, n): + + # Sort individual subarrays of size RUN + for i in range(0, n, RUN): + sort(arr, i, min((i+31), (n-1))) + + # start merging from size RUN (or 32). It will merge + # to form size 64, then 128, 256 and so on .... + size = RUN + while size < n: + + # pick starting point of left sub array. We + # are going to merge arr[left..left+size-1] + # and arr[left+size, left+2*size-1] + # After every merge, we increase left by 2*size + for left in range(0, n, 2*size): + + # find ending point of left sub array + # mid+1 is starting point of right sub array + mid = left + size - 1 + right = min((left + 2*size - 1), (n-1)) + + # merge sub array arr[left.....mid] & + # arr[mid+1....right] + merge(arr, left, mid, right) + + size = 2*size + +def merge(arr, l, m, r): + + # original array is broken in two parts + # left and right array + len1, len2 = m - l + 1, r - m + left, right = [], [] + for i in range(0, len1): + left.append(arr[l + i]) + for i in range(0, len2): + right.append(arr[m + 1 + i]) + + i, j, k = 0, 0, l + # after comparing, we merge those two array + # in larger sub array + while i < len1 and j < len2: + + if left[i] <= right[j]: + arr[k] = left[i] + i += 1 + + else: + arr[k] = right[j] + j += 1 + + k += 1 + + # copy remaining elements of left, if any + while i < len1: + + arr[k] = left[i] + k += 1 + i += 1 + + # copy remaining element of right, if any + while j < len2: + arr[k] = right[j] + k += 1 + j += 1 From 3f98cc95108f6533a59c4ce0f47d2bc091d74afb Mon Sep 17 00:00:00 2001 From: Timothy Moore Date: Tue, 15 Oct 2019 08:12:07 -0700 Subject: [PATCH 20/34] Improve sorting tests (#96) This verifies that sorting algorithms are either entirely inplace or do not modify the list and return a copy. Furthermore, this standardizes how they handle empty lists. --- pygorithm/sorting/bucket_sort.py | 7 +- pygorithm/sorting/merge_sort.py | 16 +- pygorithm/sorting/quick_sort.py | 2 +- tests/test_sorting.py | 282 ++++++++++++++++++------------- 4 files changed, 173 insertions(+), 134 deletions(-) diff --git a/pygorithm/sorting/bucket_sort.py b/pygorithm/sorting/bucket_sort.py index 4f6eb31..3fb0d05 100644 --- a/pygorithm/sorting/bucket_sort.py +++ b/pygorithm/sorting/bucket_sort.py @@ -14,7 +14,7 @@ def sort(_list, bucket_size=5): """ bucket sort algorithm - + :param _list: list of values to sort :param bucket_size: Size of the bucket :return: sorted values @@ -22,9 +22,8 @@ def sort(_list, bucket_size=5): string = False if len(_list) == 0: - # print("You don\'t have any elements in array!") - raise ValueError("Array can not be empty.") - + return [] + elif all(isinstance(element, str) for element in _list): string = True _list = [ord(element) for element in _list] diff --git a/pygorithm/sorting/merge_sort.py b/pygorithm/sorting/merge_sort.py index ccd79d9..be1fc01 100644 --- a/pygorithm/sorting/merge_sort.py +++ b/pygorithm/sorting/merge_sort.py @@ -3,16 +3,16 @@ Created On: 31st July 2017 - Best = Average = Worst = O(n log(n)) - + """ import inspect def merge(a, b): """ - Function to merge + Function to merge two arrays / separated lists - + :param a: Array 1 :param b: Array 2 :return: merged arrays @@ -34,14 +34,14 @@ def merge(a, b): def sort(_list): """ - Function to sort an array - using merge sort algorithm - + Function to sort an array + using merge sort algorithm + :param _list: list of values to sort :return: sorted """ if len(_list) == 0 or len(_list) == 1: - return _list + return list(_list) else: middle = len(_list)//2 a = sort(_list[:middle]) @@ -61,7 +61,7 @@ def time_complexities(): def get_code(): """ - easily retrieve the source code + easily retrieve the source code of the sort function :return: source code diff --git a/pygorithm/sorting/quick_sort.py b/pygorithm/sorting/quick_sort.py index f3b65df..53d62cf 100644 --- a/pygorithm/sorting/quick_sort.py +++ b/pygorithm/sorting/quick_sort.py @@ -16,7 +16,7 @@ def sort(_list): :return: sorted list """ if len(_list) <= 1: - return _list + return list(_list) pivot = _list[len(_list) // 2] left = [x for x in _list if x < pivot] middle = [x for x in _list if x == pivot] diff --git a/tests/test_sorting.py b/tests/test_sorting.py index 89c6157..e4fe3ba 100644 --- a/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -1,121 +1,161 @@ -import unittest -import random - -from pygorithm.sorting import ( - bubble_sort, - insertion_sort, - selection_sort, - merge_sort, - quick_sort, - counting_sort, - bucket_sort, - shell_sort, - heap_sort, - radix_sort) - - -class TestSortingAlgorithm(unittest.TestCase): - def setUp(self): - # to test numeric numbers - self.array = list(range(15)) - random.shuffle(self.array) - self.sorted_array = list(range(15)) - - # to test alphabets - string = 'pythonisawesome' - self.alphaArray = list(string) - random.shuffle(self.alphaArray) - self.sorted_alpha_array = sorted(string) - - -class TestBubbleSort(TestSortingAlgorithm): - def test_bubble_sort(self): - self.result = bubble_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - self.alphaResult = bubble_sort.sort(self.alphaArray) - self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -class TestInsertionSort(TestSortingAlgorithm): - def test_insertion_sort(self): - self.result = insertion_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - self.alphaResult = insertion_sort.sort(self.alphaArray) - self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -class TestSelectionSort(TestSortingAlgorithm): - def test_selection_sort(self): - self.result = selection_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - self.alphaResult = selection_sort.sort(self.alphaArray) - self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -class TestMergeSort(TestSortingAlgorithm): - def test_merge_sort(self): - self.result = merge_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - self.alphaResult = merge_sort.sort(self.alphaArray) - self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -class TestQuickSort(TestSortingAlgorithm): - def test_quick_sort(self): - self.result = quick_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - self.alphaResult = quick_sort.sort(self.alphaArray) - self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -class TestCountingSort(TestSortingAlgorithm): - def test_counting_sort(self): - # counting sort is an integer based sort - self.result = counting_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - -class TestBucketSort(TestSortingAlgorithm): - def test_bucket_sort(self): - self.result = bucket_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - self.alphaResult = bucket_sort.sort(self.alphaArray) - self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -class TestShellSort(TestSortingAlgorithm): - def test_shell_sort(self): - self.result = shell_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - self.alphaResult = shell_sort.sort(self.alphaArray) - self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -class TestHeapSort(TestSortingAlgorithm): - def test_heap_sort(self): - self.result = heap_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - self.alphaResult = heap_sort.sort(self.alphaArray) - self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -class TestRadixSort(TestSortingAlgorithm): - def test_radix_sort(self): - self.result = radix_sort.sort(self.array) - self.assertEqual(self.result, self.sorted_array) - - # TODO: Fix radix sort to sort alphabetically - # self.alphaResult = radix_sort.sort(self.alphaArray) - # self.assertEqual(self.alphaResult, self.sorted_alpha_array) - - -if __name__ == '__main__': - unittest.main() +import unittest +import random + +from pygorithm.sorting import ( + bubble_sort, + insertion_sort, + selection_sort, + merge_sort, + quick_sort, + counting_sort, + bucket_sort, + shell_sort, + heap_sort) + + +class TestSortingAlgorithm: + def test_test_setup(self): + self.assertIsNotNone(getattr(self, 'sort', None)) + self.assertIsNotNone(getattr(self, 'inplace', None)) + self.assertIsNotNone(getattr(self, 'alph_support', None)) + + def _check_sort_list(self, arr, expected): + cp_arr = list(arr) + sarr = self.sort(cp_arr) + + self.assertTrue( + isinstance(sarr, list), 'weird result type: ' + str(type(sarr))) + self.assertEqual(len(sarr), len(arr)) + self.assertEqual(sarr, expected) + if self.inplace: + self.assertTrue(cp_arr is sarr, 'was not inplace') + else: + self.assertTrue(cp_arr is not sarr, 'was inplace') + self.assertEqual(cp_arr, arr, 'inplace modified list') + + def _check_sort_alph(self, inp, expected): + if not self.alph_support: + return + + self._check_sort_list(list(inp), list(expected)) + + def test_sort_empty(self): + self._check_sort_list([], []) + + def test_sort_single(self): + self._check_sort_list([5], [5]) + + def test_sort_single_alph(self): + self._check_sort_alph('a', 'a') + + def test_sort_two_inorder(self): + self._check_sort_list([1, 2], [1, 2]) + + def test_sort_two_outoforder(self): + self._check_sort_list([2, 1], [1, 2]) + + def test_sort_5_random_numeric(self): + arr = list(range(5)) + random.shuffle(arr) + self._check_sort_list(arr, list(range(5))) + + def test_sort_15_random_numeric(self): + arr = list(range(15)) + random.shuffle(arr) + self._check_sort_list(arr, list(range(15))) + + def test_sort_5_random_alph(self): + arr = ['a', 'b', 'c', 'd', 'e'] + random.shuffle(arr) + self._check_sort_alph(''.join(arr), 'abcde') + + def test_sort_15_random_alph(self): + arr = [chr(ord('a') + i) for i in range(15)] + exp = ''.join(arr) + random.shuffle(arr) + self._check_sort_alph(''.join(arr), exp) + + +class TestBubbleSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + return bubble_sort.sort(arr) + + +class TestInsertionSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + return insertion_sort.sort(arr) + + +class TestSelectionSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + return selection_sort.sort(arr) + + +class TestMergeSort(unittest.TestCase, TestSortingAlgorithm): + inplace = False + alph_support = True + + @staticmethod + def sort(arr): + return merge_sort.sort(arr) + + +class TestQuickSort(unittest.TestCase, TestSortingAlgorithm): + inplace = False + alph_support = True + + @staticmethod + def sort(arr): + return quick_sort.sort(arr) + + +class TestCountingSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = False + + @staticmethod + def sort(arr): + return counting_sort.sort(arr) + + +class TestBucketSort(unittest.TestCase, TestSortingAlgorithm): + inplace = False + alph_support = True + + @staticmethod + def sort(arr): + return bucket_sort.sort(arr) + + +class TestShellSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + return shell_sort.sort(arr) + + +class TestHeapSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + return heap_sort.sort(arr) + + +if __name__ == '__main__': + unittest.main() From 5cc90d161cabf2d83c3d1b1717ad8616fbcf22bf Mon Sep 17 00:00:00 2001 From: Timothy Moore Date: Tue, 15 Oct 2019 18:27:16 -0700 Subject: [PATCH 21/34] Add tests and improve documentation on new sorting algorithms (#97) This effectively standardizes the function arguments for all the new sorting algorithms to be consistent with the other ones. Also fixes a few bugs and performs some style improvements, such as moving documentation of functions to after the function definition instead of before, and adding documentation where it is missing. --- pygorithm/sorting/brick_sort.py | 42 ++++--- pygorithm/sorting/cocktail_sort.py | 83 +++++++------- pygorithm/sorting/gnome_sort.py | 36 +++--- pygorithm/sorting/tim_sort.py | 177 ++++++++++++++++++----------- tests/test_sorting.py | 43 ++++++- 5 files changed, 238 insertions(+), 143 deletions(-) diff --git a/pygorithm/sorting/brick_sort.py b/pygorithm/sorting/brick_sort.py index ae4f879..333f05e 100644 --- a/pygorithm/sorting/brick_sort.py +++ b/pygorithm/sorting/brick_sort.py @@ -1,17 +1,25 @@ -def brick_sort(arr, n): - # Initially array is unsorted - isSorted = 0 - while isSorted == 0: - isSorted = 1 - temp = 0 - for i in range(1, n-1, 2): - if arr[i] > arr[i+1]: - arr[i], arr[i+1] = arr[i+1], arr[i] - isSorted = 0 - - for i in range(0, n-1, 2): - if arr[i] > arr[i+1]: - arr[i], arr[i+1] = arr[i+1], arr[i] - isSorted = 0 - - return +def brick_sort(arr): + """Performs an odd-even in-place sort, which is a variation of a bubble + sort. + + https://www.geeksforgeeks.org/odd-even-sort-brick-sort/ + + :param arr: the array of values to sort + :return: the sorted array + """ + # Initially array is unsorted + is_sorted = False + while not is_sorted: + is_sorted = True + + for i in range(1, len(arr) - 1, 2): + if arr[i] > arr[i + 1]: + arr[i], arr[i + 1] = arr[i + 1], arr[i] + is_sorted = False + + for i in range(0, len(arr) - 1, 2): + if arr[i] > arr[i + 1]: + arr[i], arr[i + 1] = arr[i + 1], arr[i] + is_sorted = False + + return arr diff --git a/pygorithm/sorting/cocktail_sort.py b/pygorithm/sorting/cocktail_sort.py index 02f84ec..f9f6ecb 100644 --- a/pygorithm/sorting/cocktail_sort.py +++ b/pygorithm/sorting/cocktail_sort.py @@ -3,55 +3,58 @@ last modified: 14-10-2019 ''' -''' -Cocktail Sort is a variation of Bubble sort. -The Bubble sort algorithm always traverses elements from left -and moves the largest element to its correct position in first iteration -and second largest in second iteration and so on. -Cocktail Sort traverses through a given array in both directions alternatively. +def cocktail_sort(arr): + ''' + Cocktail Sort is a variation of Bubble sort. + The Bubble sort algorithm always traverses elements from left + and moves the largest element to its correct position in first iteration + and second largest in second iteration and so on. + Cocktail Sort traverses through a given array in both directions alternatively. -''' + This is an in-place sort. -def cocktail_sort(a): - n = len(a) + :param arr: the array to sort + :return: the sorted array, which is the same reference as arr + ''' swapped = True start = 0 - end = n-1 - while swapped: - - # reset the swapped flag on entering the loop, - # because it might be true from a previous - # iteration. + end = len(arr) - 1 + while swapped: + # reset the swapped flag on entering the loop, + # because it might be true from a previous + # iteration. swapped = False - - # loop from left to right same as the bubble - # sort - for i in range(start, end): - if a[i] > a[i + 1]: - a[i], a[i + 1] = a[i + 1], a[i] + + # loop from left to right same as the bubble + # sort + for i in range(start, end): + if arr[i] > arr[i + 1]: + arr[i], arr[i + 1] = arr[i + 1], arr[i] swapped = True - - # if nothing moved, then array is sorted. - if not swapped: + + # if nothing moved, then array is sorted. + if not swapped: break - - # otherwise, reset the swapped flag so that it - # can be used in the next stage + + # otherwise, reset the swapped flag so that it + # can be used in the next stage swapped = False - - # move the end point back by one, because - # item at the end is in its rightful spot + + # move the end point back by one, because + # item at the end is in its rightful spot end -= 1 - - # from right to left, doing the same - # comparison as in the previous stage - for i in range(end-1, start-1, -1): - if a[i] > a[i + 1]: - a[i], a[i + 1] = a[i + 1], a[i] + + # from right to left, doing the same + # comparison as in the previous stage + for i in range(end - 1, start - 1, -1): + if arr[i] > arr[i + 1]: + arr[i], arr[i + 1] = arr[i + 1], arr[i] swapped = True - - # increase the starting point, because - # the last stage would have moved the next - # smallest number to its rightful spot. + + # increase the starting point, because + # the last stage would have moved the next + # smallest number to its rightful spot. start = start + 1 + + return arr diff --git a/pygorithm/sorting/gnome_sort.py b/pygorithm/sorting/gnome_sort.py index 742257b..ecdfee5 100644 --- a/pygorithm/sorting/gnome_sort.py +++ b/pygorithm/sorting/gnome_sort.py @@ -3,30 +3,34 @@ last modified: 14-10-2019 ''' -''' -Gnome Sort also called Stupid sort is based on the concept of a Garden Gnome sorting his flower pots. -A garden gnome sorts the flower pots by the following method- -He looks at the flower pot next to him and the previous one; -if they are in the right order he steps one pot forward, otherwise he swaps them and steps one pot backwards. -If there is no previous pot (he is at the starting of the pot line), he steps forwards; -if there is no pot next to him (he is at the end of the pot line), he is done. -''' +# A function to sort the given list using Gnome sort +def gnome_sort(arr): + ''' + Gnome Sort also called Stupid sort is based on the concept of a Garden Gnome sorting his flower pots. + A garden gnome sorts the flower pots by the following method- + He looks at the flower pot next to him and the previous one; + if they are in the right order he steps one pot forward, otherwise he swaps them and steps one pot backwards. + If there is no previous pot (he is at the starting of the pot line), he steps forwards; + if there is no pot next to him (he is at the end of the pot line), he is done. -# A function to sort the given list using Gnome sort -def gnome_sort( arr, n): + This is an in-place sort. + + :param arr: the array of values to sort + :return: the sorted array, which is the same reference as arr + ''' index = 0 - while index < n: - if index == 0: + while index < len(arr): + if index == 0: index = index + 1 - if arr[index] >= arr[index - 1]: + elif arr[index] >= arr[index - 1]: index = index + 1 - else: - arr[index], arr[index-1] = arr[index-1], arr[index] + else: + arr[index], arr[index - 1] = arr[index - 1], arr[index] index = index - 1 - + return arr diff --git a/pygorithm/sorting/tim_sort.py b/pygorithm/sorting/tim_sort.py index a9d5f28..95d0a3c 100644 --- a/pygorithm/sorting/tim_sort.py +++ b/pygorithm/sorting/tim_sort.py @@ -1,69 +1,108 @@ -from insertion_sort import sort -# iterative Timsort function to sort the -# array[0...n-1] (similar to merge sort) -def tim_sort(arr, n): - - # Sort individual subarrays of size RUN - for i in range(0, n, RUN): - sort(arr, i, min((i+31), (n-1))) - - # start merging from size RUN (or 32). It will merge - # to form size 64, then 128, 256 and so on .... - size = RUN - while size < n: - - # pick starting point of left sub array. We - # are going to merge arr[left..left+size-1] - # and arr[left+size, left+2*size-1] - # After every merge, we increase left by 2*size - for left in range(0, n, 2*size): - - # find ending point of left sub array - # mid+1 is starting point of right sub array - mid = left + size - 1 - right = min((left + 2*size - 1), (n-1)) - - # merge sub array arr[left.....mid] & - # arr[mid+1....right] - merge(arr, left, mid, right) - - size = 2*size - -def merge(arr, l, m, r): - - # original array is broken in two parts - # left and right array - len1, len2 = m - l + 1, r - m - left, right = [], [] - for i in range(0, len1): - left.append(arr[l + i]) - for i in range(0, len2): - right.append(arr[m + 1 + i]) - - i, j, k = 0, 0, l - # after comparing, we merge those two array - # in larger sub array - while i < len1 and j < len2: - - if left[i] <= right[j]: - arr[k] = left[i] - i += 1 - - else: - arr[k] = right[j] - j += 1 - - k += 1 - - # copy remaining elements of left, if any - while i < len1: - - arr[k] = left[i] - k += 1 - i += 1 - - # copy remaining element of right, if any - while j < len2: - arr[k] = right[j] - k += 1 - j += 1 +def inplace_insertion_sort(arr, start_ind, end_ind): + """ + Performs an in-place insertion sort over a continuous slice of an + array. A natural way to avoid this would be to use numpy arrays, + where slicing does not copy. + + This is in-place and has no result. + + :param arr: the array to sort + :param start_ind: the index to begin sorting at + :param end_ind: the index to end sorting at. This index is excluded + from the sort (i.e., len(arr) is ok) + """ + for i in range(start_ind + 1, end_ind): + current_number = arr[i] + + for j in range(i - 1, start_ind - 1, -1): + if arr[j] > current_number: + arr[j], arr[j + 1] = arr[j + 1], arr[j] + else: + arr[j + 1] = current_number + break + + +# iterative Timsort function to sort the +# array[0...n-1] (similar to merge sort) +def tim_sort(arr, run=32): + """ + Tim sort algorithm. See https://en.wikipedia.org/wiki/Timsort. + This is performed in-place. + + :param arr: list of values to sort + :param run: the largest array that is sorted with an insertion sort. + :return: the sorted array + """ + + # Sort individual subarrays of size run + + for i in range(0, len(arr), run): + inplace_insertion_sort(arr, i, min(i + run, len(arr))) + + # start merging from size RUN (or 32). It will merge + # to form size 64, then 128, 256 and so on .... + size = run + while size < len(arr): + # pick starting point of left sub array. We + # are going to merge arr[left..left+size-1] + # and arr[left+size, left+2*size-1] + # After every merge, we increase left by 2*size + for left in range(0, len(arr), 2 * size): + # find ending point of left sub array + # mid+1 is starting point of right sub array + mid = left + size + right = min(left + (2 * size), len(arr)) + + # merge sub array arr[left.....mid] & + # arr[mid+1....right] + merge(arr, left, mid, right) + + size = 2 * size + return arr + +def merge(arr, left, mid, right): + """ + Merge of two sections of array, both of which are individually + sorted. The result is that the entire chunk is sorted. Note that right + edges are exclusive (like slicing). + + This modifies the passed array, but requires a complete copy of the array. + + .. code:: python + + merge([0, -1, 1, 3, 2, 4], 2, 4, 6) # [0, -1, 1, 2, 3, 4] + + :param arr: the array which should have a portion sorted in-place + :param left: the left-most index which is included in the merge + :param mid: the first index that belongs to the second section + :param right: the right-edge in the merge, which is not included in the sort. + """ + # original array is broken in two parts + # left and right array + left_arr = arr[left:mid] + right_arr = arr[mid:right] + + left_pos = 0 + right_pos = 0 + arr_ind = left + # after comparing, we merge those two array + # in larger sub array + while left_pos < len(left_arr) and right_pos < len(right_arr): + if left_arr[left_pos] <= right_arr[right_pos]: + arr[arr_ind] = left_arr[left_pos] + left_pos += 1 + else: + arr[arr_ind] = right_arr[right_pos] + right_pos += 1 + + arr_ind += 1 + + # copy remaining elements of left, if any + for i in range(left_pos, len(left_arr)): + arr[arr_ind] = left_arr[i] + arr_ind += 1 + + # copy remaining element of right, if any + for i in range(right_pos, len(right_arr)): + arr[arr_ind] = right_arr[i] + arr_ind += 1 diff --git a/tests/test_sorting.py b/tests/test_sorting.py index e4fe3ba..f7fcf6d 100644 --- a/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -10,7 +10,12 @@ counting_sort, bucket_sort, shell_sort, - heap_sort) + heap_sort, + brick_sort, + tim_sort, + cocktail_sort, + gnome_sort +) class TestSortingAlgorithm: @@ -157,5 +162,41 @@ def sort(arr): return heap_sort.sort(arr) +class TestBrickSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + return brick_sort.brick_sort(arr) + + +class TestTimSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + # use a smaller run for testing + return tim_sort.tim_sort(arr, run=4) + + +class TestCocktailSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + return cocktail_sort.cocktail_sort(arr) + + +class TestGnomeSort(unittest.TestCase, TestSortingAlgorithm): + inplace = True + alph_support = True + + @staticmethod + def sort(arr): + return gnome_sort.gnome_sort(arr) + if __name__ == '__main__': unittest.main() From 4e41b3fc248e07a00a011dcca649bac24767d682 Mon Sep 17 00:00:00 2001 From: agrdivyam <56672552+agrdivyam@users.noreply.github.com> Date: Thu, 17 Oct 2019 13:30:45 +0530 Subject: [PATCH 22/34] Create GCD.py --- pygorithm/math/GCD.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 pygorithm/math/GCD.py diff --git a/pygorithm/math/GCD.py b/pygorithm/math/GCD.py new file mode 100644 index 0000000..424f2a1 --- /dev/null +++ b/pygorithm/math/GCD.py @@ -0,0 +1,18 @@ +def find_gcd(x, y): + + while(y): + x, y = y, x % y + + return x + + +l = [2, 4, 6, 8, 16] + +num1 = l[0] +num2 = l[1] +gcd = find_gcd(num1, num2) + +for i in range(2, len(l)): + gcd = find_gcd(gcd, l[i]) + +print(gcd) From 03be15c5c9c072baf830bf3da27256a3daca9549 Mon Sep 17 00:00:00 2001 From: Omkar Pathak Date: Mon, 13 Apr 2020 10:14:03 +0530 Subject: [PATCH 23/34] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 1528dd4..7823a9a 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ Pygorithm :alt: Python 3.6 .. image:: https://img.shields.io/badge/Say%20Thanks-%F0%9F%A6%89-1EAEDB.svg - :target: https://saythanks.io/to/OmkarPathak + :target: https://saythanks.io/to/omkarpathak27@gmail.com :alt: Say Thanks! .. image:: https://img.shields.io/github/contributors/omkarpathak/pygorithm.svg From 36821a1531a5c03df214e0955b2297e288f7c410 Mon Sep 17 00:00:00 2001 From: Omkar Pathak Date: Sun, 21 Jun 2020 18:24:59 +0530 Subject: [PATCH 24/34] Update README.rst --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 7823a9a..023f2f0 100644 --- a/README.rst +++ b/README.rst @@ -30,6 +30,10 @@ Pygorithm | A Python module to learn all the major algorithms on the go! | Purely for educational purposes + +.. image:: https://i.imgur.com/6HOIlCX.png + :target: https://tracking.gitads.io/?campaign=gitads&repo=pygorithm&redirect=gitads.io. + Features ~~~~~~~~ From cf932ce8f527ff449829c1e14b2d812303741544 Mon Sep 17 00:00:00 2001 From: Omkar Pathak Date: Sun, 21 Jun 2020 18:25:36 +0530 Subject: [PATCH 25/34] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 023f2f0..2800f8d 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ Pygorithm .. image:: https://i.imgur.com/6HOIlCX.png - :target: https://tracking.gitads.io/?campaign=gitads&repo=pygorithm&redirect=gitads.io. + :target: https://tracking.gitads.io/?campaign=gitads&repo=pygorithm&redirect=gitads.io Features ~~~~~~~~ From 07b5496897360ed90320628f6d38609f8a60b99f Mon Sep 17 00:00:00 2001 From: Omkar Pathak Date: Tue, 23 Jun 2020 10:41:40 +0530 Subject: [PATCH 26/34] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 2800f8d..1827a86 100644 --- a/README.rst +++ b/README.rst @@ -31,7 +31,7 @@ Pygorithm | Purely for educational purposes -.. image:: https://i.imgur.com/6HOIlCX.png +.. image:: https://images.gitads.io/pygorithm :target: https://tracking.gitads.io/?campaign=gitads&repo=pygorithm&redirect=gitads.io Features From cea6a35fb0012c404720f66d24c4e84259f52d11 Mon Sep 17 00:00:00 2001 From: Ashok Bakthavathsalam Date: Thu, 27 Aug 2020 12:36:11 +0000 Subject: [PATCH 27/34] verbose merge sort init --- pygorithm/sorting/merge_sort.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pygorithm/sorting/merge_sort.py b/pygorithm/sorting/merge_sort.py index be1fc01..a410e49 100644 --- a/pygorithm/sorting/merge_sort.py +++ b/pygorithm/sorting/merge_sort.py @@ -48,6 +48,8 @@ def sort(_list): b = sort(_list[middle:]) return merge(a, b) +def sort_iter(_list): + pass # TODO: Are these necessary? def time_complexities(): From 5c0973e84dbe8096dcf4f5eee78bca8c2fa0da57 Mon Sep 17 00:00:00 2001 From: Ashok Bakthavathsalam Date: Thu, 27 Aug 2020 12:37:46 +0000 Subject: [PATCH 28/34] docstring added --- pygorithm/sorting/merge_sort.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pygorithm/sorting/merge_sort.py b/pygorithm/sorting/merge_sort.py index a410e49..7160c35 100644 --- a/pygorithm/sorting/merge_sort.py +++ b/pygorithm/sorting/merge_sort.py @@ -49,7 +49,14 @@ def sort(_list): return merge(a, b) def sort_iter(_list): - pass + """ + Function to sort an array + using merge sort algorithm, iteratively + + :param _list: list of values to sort + :return: sorted + """ + pass # TODO: Are these necessary? def time_complexities(): From 63b706e31e1c1abcbb792501904514c9e79b2aa4 Mon Sep 17 00:00:00 2001 From: Ashok Bakthavathsalam Date: Thu, 27 Aug 2020 13:45:09 +0000 Subject: [PATCH 29/34] added iterative code --- pygorithm/sorting/merge_sort.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pygorithm/sorting/merge_sort.py b/pygorithm/sorting/merge_sort.py index 7160c35..50ff6ef 100644 --- a/pygorithm/sorting/merge_sort.py +++ b/pygorithm/sorting/merge_sort.py @@ -48,7 +48,8 @@ def sort(_list): b = sort(_list[middle:]) return merge(a, b) -def sort_iter(_list): +from itertools import zip_longest +def sorti(_list): """ Function to sort an array using merge sort algorithm, iteratively @@ -56,7 +57,16 @@ def sort_iter(_list): :param _list: list of values to sort :return: sorted """ - pass + # breakdown every element into its own list + series = [[i] for i in _list] + while len(series) > 1: + # iterator to handle two at a time in the zip_longest below + isl = iter(series) + series = [ + merge(a, b) if b else a + for a, b in zip_longest(isl, isl) + ] + return series[0] # TODO: Are these necessary? def time_complexities(): From d70c319ffee40998049712aaa9d26f3580ac16b5 Mon Sep 17 00:00:00 2001 From: Ashok Bakthavathsalam Date: Thu, 27 Aug 2020 16:33:13 +0000 Subject: [PATCH 30/34] added test for iterative mergesort --- pygorithm/sorting/merge_sort.py | 1 + tests/test_sorting.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/pygorithm/sorting/merge_sort.py b/pygorithm/sorting/merge_sort.py index 50ff6ef..8e50e27 100644 --- a/pygorithm/sorting/merge_sort.py +++ b/pygorithm/sorting/merge_sort.py @@ -60,6 +60,7 @@ def sorti(_list): # breakdown every element into its own list series = [[i] for i in _list] while len(series) > 1: + print(series) # iterator to handle two at a time in the zip_longest below isl = iter(series) series = [ diff --git a/tests/test_sorting.py b/tests/test_sorting.py index f7fcf6d..e7acb99 100644 --- a/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -116,6 +116,13 @@ class TestMergeSort(unittest.TestCase, TestSortingAlgorithm): def sort(arr): return merge_sort.sort(arr) +class TestMergeSortIterative(unittest.TestCase, TestSortingAlgorithm): + inplace = False + alph_support = True + + @staticmethod + def sort(arr): + return merge_sort.sorti(arr) class TestQuickSort(unittest.TestCase, TestSortingAlgorithm): inplace = False From 395c2cd29c3817fd6f16b35aa64575b8301cd3c3 Mon Sep 17 00:00:00 2001 From: Ashok Bakthavathsalam Date: Thu, 27 Aug 2020 16:39:13 +0000 Subject: [PATCH 31/34] added verbose option --- pygorithm/sorting/merge_sort.py | 4 ++-- tests/test_sorting.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pygorithm/sorting/merge_sort.py b/pygorithm/sorting/merge_sort.py index 8e50e27..b41fcf4 100644 --- a/pygorithm/sorting/merge_sort.py +++ b/pygorithm/sorting/merge_sort.py @@ -49,7 +49,7 @@ def sort(_list): return merge(a, b) from itertools import zip_longest -def sorti(_list): +def sorti(_list, verbose=True): """ Function to sort an array using merge sort algorithm, iteratively @@ -60,7 +60,7 @@ def sorti(_list): # breakdown every element into its own list series = [[i] for i in _list] while len(series) > 1: - print(series) + if verbose: print(series) # iterator to handle two at a time in the zip_longest below isl = iter(series) series = [ diff --git a/tests/test_sorting.py b/tests/test_sorting.py index e7acb99..c9c650c 100644 --- a/tests/test_sorting.py +++ b/tests/test_sorting.py @@ -122,7 +122,7 @@ class TestMergeSortIterative(unittest.TestCase, TestSortingAlgorithm): @staticmethod def sort(arr): - return merge_sort.sorti(arr) + return merge_sort.sorti(arr, verbose=False) class TestQuickSort(unittest.TestCase, TestSortingAlgorithm): inplace = False From 88d7ca32e5fdb3fae3e7e591cb0d16ede0eadf2b Mon Sep 17 00:00:00 2001 From: Ashok Bakthavathsalam Date: Thu, 27 Aug 2020 16:54:23 +0000 Subject: [PATCH 32/34] get_code has option to get iterative code --- pygorithm/sorting/merge_sort.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pygorithm/sorting/merge_sort.py b/pygorithm/sorting/merge_sort.py index b41fcf4..df35117 100644 --- a/pygorithm/sorting/merge_sort.py +++ b/pygorithm/sorting/merge_sort.py @@ -79,11 +79,14 @@ def time_complexities(): return "Best Case: O(nlogn), Average Case: O(nlogn), Worst Case: O(nlogn)" -def get_code(): +def get_code(iter=False): """ easily retrieve the source code of the sort function :return: source code """ + if iter: + return inspect.getsource(sorti) + "\n" + inspect.getsource(merge) + return inspect.getsource(sort) + "\n" + inspect.getsource(merge) From e0b5ae368b51d4514082064041f1dd94249e7f3f Mon Sep 17 00:00:00 2001 From: Ashok Bakthavathsalam Date: Thu, 27 Aug 2020 16:57:12 +0000 Subject: [PATCH 33/34] get_code has option to get iterative code --- pygorithm/sorting/merge_sort.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pygorithm/sorting/merge_sort.py b/pygorithm/sorting/merge_sort.py index b41fcf4..f4c21a3 100644 --- a/pygorithm/sorting/merge_sort.py +++ b/pygorithm/sorting/merge_sort.py @@ -79,11 +79,14 @@ def time_complexities(): return "Best Case: O(nlogn), Average Case: O(nlogn), Worst Case: O(nlogn)" -def get_code(): +def get_code(iter=False): """ easily retrieve the source code of the sort function :return: source code """ + if iter: + return inspect.getsource(sorti) + "\n" + return inspect.getsource(sort) + "\n" + inspect.getsource(merge) From be35813729a0151da1ac9ba013453a61ffa249b8 Mon Sep 17 00:00:00 2001 From: Anubhav Sharma Date: Tue, 6 Oct 2020 09:03:37 +0530 Subject: [PATCH 34/34] Adding Two Algorithms (#102) * Fractional Knapsack Algorithm * Longest Palindrome Substring Algorithm --- .../fractional_knapsack.py | 67 +++++++++++++++++ .../longest_palindrome_substring.py | 75 +++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 pygorithm/dynamic_programming/fractional_knapsack.py create mode 100644 pygorithm/dynamic_programming/longest_palindrome_substring.py diff --git a/pygorithm/dynamic_programming/fractional_knapsack.py b/pygorithm/dynamic_programming/fractional_knapsack.py new file mode 100644 index 0000000..83e88f5 --- /dev/null +++ b/pygorithm/dynamic_programming/fractional_knapsack.py @@ -0,0 +1,67 @@ +# https://en.wikipedia.org/wiki/Continuous_knapsack_problem +# https://www.guru99.com/fractional-knapsack-problem-greedy.html +# https://medium.com/walkinthecode/greedy-algorithm-fractional-knapsack-problem-9aba1daecc93 + +""" +Author : Anubhav Sharma +This is a pure Python implementation of Dynamic Programming solution to the Fractional Knapsack of a given items and weights. +The problem is : +Given N items and weights, to find the max weight of item to put in fractional knapsack in that given knapsack and +return it. +Example: Weight of knapsack to carry, Items and there weights as input will return + Items in fraction to put in the knapsack as per weight as output +""" + +def fractional_knapsack(value: list[int], weight: list[int], capacity: int) -> tuple[int, list[int]]: + """ + >>> value = [1, 3, 5, 7, 9] + >>> weight = [0.9, 0.7, 0.5, 0.3, 0.1] + >>> fractional_knapsack(value, weight, 5) + (25, [1, 1, 1, 1, 1]) + >>> fractional_knapsack(value, weight, 15) + (25, [1, 1, 1, 1, 1]) + >>> fractional_knapsack(value, weight, 25) + (25, [1, 1, 1, 1, 1]) + >>> fractional_knapsack(value, weight, 26) + (25, [1, 1, 1, 1, 1]) + >>> fractional_knapsack(value, weight, -1) + (-90.0, [0, 0, 0, 0, -10.0]) + >>> fractional_knapsack([1, 3, 5, 7], weight, 30) + (16, [1, 1, 1, 1]) + >>> fractional_knapsack(value, [0.9, 0.7, 0.5, 0.3, 0.1], 30) + (25, [1, 1, 1, 1, 1]) + >>> fractional_knapsack([], [], 30) + (0, []) + """ + index = list(range(len(value))) + ratio = [v / w for v, w in zip(value, weight)] + index.sort(key=lambda i: ratio[i], reverse=True) + + max_value = 0 + fractions = [0] * len(value) + for i in index: + if weight[i] <= capacity: + fractions[i] = 1 + max_value += value[i] + capacity -= weight[i] + else: + fractions[i] = capacity / weight[i] + max_value += value[i] * capacity / weight[i] + break + + return max_value, fractions + + +if __name__ == "__main__": + n = int(input("Enter number of items: ")) + value = input(f"Enter the values of the {n} item(s) in order: ").split() + value = [int(v) for v in value] + weight = input(f"Enter the positive weights of the {n} item(s) in order: ".split()) + weight = [int(w) for w in weight] + capacity = int(input("Enter maximum weight: ")) + + max_value, fractions = fractional_knapsack(value, weight, capacity) + print("The maximum value of items that can be carried:", max_value) + print("The fractions in which the items should be taken:", fractions) + + \ No newline at end of file diff --git a/pygorithm/dynamic_programming/longest_palindrome_substring.py b/pygorithm/dynamic_programming/longest_palindrome_substring.py new file mode 100644 index 0000000..4329364 --- /dev/null +++ b/pygorithm/dynamic_programming/longest_palindrome_substring.py @@ -0,0 +1,75 @@ +""" +Author : Anubhav Sharma +This is a pure Python implementation of Dynamic Programming solution to the longest +palindrome substring of a given string. +I use Manacher Algorithm which is amazing algorithm and find solution in linear time complexity. +The problem is : +Given a string, to find the longest palindrome sub-string in that given string and +return it. +Example: aabbabbaababa as input will return + aabbabbaa as output +""" +def manacher_algo_lps(s,n): + """ + PARAMETER + -------------- + s = string + n = string_len (int) + manacher Algorithm is the fastest technique to find the longest palindrome substring in any given string. + RETURN + --------------- + Longest Palindrome String(String) + """ + # variables to use + p = [0] * n + c = 0 + r = 0 + maxlen = 0 + + # Main Algorithm + for i in range(n): + mirror = 2*c-i # Finding the Mirror(i.e. Pivort to break) of the string + if i < r: + p[i] = (r - i) if (r - i) < p[mirror] else p[mirror] + a = i + (1 + p[i]) + b = i - (1 + p[i]) + + # Attempt to expand palindrome centered at currentRightPosition i + # Here for odd positions, we compare characters and + # if match then increment LPS Length by ONE + # If even position, we just increment LPS by ONE without + # any character comparison + while a=0 and s[a] == s[b]: + p[i] += 1 + a += 1 + b -= 1 + if (i + p[i]) > r: + c = i + r = i + p[i] + if p[i] > maxlen: # Track maxLPSLength + maxlen = p[i] + i = p.index(maxlen) + return s[i-maxlen:maxlen+i][1::2] + +def longest_palindrome(s: str) -> str: + s = '#'.join(s) + s = '#'+s+'#' + + # Calling Manacher Algorithm + return manacher_algo_lps(s,len(s)) + +def main(): + + # Input to enter + input_string = "abbbacdcaacdca" + + # Calling the longest palindrome algorithm + s = longest_palindrome(input_string) + print("LPS Using Manacher Algorithm {}".format(s)) + +# Calling Main Function +if __name__ == "__main__": + + main() + + \ No newline at end of file