From 9972e7893931463e19293c70ed24c76546c0a974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jill-J=C3=AAnn=20Vie?= Date: Tue, 6 Jan 2015 14:59:51 +0100 Subject: [PATCH 01/62] Rename kandane.py to kadane.py Meow. --- misc/{kandane.py => kadane.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename misc/{kandane.py => kadane.py} (100%) diff --git a/misc/kandane.py b/misc/kadane.py similarity index 100% rename from misc/kandane.py rename to misc/kadane.py From 2c0c4ea053b0255ace67da27c4feebfe4edf0e00 Mon Sep 17 00:00:00 2001 From: SanketDG Date: Sat, 10 Jan 2015 21:07:39 +0530 Subject: [PATCH 02/62] Add Rabin Miller primality test --- misc/rabin_miller_primality_test.py | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 misc/rabin_miller_primality_test.py diff --git a/misc/rabin_miller_primality_test.py b/misc/rabin_miller_primality_test.py new file mode 100644 index 0000000..8e50008 --- /dev/null +++ b/misc/rabin_miller_primality_test.py @@ -0,0 +1,39 @@ +import sys +import random + + +def is_probable_prime(n, k=7): + """use Rabin-Miller algorithm to return True (n is probably prime) + or False (n is definitely composite) + + the parameter k defines the accuracy of the test. + """ + if n < 6: # assuming n >= 0 in all cases... shortcut small cases here + return [False, False, True, True, False, True][n] + if n & 1 == 0: # should be faster than n % 2 + return False + else: + s, d = 0, n - 1 + while d & 1 == 0: + s, d = s + 1, d >> 1 + # Use random.randint(2, n-2) for very large numbers + for a in random.sample(xrange(2, min(n - 2, sys.maxint)), min(n - 4, k)): + x = pow(a, d, n) + if x != 1 and x + 1 != n: + for r in xrange(1, s): + x = pow(x, 2, n) + if x == 1: + return False # composite for sure + elif x == n - 1: + a = 0 # so we know loop didn't continue to end + break # could be strong liar, try another a + if a: + return False # composite if we reached end of this loop + return True # probably prime if reached end of outer loop + +print is_probable_prime(7) +print is_probable_prime(5915587277) +print is_probable_prime(48112959837082048697) +print is_probable_prime(671998030559713968361666935769) +print is_probable_prime(2425967623052370772757633156976982469681) +print is_probable_prime(22953686867719691230002707821868552601124472329079) From c95b3ea5d7089333bdab0a6b3dcaadbaaf276b14 Mon Sep 17 00:00:00 2001 From: SanketDG Date: Sat, 10 Jan 2015 21:10:50 +0530 Subject: [PATCH 03/62] Add Rabin-Miller primality test --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 07fb51c..4c2cb92 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Completed --- - Karatsuba Multiplication - Basic Sorting +- Rabin-Miller primality test - Counting Inversions in an array - Selecting ith order statistic in an array - Graph datastructure (directed & undirected) From 8e8ff007440eee2bc594cc3faed75af7a7dc770a Mon Sep 17 00:00:00 2001 From: Antriani Stylianou Date: Fri, 13 Feb 2015 22:30:45 +0100 Subject: [PATCH 04/62] fix initial value --- misc/kadane.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/kadane.py b/misc/kadane.py index 08a2e4f..1524bc6 100644 --- a/misc/kadane.py +++ b/misc/kadane.py @@ -12,7 +12,7 @@ def find_max_subarray(numbers): def find_max_subarray2(numbers): """ shorter version """ - max_till_here = [numbers[0]] + max_till_here = [0] for n in numbers: max_till_here.append(max(n, max_till_here[-1] + n)) return max(max_till_here) From 9af82bd9c7d91a459b2021746bf1741162b9fd9f Mon Sep 17 00:00:00 2001 From: Antriani Stylianou Date: Sun, 15 Feb 2015 19:25:31 +0100 Subject: [PATCH 05/62] base case kadane --- misc/kadane.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/misc/kadane.py b/misc/kadane.py index 1524bc6..2d74f8c 100644 --- a/misc/kadane.py +++ b/misc/kadane.py @@ -12,8 +12,10 @@ def find_max_subarray(numbers): def find_max_subarray2(numbers): """ shorter version """ - max_till_here = [0] - for n in numbers: + if len(numbers) == 0: + return 0 + max_till_here = [numbers[0]] + for n in numbers[1:]: max_till_here.append(max(n, max_till_here[-1] + n)) return max(max_till_here) From 5b7b259080a19dc445ffe0865eca70c994580916 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Mon, 16 Feb 2015 19:15:32 +0300 Subject: [PATCH 06/62] removed test for blank array in kandane --- misc/kadane.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/misc/kadane.py b/misc/kadane.py index 2d74f8c..6827a52 100644 --- a/misc/kadane.py +++ b/misc/kadane.py @@ -9,11 +9,8 @@ def find_max_subarray(numbers): max_value = max(max_value, max_till_here[i]) return max_value - +# another version def find_max_subarray2(numbers): - """ shorter version """ - if len(numbers) == 0: - return 0 max_till_here = [numbers[0]] for n in numbers[1:]: max_till_here.append(max(n, max_till_here[-1] + n)) From 7dc6b91af9a8c54daa1e3ce1d3be2417ac8b8034 Mon Sep 17 00:00:00 2001 From: SanketDG Date: Wed, 11 Mar 2015 23:58:29 +0530 Subject: [PATCH 07/62] add binary search --- sorting and basics/binary_search.py | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 sorting and basics/binary_search.py diff --git a/sorting and basics/binary_search.py b/sorting and basics/binary_search.py new file mode 100644 index 0000000..c849ad7 --- /dev/null +++ b/sorting and basics/binary_search.py @@ -0,0 +1,36 @@ +from sorting import mergesort + + +def search(arr, item): + """Performs binary search on an array + with the given item and returns True or + False. + +>>> search([5, 4, 1, 6, 2, 3, 9, 7], 2) + True + +>>> search([5, 4, 1, 6, 2, 3, 9, 7], 8) + False + """ + + arr = mergesort(arr) + + first = 0 + last = len(arr) - 1 + found = False + + while first <= last and not found: + midpoint = (first + last) // 2 + if arr[midpoint] == item: + found = True + else: + if item < arr[midpoint]: + last = midpoint - 1 + else: + first = midpoint + 1 + + return found + + +print search([5, 4, 1, 6, 2, 3, 9, 7], 2) +print search([5, 4, 1, 6, 2, 3, 9, 7], 8) From ef0af81ce6c9e6b780c472334a0d0caab01cb53c Mon Sep 17 00:00:00 2001 From: SanketDG Date: Wed, 25 Mar 2015 20:52:47 +0530 Subject: [PATCH 08/62] resolve merge conflict --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4c2cb92..51450d1 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Completed - Karatsuba Multiplication - Basic Sorting - Rabin-Miller primality test +- Binary Search - Counting Inversions in an array - Selecting ith order statistic in an array - Graph datastructure (directed & undirected) From beaa5021aa24a3131b713165cb86a09b74a0844c Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Sat, 9 May 2015 22:48:31 -0500 Subject: [PATCH 09/62] Simplify the top by reading last element --- linear_datastructures/stack-adt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linear_datastructures/stack-adt.py b/linear_datastructures/stack-adt.py index 8b66349..a828ff7 100644 --- a/linear_datastructures/stack-adt.py +++ b/linear_datastructures/stack-adt.py @@ -17,7 +17,7 @@ def pop(self): def top(self): if self.isEmpty(): return None - return self.items[len(self.items)-1] + return self.items[-1] def string_reverse(s): stack = Stack() From ff9cf5f627165a6771f64fcf55e78c566535f400 Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Sat, 9 May 2015 23:53:01 -0500 Subject: [PATCH 10/62] Fix a bug for inserting When the value is equal, the function should return, or the length will be mistakenly increased by 1 --- trees/binarysearchtree.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/trees/binarysearchtree.py b/trees/binarysearchtree.py index 162be5f..0ff0488 100644 --- a/trees/binarysearchtree.py +++ b/trees/binarysearchtree.py @@ -106,6 +106,8 @@ def insert(self, value): node = node.right else: node = node.left + if node.value == value: + return if parent.value > value: parent.left = new_node else: From 6c09ea5e2b96277b41e9921f240c13ec53dfff15 Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Sun, 10 May 2015 23:45:11 -0500 Subject: [PATCH 11/62] simplify the nodes method dict.keys() should return list --- graphs/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/graph.py b/graphs/graph.py index 98c6c30..0ebbaca 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -61,7 +61,7 @@ def nodes(self): """ Returns a list of nodes in the graph """ - return list(self.node_neighbors.keys()) + return self.node_neighbors.keys() def has_edge(self, edge): """ From 9b707a06026cd79688947e3b6ea9d6c8e6eb5590 Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Sun, 10 May 2015 23:50:41 -0500 Subject: [PATCH 12/62] Fix potential bug in has edge The u node may not exist. --- graphs/graph.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/graphs/graph.py b/graphs/graph.py index 0ebbaca..1ceb265 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -69,9 +69,7 @@ def has_edge(self, edge): graph. An edge, here, is a pair of node like C(m, n) or a tuple """ u, v = edge - if v not in self.node_neighbors[u]: - return False - return True + return v in self.node_neighbors.get(u, []) def neighbors(self, node): """ From 4e3370559d542a4005cfecc0b8750a8c9dfa03d9 Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Sun, 10 May 2015 23:53:37 -0500 Subject: [PATCH 13/62] change hard coded default wright in add_edge --- graphs/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/graph.py b/graphs/graph.py index 1ceb265..62da9c2 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -38,7 +38,7 @@ def has_node(self, node): """ return node in self.node_neighbors - def add_edge(self, edge, wt=1, label=""): + def add_edge(self, edge, wt=self.DEFAULT_WEIGHT, label=""): """ Add an edge to the graph connecting two nodes. An edge, here, is a pair of node like C(m, n) or a tuple From c74a4e957d084ed5f0957f9355feb133f9c84b97 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Sun, 10 May 2015 23:59:50 -0500 Subject: [PATCH 14/62] change self.DEFAULT_WEIGHT to DEFAULT_WEIGHT --- graphs/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/graph.py b/graphs/graph.py index 62da9c2..277fd6d 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -38,7 +38,7 @@ def has_node(self, node): """ return node in self.node_neighbors - def add_edge(self, edge, wt=self.DEFAULT_WEIGHT, label=""): + def add_edge(self, edge, wt=DEFAULT_WEIGHT, label=""): """ Add an edge to the graph connecting two nodes. An edge, here, is a pair of node like C(m, n) or a tuple From 1086ab4a759d74bda3f65de9dc3dee4ba1fe9b89 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Mon, 11 May 2015 00:13:56 -0500 Subject: [PATCH 15/62] make edges method a little more efficient --- graphs/graph.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphs/graph.py b/graphs/graph.py index 277fd6d..47e2016 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -113,8 +113,8 @@ def edges(self): """ edge_list = [] for node in self.nodes(): - for each in self.neighbors(node): - edge_list.append((node, each)) + edges = [(node, each) for each in self.neighbors(node)] + edge_list.extend(edges) return edge_list # Methods for setting properties on nodes and edges From 4b9a062303c53c3b51252731d97c4b7cdae9f33f Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Mon, 11 May 2015 00:16:24 -0500 Subject: [PATCH 16/62] check if edge exists in set_edge weight method --- graphs/graph.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/graphs/graph.py b/graphs/graph.py index 47e2016..4b0f0c2 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -121,6 +121,8 @@ def edges(self): def set_edge_weight(self, edge, wt): """Set the weight of the edge """ u, v = edge + if not self.has_edge(edge): + raise Exception("Edge (%s, %s) not an existing edge" % (u, v)) self.node_neighbors[u][v] = wt if u != v: self.node_neighbors[v][u] = wt From 41f4f39c4d080ba6579630e770eb7ec2a4305fb8 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Mon, 11 May 2015 00:27:08 -0500 Subject: [PATCH 17/62] simplify get_edge_weights --- graphs/graph.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/graphs/graph.py b/graphs/graph.py index 4b0f0c2..a2fce7a 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -140,10 +140,7 @@ def get_edge_weights(self): unique_list = {} for u in self.nodes(): for v in self.neighbors(u): - if not unique_list.get(v) or u not in unique_list.get(v): + if u not in unique_list.get(v, []): edge_list.append((self.node_neighbors[u][v], (u, v))) - if u not in unique_list: - unique_list[u] = [v] - else: - unique_list[u].append(v) + unique_list.setdefault(u, [v]).append(v) return edge_list From e653c6eb9baa67c54c2f732250a99a41ea303ec4 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Mon, 11 May 2015 00:37:52 -0500 Subject: [PATCH 18/62] change default list in get_edge_weights --- graphs/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/graph.py b/graphs/graph.py index a2fce7a..bb96bd3 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -142,5 +142,5 @@ def get_edge_weights(self): for v in self.neighbors(u): if u not in unique_list.get(v, []): edge_list.append((self.node_neighbors[u][v], (u, v))) - unique_list.setdefault(u, [v]).append(v) + unique_list.setdefault(u, []).append(v) return edge_list From c55ad5f8e647bebd84732815fd9e325de1fa68a5 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Mon, 11 May 2015 01:35:58 -0500 Subject: [PATCH 19/62] change list to set for performance improvement --- graphs/graph.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphs/graph.py b/graphs/graph.py index bb96bd3..fb2073e 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -140,7 +140,7 @@ def get_edge_weights(self): unique_list = {} for u in self.nodes(): for v in self.neighbors(u): - if u not in unique_list.get(v, []): + if u not in unique_list.get(v, set()): edge_list.append((self.node_neighbors[u][v], (u, v))) - unique_list.setdefault(u, []).append(v) + unique_list.setdefault(u, set()).add(v) return edge_list From d996988c79018f01d2df4272f114807145f3221a Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Mon, 11 May 2015 01:57:06 -0500 Subject: [PATCH 20/62] change hard coded default wright in add_edge --- graphs/digraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/digraph.py b/graphs/digraph.py index 10d3a6d..e8badd3 100644 --- a/graphs/digraph.py +++ b/graphs/digraph.py @@ -18,7 +18,7 @@ def __init__(self): def __str__(self): return "Directed Graph \nNodes: %s \nEdges: %s" % (self.nodes(), self.edges()) - def add_edge(self, edge, wt=1, label=""): + def add_edge(self, edge, wt=DEFAULT_WEIGHT, label=""): """ Add an edge to the graph connecting two nodes. An edge, here, is a pair of node like C(m, n) or a tuple From f8ab93b463620d07d14b275d7cdc6cacc5fe0406 Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Tue, 12 May 2015 00:20:52 -0500 Subject: [PATCH 21/62] Change list to set to optimize performance --- graphs/graph_algorithms.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/graphs/graph_algorithms.py b/graphs/graph_algorithms.py index 4329399..ce5530a 100644 --- a/graphs/graph_algorithms.py +++ b/graphs/graph_algorithms.py @@ -8,13 +8,13 @@ def BFS(gr, s): Returns a list of nodes that are "findable" from s """ if not gr.has_node(s): raise Exception("Node %s not in graph" % s) - nodes_explored = [s] + nodes_explored = set([s]) q = deque([s]) while len(q)!=0: node = q.popleft() for each in gr.neighbors(node): if each not in nodes_explored: - nodes_explored.append(each) + nodes_explored.add(each) q.append(each) return nodes_explored @@ -28,7 +28,7 @@ def shortest_hops(gr, s): else: dist = {} q = deque([s]) - nodes_explored = [s] + nodes_explored = set([s]) for n in gr.nodes(): if n == s: dist[n] = 0 else: dist[n] = float('inf') @@ -36,7 +36,7 @@ def shortest_hops(gr, s): node = q.popleft() for each in gr.neighbors(node): if each not in nodes_explored: - nodes_explored.append(each) + nodes_explored.add(each) q.append(each) dist[each] = dist[node] + 1 return dist @@ -121,7 +121,7 @@ def outer_dfs(digr, node, nodes_explored, path): def DFS_loop(digr): """ Core DFS loop used to find strongly connected components in a directed graph """ - node_explored = [] # list for keeping track of nodes explored + node_explored = set([]) # list for keeping track of nodes explored finishing_times = [] # list for adding nodes based on their finishing times for node in digr.nodes(): if node not in node_explored: @@ -131,7 +131,7 @@ def DFS_loop(digr): def inner_DFS(digr, node, node_explored, finishing_times): """ Inner DFS used in DFS loop method """ - node_explored.append(node) # mark explored + node_explored.add(node) # mark explored for each in digr.neighbors(node): if each not in node_explored: inner_DFS(digr, each, node_explored, finishing_times) @@ -143,7 +143,7 @@ def shortest_path(digr, s): """ Finds the shortest path from s to every other vertex findable from s using Dijkstra's algorithm in O(mlogn) time. Uses heaps for super fast implementation """ - nodes_explored = [s] + nodes_explored = set([s]) nodes_unexplored = DFS(digr, s)[1:] # all accessible nodes from s dist = {s:0} node_heap = [] @@ -155,7 +155,7 @@ def shortest_path(digr, s): while len(node_heap) > 0: min_dist, nearest_node = heapq.heappop(node_heap) dist[nearest_node] = min_dist - nodes_explored.append(nearest_node) + nodes_explored.add(nearest_node) nodes_unexplored.remove(nearest_node) # recompute keys for just popped node @@ -183,7 +183,7 @@ def minimum_spanning_tree(gr): cost spanning tree in a undirected connected graph. Works only with undirected and connected graphs """ s = gr.nodes()[0] - nodes_explored = [s] + nodes_explored = set([s]) nodes_unexplored = gr.nodes() nodes_unexplored.remove(s) min_cost, node_heap = 0, [] @@ -197,7 +197,7 @@ def minimum_spanning_tree(gr): # adds the cheapest to "explored" node_cost, min_node = heapq.heappop(node_heap) min_cost += node_cost - nodes_explored.append(min_node) + nodes_explored.add(min_node) nodes_unexplored.remove(min_node) # recompute keys for neighbors of deleted node From 28a6ce5d9282f84e134aaec76f5411876fd6fa5f Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Tue, 12 May 2015 00:32:55 -0500 Subject: [PATCH 22/62] union two sets in undirected_connected_components --- graphs/graph_algorithms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphs/graph_algorithms.py b/graphs/graph_algorithms.py index ce5530a..cf6349c 100644 --- a/graphs/graph_algorithms.py +++ b/graphs/graph_algorithms.py @@ -46,13 +46,13 @@ def undirected_connected_components(gr): in an undirected graph """ if gr.DIRECTED: raise Exception("This method works only with a undirected graph") - explored = [] + explored = set([]) con_components = [] for node in gr.nodes(): if node not in explored: reachable_nodes = BFS(gr, node) con_components.append(reachable_nodes) - explored += reachable_nodes + explored |= reachable_nodes return con_components def DFS(gr, s): From 0660da907542e2e1fd754471ec8c842f89de4513 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Tue, 12 May 2015 00:43:09 -0500 Subject: [PATCH 23/62] change list to set in depth_first_search --- graphs/graph_algorithms.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/graphs/graph_algorithms.py b/graphs/graph_algorithms.py index cf6349c..3818a23 100644 --- a/graphs/graph_algorithms.py +++ b/graphs/graph_algorithms.py @@ -57,7 +57,7 @@ def undirected_connected_components(gr): def DFS(gr, s): """ Depth first search wrapper """ - path = [] + path = set([]) depth_first_search(gr, s, path) return path @@ -65,7 +65,7 @@ def depth_first_search(gr, s, path): """ Depth first search Returns a list of nodes "findable" from s """ if s in path: return False - path.append(s) + path.add(s) for each in gr.neighbors(s): if each not in path: depth_first_search(gr, each, path) @@ -144,7 +144,8 @@ def shortest_path(digr, s): from s using Dijkstra's algorithm in O(mlogn) time. Uses heaps for super fast implementation """ nodes_explored = set([s]) - nodes_unexplored = DFS(digr, s)[1:] # all accessible nodes from s + nodes_unexplored = DFS(digr, s) # all accessible nodes from s + nodes_unexplored.remove(s) dist = {s:0} node_heap = [] From 465091ca468607645efc89d5c1d5cc1e0937153b Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Tue, 12 May 2015 10:17:15 -0500 Subject: [PATCH 24/62] optimize mergesort --- sorting and basics/sorting.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sorting and basics/sorting.py b/sorting and basics/sorting.py index 11d94c7..dbcbc0f 100644 --- a/sorting and basics/sorting.py +++ b/sorting and basics/sorting.py @@ -9,10 +9,8 @@ def mergesort(arr): """ n = len(arr) if n <= 1: return arr - a1 = arr[:n/2] - a2 = arr[n/2:] - a1 = mergesort(a1) - a2 = mergesort(a2) + a1 = mergesort(arr[:n/2]) + a2 = mergesort(arr[n/2:]) return merge(a1, a2) def merge(arr_a, arr_b): @@ -25,8 +23,8 @@ def merge(arr_a, arr_b): else: arr_c.append(arr_b[j]) j += 1 - if arr_a[i:]: arr_c += arr_a[i:] - if arr_b[j:]: arr_c += arr_b[j:] + if arr_a[i:]: arr_c.extend(arr_a[i:]) + if arr_b[j:]: arr_c.extend(arr_b[j:]) return arr_c def quicksort(a): From 1f410b621a07f515fe4e170e727344bc10b19f6b Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Tue, 12 May 2015 10:53:21 -0500 Subject: [PATCH 25/62] opotimize the insertion sort --- sorting and basics/sorting.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sorting and basics/sorting.py b/sorting and basics/sorting.py index dbcbc0f..9e19d4e 100644 --- a/sorting and basics/sorting.py +++ b/sorting and basics/sorting.py @@ -82,8 +82,9 @@ def insertionsort(a): item = a[i] j = i while j > 0 and a[j-1] > item: - a[j],a[j-1] = a[j-1],a[j] + a[j] = a[j-1] j -= 1 + a[j] = item return a if __name__ == "__main__": From ceefd98f8203e12942885dcfdbe170146a8a671d Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Tue, 12 May 2015 22:24:36 -0500 Subject: [PATCH 26/62] optimize karatsuba --- sorting and basics/karatsuba.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sorting and basics/karatsuba.py b/sorting and basics/karatsuba.py index b361b2b..1ce9ad3 100644 --- a/sorting and basics/karatsuba.py +++ b/sorting and basics/karatsuba.py @@ -6,17 +6,17 @@ def karatsuba(x, y, b=10): >>> karatsuba(1234223123412323, 1234534213423333123) 1523690672850721578619752112274729L """ - nx, ny = len(str(x))/2, len(str(y))/2 - if x < 1000 or y < 1000: return x * y - m = nx if nx < ny else ny - x1 = x / (b**m) - x0 = x % (x1 * (b**m)) - y1 = y / (b**m) - y0 = y % (y1 * (b**m)) - z1 = karatsuba(x1,y1,b) - z3 = karatsuba(x0,y0,b) + + if x < 1000 or y < 1000: + return x * y + m = min(len(str(x)) / 2, len(str(y)) / 2) + bm = b**m + x1, x0 = x / bm, x % bm + y1, y0 = y / bm, y % bm + z1 = karatsuba(x1, y1, b) + z3 = karatsuba(x0, y0, b) z2 = karatsuba(x1 + x0, y1 + y0, b) - z1 - z3 - return (b**(2*m))*z1 + (b**m)*z2 + z3 + return (bm**2)*z1 + bm*z2 + z3 if __name__ == "__main__": import doctest From bd5b6500eae8d9895e00a8e9b3cc596d6b9a8554 Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Wed, 13 May 2015 13:33:16 -0500 Subject: [PATCH 27/62] remove keys() in count_groups It is not necessary to form a list of keys to get size of dictionary. --- union_find/unionfind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/union_find/unionfind.py b/union_find/unionfind.py index 8f5838b..2bce362 100644 --- a/union_find/unionfind.py +++ b/union_find/unionfind.py @@ -59,7 +59,7 @@ def get_leader(self, a): def count_groups(self): """ returns a count of the number of groups/sets in the data structure""" - return len(self.group.keys()) + return len(self.group) def make_union(self, leadera, leaderb): """ takes union of two sets with leaders, leadera and leaderb From 2d1c7e799aae1d285e4b8fffd834bf13daf47740 Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Wed, 13 May 2015 21:58:22 -0500 Subject: [PATCH 28/62] fix division bug Need to have float number at one side for correct division --- heaps/minheap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/heaps/minheap.py b/heaps/minheap.py index 8c64b76..c43a32d 100644 --- a/heaps/minheap.py +++ b/heaps/minheap.py @@ -23,7 +23,7 @@ def height(self): def is_leaf(self, i): """ returns True if i is a leaf node """ - return i > int(math.ceil( (len(self.heap)- 2) / 2)) + return i > int(math.ceil((len(self.heap) - 2) / 2.0)) def parent(self, i): if i == 0: From ccb9099cce0b0fb04181364eb6fd4f42452adf50 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Wed, 13 May 2015 22:05:10 -0500 Subject: [PATCH 29/62] simpligy parent, leftchild, and rightchild method --- heaps/minheap.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/heaps/minheap.py b/heaps/minheap.py index c43a32d..144e4cb 100644 --- a/heaps/minheap.py +++ b/heaps/minheap.py @@ -11,7 +11,7 @@ def __init__(self, nums=None): self.heap = [] if nums: self.build_heap(nums) - + def __str__(self): return "Min-heap with %s items" % (len(self.heap)) @@ -19,7 +19,7 @@ def max_elements(self): return len(self.heap) def height(self): - return math.ceil(math.log(len(self.heap))/math.log(2)) + return math.ceil(math.log(len(self.heap)) / math.log(2)) def is_leaf(self, i): """ returns True if i is a leaf node """ @@ -29,18 +29,14 @@ def parent(self, i): if i == 0: return [] elif i % 2 != 0: # odd - return (i-1)/2 - return int(math.floor((i-1)/2)) + return (i - 1) / 2 + return (i - 2) / 2 def leftchild(self, i): - if not self.is_leaf(i): - return 2*i+1 - return [] + return 2 * i + 1 def rightchild(self, i): - if not self.is_leaf(i): - return 2*i+2 - return [] + return 2 * i + 2 def heapify(self, i): l = self.leftchild(i) @@ -60,8 +56,7 @@ def build_heap(self, elem): self.heap = elem[:] last_leaf = int(math.ceil( (len(self.heap)- 2) / 2)) for i in range(last_leaf, -1, -1): - self.heapify(i) - + self.heapify(i) def heappush(self, x): """ Adds a new item x in the heap""" From 794958f3003649e2af2eefab9afa120cf930f3f5 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Wed, 13 May 2015 22:19:40 -0500 Subject: [PATCH 30/62] change the return value of parent for root --- heaps/maxheap.py | 2 +- heaps/minheap.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/heaps/maxheap.py b/heaps/maxheap.py index 6f14856..f902af4 100644 --- a/heaps/maxheap.py +++ b/heaps/maxheap.py @@ -27,7 +27,7 @@ def heappush(self, x): i = len(self.heap) self.heap.append(x) parent = self.parent(i) - while parent != [] and self.heap[i] > self.heap[parent]: + while parent != -1 and self.heap[i] > self.heap[parent]: self.heap[i], self.heap[parent] = self.heap[parent], self.heap[i] i = parent parent = self.parent(i) diff --git a/heaps/minheap.py b/heaps/minheap.py index 144e4cb..a3cce3a 100644 --- a/heaps/minheap.py +++ b/heaps/minheap.py @@ -27,7 +27,7 @@ def is_leaf(self, i): def parent(self, i): if i == 0: - return [] + return -1 elif i % 2 != 0: # odd return (i - 1) / 2 return (i - 2) / 2 @@ -54,16 +54,16 @@ def build_heap(self, elem): """ transforms a list of elements into a heap in linear time """ self.heap = elem[:] - last_leaf = int(math.ceil( (len(self.heap)- 2) / 2)) + last_leaf = self.parent(len(self.heap)) for i in range(last_leaf, -1, -1): - self.heapify(i) + self.heapify(i) def heappush(self, x): """ Adds a new item x in the heap""" i = len(self.heap) self.heap.append(x) parent = self.parent(i) - while parent != [] and self.heap[i] < self.heap[parent]: + while parent != -1 and self.heap[i] < self.heap[parent]: self.heap[i], self.heap[parent] = self.heap[parent], self.heap[i] i = parent parent = self.parent(i) From 756213f5dc150ef3bae72b16d2bc9139b89c2f6d Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Thu, 14 May 2015 15:05:16 -0500 Subject: [PATCH 31/62] Simplify _get_child_branches --- trees/trie.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/trees/trie.py b/trees/trie.py index b5d858c..be951e6 100644 --- a/trees/trie.py +++ b/trees/trie.py @@ -6,8 +6,6 @@ """ # HELPERS # def _get_child_branches(tr): - if tr == []: - return [] return tr[1:] def _get_child_branch(tr, c): From 1d19fd8c01a41c203e4ccf5cf3e8faf3062c2dd9 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Thu, 14 May 2015 15:25:12 -0500 Subject: [PATCH 32/62] simplify insert_key --- trees/trie.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/trees/trie.py b/trees/trie.py index be951e6..7ae717d 100644 --- a/trees/trie.py +++ b/trees/trie.py @@ -56,22 +56,19 @@ def retrie_val(k, tr): def insert_key(key, v, trie_list): - if key == "": - return None - elif has_key(key, trie_list): - return None - else: - tr = trie_list - for char in key: - branch = _get_child_branch(tr, char) - if branch == None: - new_branch = [char] - tr.append(new_branch) - tr = new_branch - else: - tr = branch - tr.append((key, v)) - return None + if key == "" or has_key(key, trie_list): + return + + tr = trie_list + for char in key: + branch = _get_child_branch(tr, char) + if branch == None: + new_branch = [char] + tr.append(new_branch) + tr = new_branch + else: + tr = branch + tr.append((key, v)) def start_with_prefix(prefix, trie): From 06ba8172c95d50446f5ae1717898eaeaac9f8891 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Thu, 14 May 2015 15:51:03 -0500 Subject: [PATCH 33/62] simplify method and reformat code to PEP8 style --- trees/trie.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/trees/trie.py b/trees/trie.py index 7ae717d..bbf042b 100644 --- a/trees/trie.py +++ b/trees/trie.py @@ -5,17 +5,25 @@ start_with_prefix(prefix) """ # HELPERS # + + def _get_child_branches(tr): return tr[1:] + def _get_child_branch(tr, c): + """ + Find matching branch with the character + """ for branch in _get_child_branches(tr): if branch[0] == c: return branch + return None + def _retrive_branch(k, trie_list): - if k == "": + if not k: return None tr = trie_list for c in k: @@ -23,46 +31,48 @@ def _retrive_branch(k, trie_list): if not child_branch: return None tr = child_branch + return tr + def _is_trie_bucket(bucket): if len(bucket) != 2: return False - if type(bucket[1]) is tuple: - return True + + return type(bucket[1]) is tuple + def _get_bucket_key(bucket): if not _is_trie_bucket(bucket): return None - return bucket[1][0] + + return bucket[1][0] # HAS_KEY # + + def has_key(k, tr): - if k == "": - return None - key_tuple = _retrive_branch(k, tr) - if not key_tuple: - return False - return True + return _retrive_branch(k, tr) is not None # RETRIE_VAL + + def retrie_val(k, tr): - if k == "": - return None key_tuple = _retrive_branch(k, tr) if not key_tuple: return None + return key_tuple[1] def insert_key(key, v, trie_list): - if key == "" or has_key(key, trie_list): + if not key or has_key(key, trie_list): return tr = trie_list for char in key: branch = _get_child_branch(tr, char) - if branch == None: + if not branch: new_branch = [char] tr.append(new_branch) tr = new_branch @@ -137,7 +147,7 @@ def start_with_prefix(prefix, trie): Washington West Virginia Wisconsin - Wyoming""" + Wyoming""" states_list = [w.strip().lower() for w in states.splitlines() if w] for state in states_list: insert_key(state, True, trie) From 25a11b9f7697cbb665b478fe5a78567c7a8ef77d Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Thu, 14 May 2015 15:54:47 -0500 Subject: [PATCH 34/62] make parameter more consistent --- trees/trie.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/trees/trie.py b/trees/trie.py index bbf042b..ea0490f 100644 --- a/trees/trie.py +++ b/trees/trie.py @@ -7,32 +7,32 @@ # HELPERS # -def _get_child_branches(tr): - return tr[1:] +def _get_child_branches(trie): + return trie[1:] -def _get_child_branch(tr, c): +def _get_child_branch(trie, c): """ Find matching branch with the character """ - for branch in _get_child_branches(tr): + for branch in _get_child_branches(trie): if branch[0] == c: return branch return None -def _retrive_branch(k, trie_list): +def _retrive_branch(k, trie): if not k: return None - tr = trie_list + for c in k: - child_branch = _get_child_branch(tr, c) + child_branch = _get_child_branch(trie, c) if not child_branch: return None - tr = child_branch + trie = child_branch - return tr + return trie def _is_trie_bucket(bucket): @@ -51,40 +51,40 @@ def _get_bucket_key(bucket): # HAS_KEY # -def has_key(k, tr): - return _retrive_branch(k, tr) is not None +def has_key(k, trie): + return _retrive_branch(k, trie) is not None # RETRIE_VAL -def retrie_val(k, tr): - key_tuple = _retrive_branch(k, tr) +def retrie_val(k, trie): + key_tuple = _retrive_branch(k, trie) if not key_tuple: return None return key_tuple[1] -def insert_key(key, v, trie_list): - if not key or has_key(key, trie_list): +def insert_key(key, v, trie): + if not key or has_key(key, trie): return - tr = trie_list for char in key: - branch = _get_child_branch(tr, char) + branch = _get_child_branch(trie, char) if not branch: new_branch = [char] - tr.append(new_branch) - tr = new_branch + trie.append(new_branch) + trie = new_branch else: - tr = branch - tr.append((key, v)) + trie = branch + trie.append((key, v)) def start_with_prefix(prefix, trie): branch = _retrive_branch(prefix, trie) if not branch: return [] + prefix_list = [] q = branch[1:] while q: From 2d6548effc4fc6aea0286472ae2a15d7efdf3692 Mon Sep 17 00:00:00 2001 From: Xuefeng-Zhu Date: Thu, 14 May 2015 16:01:11 -0500 Subject: [PATCH 35/62] add comments --- trees/trie.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/trees/trie.py b/trees/trie.py index ea0490f..88bfbf7 100644 --- a/trees/trie.py +++ b/trees/trie.py @@ -1,19 +1,21 @@ -""" Tries in python +""" Tries in python Methods - insert_key(k, v) has_key(k) retrie_val(k) start_with_prefix(prefix) """ -# HELPERS # def _get_child_branches(trie): + """ + Helper method for getting branches + """ return trie[1:] def _get_child_branch(trie, c): """ - Find matching branch with the character + Get branch matching the character """ for branch in _get_child_branches(trie): if branch[0] == c: @@ -23,6 +25,9 @@ def _get_child_branch(trie, c): def _retrive_branch(k, trie): + """ + Get branch matching the key word + """ if not k: return None @@ -48,14 +53,13 @@ def _get_bucket_key(bucket): return bucket[1][0] -# HAS_KEY # - def has_key(k, trie): + """ + Check if trie contain the key word + """ return _retrive_branch(k, trie) is not None -# RETRIE_VAL - def retrie_val(k, trie): key_tuple = _retrive_branch(k, trie) @@ -66,6 +70,9 @@ def retrie_val(k, trie): def insert_key(key, v, trie): + """ + Insert a (key, value) pair into trie + """ if not key or has_key(key, trie): return @@ -81,6 +88,9 @@ def insert_key(key, v, trie): def start_with_prefix(prefix, trie): + """ + Find words start with prefix + """ branch = _retrive_branch(prefix, trie) if not branch: return [] @@ -93,6 +103,7 @@ def start_with_prefix(prefix, trie): prefix_list.append(_get_bucket_key(curr_branch)) else: q.extend(curr_branch[1:]) + return prefix_list if __name__ == "__main__": From 00d882e00c5d98e314728087d08e1923213e783b Mon Sep 17 00:00:00 2001 From: Xuefeng Zhu Date: Thu, 14 May 2015 20:56:51 -0500 Subject: [PATCH 36/62] Fix the program --- misc/max_area_histogram.py | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/misc/max_area_histogram.py b/misc/max_area_histogram.py index ce342ab..0769e36 100644 --- a/misc/max_area_histogram.py +++ b/misc/max_area_histogram.py @@ -10,7 +10,7 @@ """ -# hist represented as ith bar has height h(i) +# hist represented as ith bar has height h(i) histogram = [6, 4, 2, 1, 3, 4, 5, 2, 6] """ @@ -21,26 +21,29 @@ 4. compute max area """ -def get_L(hist): - L = [0]*len(hist) - for i in range(1, len(hist)): - if hist[i] > hist[i-1]: - L[i] = i + +def find_Li(hist, i): + left_edge = 0 + for j in range(i-1, -1, -1): + if hist[j] >= hist[i]: + left_edge += 1 else: - L[i] = L[i-1] - return L + return left_edge + + return left_edge -print get_L(histogram) def find_Ri(hist, i): right_edge = 0 - for j in range(i+1, len(hist)): + for j in range(i + 1, len(hist)): if hist[j] >= hist[i]: right_edge += 1 else: return right_edge + return right_edge + def get_area(hist, i): return hist[i] * (find_Li(hist, i) + find_Ri(hist, i) + 1) @@ -53,6 +56,7 @@ def get_max_area(hist): max_area = area return max_area + def max_rectangle_area(histogram): """Find the area of the largest rectangle that fits entirely under the histogram. @@ -61,21 +65,21 @@ def max_rectangle_area(histogram): stack = [] top = lambda: stack[-1] max_area = 0 - pos = 0 # current position in the histogram + pos = 0 # current position in the histogram for pos, height in enumerate(histogram): - start = pos # position where rectangle starts + start = pos # position where rectangle starts while True: - if not stack or height > top().height: - stack.append(Info(start, height)) # push - elif stack and height < top().height: - max_area = max(max_area, top().height*(pos-top().start)) + if not stack or height > top()[1]: + stack.append((start, height)) # push + elif stack and height < top()[1]: + max_area = max(max_area, top()[1] * (pos - top()[0])) start, _ = stack.pop() continue - break # height == top().height goes here + break # height == top().height goes here pos += 1 for start, height in stack: - max_area = max(max_area, height*(pos-start)) + max_area = max(max_area, height * (pos - start)) return max_area From c6968b1221ef60bbc4ff17716cbc3049b7b97c88 Mon Sep 17 00:00:00 2001 From: Vikas Prasad Date: Mon, 3 Aug 2015 22:08:53 +0530 Subject: [PATCH 37/62] Added Hierholzer's algorithm to find Eulerian Tour in a graph. --- graphs/eulerian_tour.py | 105 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100755 graphs/eulerian_tour.py diff --git a/graphs/eulerian_tour.py b/graphs/eulerian_tour.py new file mode 100755 index 0000000..055571a --- /dev/null +++ b/graphs/eulerian_tour.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# Find Eulerian Tour +# +# Write a program that takes in a graph +# represented as a list of tuples +# and return a list of nodes that +# you would follow on an Eulerian Tour +# +# For example, if the input graph was +# [(1, 2), (2, 3), (3, 1)] +# A possible Eulerian tour would be [1, 2, 3, 1] + +def get_a_tour(): + '''This function returns a possible tour in the current graph and removes the edges included in that tour, from the graph.''' + global graph + + nodes_degree = {} # Creating a {node: degree} dictionary for current graph. + for edge in graph: + a, b = edge[0], edge[1] + nodes_degree[a] = nodes_degree.get(a, 0) + 1 + nodes_degree[b] = nodes_degree.get(b, 0) + 1 + + tour =[] # Finding a tour in the current graph. + loop = enumerate(nodes_degree) + while True: + try: + l = loop.__next__() + index = l[0] + node = l[1] + degree = nodes_degree[node] + try: + if (tour[-1], node) in graph or (node, tour[-1]) in graph: + tour.append(node) + try: + graph.remove((tour[-2], tour[-1])) + nodes_degree[tour[-1]] -= 1 # Updating degree of nodes in the graph, not required but for the sake of completeness. + nodes_degree[tour[-2]] -= 1 # Can also be used to check the correctness of program. In the end all degrees must zero. + except ValueError: + graph.remove((tour[-1], tour[-2])) + nodes_degree[tour[-1]] -= 1 + nodes_degree[tour[-2]] -= 1 + except IndexError: + tour.append(node) + except StopIteration: + loop = enumerate(nodes_degree) + + if len(tour) > 2: + if tour[0] == tour[-1]: + return tour + +def get_eulerian_tour(): + '''This function returns a Eulerian Tour for the input graph.''' + global graph + tour = get_a_tour() + + if graph: # If stuck at the beginning, finding additional tour in the graph. + loop = enumerate(tour[: -1]) + l = loop.__next__() + i = l[0] + node = l[1] + try: + while True: + if node in list(zip(*graph))[0] or node in list(zip(*graph))[1]: + t = get_a_tour() # Retreivng the additional tour + j = t.index(node) + tour = tour[ : i] + t[j:-1] + t[ :j+1] + tour[i+1: ] # Joining the two tours. + if not graph: # Found Eulerian Tour + return tour # Returning the Eulerian Tour + loop = enumerate(tour[: -1]) # Still stuck? Looping back to search for another tour. + l = loop.__next__() + i = l[0] + node = l[1] + except StopIteration: # Oops! seems like the vertices in the current tour cannot connect to rest of the edges in the graph. + print("Your graph doesn't seem to be connected") + exit() + else: # Found the Eulerian Tour in the very first call. Lucky Enough! + return tour + +# Sample inputs +# graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6)] +# graph = [(1, 2), (1, 3), (2, 3)] +# graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (9, 10), (10, 11), (11, 9)] +# graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (2, 7), (7, 8), (8, 2)] +# graph = [(1, 2), (1, 3), (2, 3), (2, 4), (2, 6), (3, 4), (3, 5), (4, 5), (4, 6), (1, 5), (5, 6), (1, 6)] +# graph = [(1, 2), (2, 3), (3, 1), (3, 4), (4, 3)] +# graph = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] +# graph = [(2, 6), (4, 2), (5, 4), (6, 5), (6, 8), (7, 9), (8, 7), (9, 6)] + +# creating a {node: degree} dictionary +nodes_degree = {} +for edge in graph: + a, b = edge[0], edge[1] + nodes_degree[a] = nodes_degree.get(a, 0) + 1 + nodes_degree[b] = nodes_degree.get(b, 0) + 1 + +#checking degree +degrees = nodes_degree.values() # remember it return a view +for degree in degrees: + if degree % 2: + print("Your graph have one or more nodes with odd degrees. Hence an Eulerian Tour is impossible.") + exit() + +#finding Eulerian Tour +tour = get_eulerian_tour() +print(tour) From 5e54a27618d64767a3bc56dc202dfa563ad5abc6 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Mon, 10 Aug 2015 14:27:57 +0530 Subject: [PATCH 38/62] Added a singly linked list --- linear_datastructures/__init__.py | 0 linear_datastructures/single_linked_list.py | 72 +++++++++++++++++++++ misc/temp.cpp | 63 ------------------ 3 files changed, 72 insertions(+), 63 deletions(-) create mode 100644 linear_datastructures/__init__.py create mode 100644 linear_datastructures/single_linked_list.py delete mode 100644 misc/temp.cpp diff --git a/linear_datastructures/__init__.py b/linear_datastructures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/linear_datastructures/single_linked_list.py b/linear_datastructures/single_linked_list.py new file mode 100644 index 0000000..c473b14 --- /dev/null +++ b/linear_datastructures/single_linked_list.py @@ -0,0 +1,72 @@ +class Node(object): + def __init__(self, data=None): + self.data = data + self.next = None + + def __repr__(self): + return str(self.data) + +class LinkedList(object): + def __init__(self, iterable=[]): + self.head = None + self.size = 0 + for item in iterable: + self.append(item) + + def __repr__(self): + (current, nodes) = self.head, [] + while current: + nodes.append(str(current)) + current = current.next + return "->".join(nodes) + + def __len__(self): + return self.size + + def __iter__(self): + current = self.head + while current: + yield current + current = current.next + raise StopIteration + + def __contains__(self, data): + tmp = self.head + found = False + while tmp and not found: + if data == tmp.data: + found = True + else: + tmp = tmp.next + return found + + def append(self, data): + tmp = Node(data) + tmp.next = self.head + self.head = tmp + self.size += 1 + + def getHead(self): + return self.head + + def getTail(self): + tmp = self.head + while tmp.next: + tmp = tmp.next + return tmp + + def delete(self, data): + tmp = self.head + prev = None + found = False + while tmp and not found: + if data == tmp.data: + found = True + else: + prev = tmp + tmp = tmp.next + if found: + if prev == None: + self.head = self.head.next + else: + prev.next = tmp.next diff --git a/misc/temp.cpp b/misc/temp.cpp deleted file mode 100644 index d580669..0000000 --- a/misc/temp.cpp +++ /dev/null @@ -1,63 +0,0 @@ -int largestArea(int arr[], int len) -{ - int area[len]; //initialize it to 0 - int n, i, t; - stack St; //include stack for using this #include - bool done; - - for (i=0; i=0; i--) - { - while (!St.empty()) - { - if(arr[i] <= arr[St.top()]) - { - St.pop(); - } - else - break; - } - if(St.empty()) - t = len; - else - t = St.top(); - //calculating Ri, after this step area[i] = Li + Ri - area[i] += t - i -1; - St.push(i); - } - - int max = 0; - //Calculating Area[i] and find max Area - for (i=0; i max) - max = area[i]; - } - - return max; -} - From 2b54e4a98dbf556494a5de2866c9b88f96cc4a4f Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Mon, 10 Aug 2015 15:04:59 +0530 Subject: [PATCH 39/62] Added tests for singly linked lists --- README.md | 1 + {linear_datastructures => lists}/__init__.py | 0 .../singlylinkedlist.py | 13 ++++++++--- {linear_datastructures => lists}/stack-adt.py | 0 tests/singly_linked_list_test.py | 22 +++++++++++++++++++ 5 files changed, 33 insertions(+), 3 deletions(-) rename {linear_datastructures => lists}/__init__.py (100%) rename linear_datastructures/single_linked_list.py => lists/singlylinkedlist.py (81%) rename {linear_datastructures => lists}/stack-adt.py (100%) create mode 100644 tests/singly_linked_list_test.py diff --git a/README.md b/README.md index 51450d1..1a5a19b 100644 --- a/README.md +++ b/README.md @@ -48,3 +48,4 @@ Tests python -m tests.graph_algorithms_test python -m tests.heap_test python -m tests.unionfind_test + python -m tests.singly_linked_list_test diff --git a/linear_datastructures/__init__.py b/lists/__init__.py similarity index 100% rename from linear_datastructures/__init__.py rename to lists/__init__.py diff --git a/linear_datastructures/single_linked_list.py b/lists/singlylinkedlist.py similarity index 81% rename from linear_datastructures/single_linked_list.py rename to lists/singlylinkedlist.py index c473b14..d8bd85f 100644 --- a/linear_datastructures/single_linked_list.py +++ b/lists/singlylinkedlist.py @@ -6,12 +6,11 @@ def __init__(self, data=None): def __repr__(self): return str(self.data) -class LinkedList(object): +class SinglyLinkedList(object): def __init__(self, iterable=[]): self.head = None self.size = 0 - for item in iterable: - self.append(item) + for item in iterable: self.append(item) def __repr__(self): (current, nodes) = self.head, [] @@ -66,7 +65,15 @@ def delete(self, data): prev = tmp tmp = tmp.next if found: + self.size -= 1 if prev == None: self.head = self.head.next else: prev.next = tmp.next + +if __name__ == "__main__": + list1 = SinglyLinkedList(range(0, 100, 10)) + print list1 # testing repr + print 50 in list1, 110 not in list1 # testing contains + list1.delete(50) # testing delete + print len(list1) == 9, 50 not in list1 # testing size diff --git a/linear_datastructures/stack-adt.py b/lists/stack-adt.py similarity index 100% rename from linear_datastructures/stack-adt.py rename to lists/stack-adt.py diff --git a/tests/singly_linked_list_test.py b/tests/singly_linked_list_test.py new file mode 100644 index 0000000..6a404f1 --- /dev/null +++ b/tests/singly_linked_list_test.py @@ -0,0 +1,22 @@ +import os, sys +sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) +import unittest +from lists.singlylinkedlist import SinglyLinkedList + +class test_graph(unittest.TestCase): + def setUp(self): + self.tens = SinglyLinkedList(range(0, 100, 10)) + self.blankList = SinglyLinkedList() + + def test_length_method(self): + self.assertEqual(len(self.tens), 10) + self.assertEqual(len(self.blankList), 0) + + def test_add_method(self): + self.blankList.append(50) + self.tens.append(110) + self.assertEqual(len(self.blankList), 1) + self.assertEqual(len(self.tens), 11) + +if __name__ == "__main__": + unittest.main() From 22727af53061b5f355259562920766f52515ee03 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Mon, 10 Aug 2015 16:41:46 +0530 Subject: [PATCH 40/62] Added a queue --- lists/queue.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 lists/queue.py diff --git a/lists/queue.py b/lists/queue.py new file mode 100644 index 0000000..a33fa83 --- /dev/null +++ b/lists/queue.py @@ -0,0 +1,19 @@ +class Queue(object): + def __init__(self): + self.items = [] + + def __str__(self): + return ("Queue of size: %d" % len(self.items)) + + def isEmpty(self): + return len(self.items) == 0 + + def enqueue(self, item): + self.items.insert(0, item) + + def dequeue(self): + return self.items.pop() + + def size(self): + return len(self.items) + From 9a3a4c7b4e6832cb8443c0c85a49d200a6ff65c4 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Tue, 11 Aug 2015 00:25:22 +0530 Subject: [PATCH 41/62] Added a few more DP problems --- dp/coinchange.py | 24 ++++++++++++++++++++++++ dp/kadane.py | 26 ++++++++++++++++++++++++++ misc/kadane.py | 20 -------------------- trees/binarysearchtree.py | 17 ++++++++--------- 4 files changed, 58 insertions(+), 29 deletions(-) create mode 100644 dp/coinchange.py create mode 100644 dp/kadane.py delete mode 100644 misc/kadane.py diff --git a/dp/coinchange.py b/dp/coinchange.py new file mode 100644 index 0000000..1b3a370 --- /dev/null +++ b/dp/coinchange.py @@ -0,0 +1,24 @@ +""" +Problem: http://www.algorithmist.com/index.php/Coin_Change +""" +def coinchange(total, coins): + M = len(coins) + table = [[0]*M for i in range(total+1)] + for i in range(M): + table[0][i] = 1 + + for i in range(1, total+1): + for j in range(M): + # count of solutions excluding coin + x = table[i][j-1] if j > 0 else 0 + + # count of solutions including coin + y = table[i-coins[j]][j] if i - coins[j] >= 0 else 0 + table[i][j] = x + y + + return table[total][M-1] + +if __name__ == "__main__": + print coinchange(10, [2, 3, 5, 6]) # 5 + print coinchange(5, [2, 3, 5]) # 2 + print coinchange(4, [1, 2, 3]) # 4 diff --git a/dp/kadane.py b/dp/kadane.py new file mode 100644 index 0000000..df12645 --- /dev/null +++ b/dp/kadane.py @@ -0,0 +1,26 @@ +""" +The recurrence relation that we solve at each step is the following - + +Let S[i] = be the max value contigous subsequence till the ith element +of the array. + +Then S[i] = max(A[i], A[i] + S[i - 1]) +At each step, we have two options +1) We add the ith element to the sum till the i-1th elem +2) We start a new array starting at i + +We take a max of both these options and accordingly build up the array. +""" +def max_value_contigous_subsequence(arr): + A = [arr[0]] + [0] * (len(arr) - 1) + max_to_here = arr[0] + for i in range(1, len(arr)): + A[i] = max(arr[i], arr[i] + A[i-1]) + max_to_here = max(max_to_here, A[i]) + return max_to_here + +if __name__ == "__main__": + x = [-2, -3, 4, -1, -2, 1, 5, -3] + y = [-2, 1, -3, 4, -1, 2, 1, -5, 4] + z = [-1, 3, -5, 4, 6, -1, 2, -7, 13, -3] + print map(max_value_contigous_subsequence, [x, y, z]) diff --git a/misc/kadane.py b/misc/kadane.py deleted file mode 100644 index 6827a52..0000000 --- a/misc/kadane.py +++ /dev/null @@ -1,20 +0,0 @@ -# The maximum subarray problem is the task of finding the contiguous subarray within a one-dimensional array of numbers which has the largest sum. Kadane's algorithm finds the maximum subarray sum in linear time. -# For example, in the array { -1, 3, -5, 4, 6, -1, 2, -7, 13, -3 }, the maximum subarray sum is 17 (from the highlighted subarray). - -def find_max_subarray(numbers): - max_till_here = [0]*len(numbers) - max_value = 0 - for i in range(len(numbers)): - max_till_here[i] = max(numbers[i], max_till_here[i-1] + numbers[i]) - max_value = max(max_value, max_till_here[i]) - return max_value - -# another version -def find_max_subarray2(numbers): - max_till_here = [numbers[0]] - for n in numbers[1:]: - max_till_here.append(max(n, max_till_here[-1] + n)) - return max(max_till_here) - -print find_max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4]) # 6 -print find_max_subarray([ -1, 3, -5, 4, 6, -1, 2, -7, 13, -3 ]) # 17 diff --git a/trees/binarysearchtree.py b/trees/binarysearchtree.py index 0ff0488..14885c0 100644 --- a/trees/binarysearchtree.py +++ b/trees/binarysearchtree.py @@ -12,14 +12,13 @@ def __init__(self, value): def __repr__(self): return "Node with value - %s" % self.value - class BinarySearchTree(object): def __init__(self): self.root = None self.len = 0 def __len__(self): - return self.len + return self.len def is_empty(self): return self.root == None @@ -41,7 +40,7 @@ def _postorder(self, node, values): self._postorder(node.left, values) self._postorder(node.right, values) values.append(node.value) - + def values(self, reverse = False, order="in"): values = [] if order == "in": @@ -55,7 +54,7 @@ def values(self, reverse = False, order="in"): return values def _search(self, root, value): - if not root or root.value == value: + if not root or root.value == value: return root if value < root.value: return self._search(root.left, value) @@ -80,11 +79,11 @@ def get_min(self): def get_max(self): """ returns the element with the maximum value """ return self._extremes(self.root, find_min=False) - + def successor(self, value): """ returns the successor of the element with value - value""" node = self.find_element(value) - if not node: + if not node: return None if node.right: return self._extremes(node.right, find_min=True) @@ -115,14 +114,14 @@ def insert(self, value): new_node.parent = parent self.len += 1 return - + def delete(self, value): """ deletes a node from tree with value - value """ node = self.find_element(value) if not node: return None if not node.left or not node.right: - node_spliced = node + node_spliced = node else: node_spliced = self.successor(node.value) if node_spliced.left: @@ -137,7 +136,7 @@ def delete(self, value): node_spliced.parent.left = temp_node else: node_spliced.parent.right = temp_node - + if node != node_spliced: node.value = node_spliced.value return node_spliced From e74dd2d2306d3d64c921f5c7d273ded01aa12497 Mon Sep 17 00:00:00 2001 From: Vedang Mehta Date: Tue, 11 Aug 2015 01:03:17 +0530 Subject: [PATCH 42/62] Updated sieve_of_eratosthenes.py Edited according to unit test and changed the loop to list comprehension. --- misc/sieve_of_eratosthenes.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 misc/sieve_of_eratosthenes.py diff --git a/misc/sieve_of_eratosthenes.py b/misc/sieve_of_eratosthenes.py new file mode 100644 index 0000000..03d38e2 --- /dev/null +++ b/misc/sieve_of_eratosthenes.py @@ -0,0 +1,29 @@ +""" + +Implementation of Sieve of Eratosthenes algorithm to generate all the primes upto N. + +Reference : https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + +Algorithm : +* We have a list of numbers from 1 to N. +* Initially, all the numbers are marked as primes. +* We go to every prime number in the list (<= N ^ 1/2) and mark all the multiples of this prime number which are bigger than the number itself as non-primes. + +""" + +from math import sqrt,ceil + +def calculate_primes(n): + bool_array = [True] * (n+1) + bool_array[0] = False + bool_array[1] = False + upper_bound = ceil(sqrt(n)) + for i in range(2,upper_bound): + if bool_array[i]: + for j in range(i*i,n+1,i): + bool_array[j] = False + prime_array = [i for i in range(n+1) if bool_array[i]] + return prime_array + +if __name__ == "__main__": + print(calculate_primes(50)) From b61fa5772e8b3e82687675bc20273ed023f95d49 Mon Sep 17 00:00:00 2001 From: Vedang Mehta Date: Tue, 11 Aug 2015 01:14:50 +0530 Subject: [PATCH 43/62] Unit test for Sieve of Eratosthenes. --- tests/sieve_test.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/sieve_test.py diff --git a/tests/sieve_test.py b/tests/sieve_test.py new file mode 100644 index 0000000..a75dd9c --- /dev/null +++ b/tests/sieve_test.py @@ -0,0 +1,10 @@ +import unittest +from sieve_of_eratosthenes import calculate_primes + +class TestSieveOfEratosthenes(unittest.TestCase): + def test_primes(self): + self.prime_list = [2,3,5,7,11,13,17,19] + self.assertEqual(self.prime_list,calculate_primes(20)) + +if __name__ == '__main__': + unittest.main() From 2ad3bf28e218cbd8a096c2542f1d53c79dfa3262 Mon Sep 17 00:00:00 2001 From: Vedang Mehta Date: Tue, 11 Aug 2015 01:28:22 +0530 Subject: [PATCH 44/62] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1a5a19b..4c3066c 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Completed - Karatsuba Multiplication - Basic Sorting - Rabin-Miller primality test +- Sieve of Eratosthenes for prime numbers - Binary Search - Counting Inversions in an array - Selecting ith order statistic in an array From ae253fe651d67c1650086b84ada47b7631c0b92d Mon Sep 17 00:00:00 2001 From: Vedang Mehta Date: Tue, 11 Aug 2015 18:07:21 +0530 Subject: [PATCH 45/62] Create GCD.py Euclidean algorithm to find the GCD of two numbers. --- misc/GCD.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 misc/GCD.py diff --git a/misc/GCD.py b/misc/GCD.py new file mode 100644 index 0000000..54f27ac --- /dev/null +++ b/misc/GCD.py @@ -0,0 +1,24 @@ +""" +Greatest common divisor(GCD) of two integers X and Y is the largest integer that divides both X and Y. + +References : +https://en.wikipedia.org/wiki/Euclidean_algorithm +https://proofwiki.org/wiki/Euclidean_Algorithm +http://stackoverflow.com/questions/6005582/how-does-the-euclidean-algorithm-work + + +Algorithm : +* If X = 0 then GCD(X,Y) = Y, as GCD(0,Y) = Y. +* If Y = 0 then GCD(X,Y) = X, as GCD(X,0) = X. +* Write X in quotient remainder form (X = Y * Q + R). +* Find GCD(Y,R) using the Euclidean algorithm since GCD(X,Y) = GCD(Y,R). +""" + +def greatest_common_divisor(x,y): + if(y == 0): + return x + else: + return greatest_common_divisor(y,x%y) + +if __name__ == "__main__": + print(greatest_common_divisor(20,25)) From 26c5ae16eea9430462f821028850bca773361c09 Mon Sep 17 00:00:00 2001 From: Vedang Mehta Date: Tue, 11 Aug 2015 18:09:48 +0530 Subject: [PATCH 46/62] Create gcd_test.py Unit test for GCD.py --- tests/gcd_test.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/gcd_test.py diff --git a/tests/gcd_test.py b/tests/gcd_test.py new file mode 100644 index 0000000..87064a5 --- /dev/null +++ b/tests/gcd_test.py @@ -0,0 +1,13 @@ +import unittest +import fractions +import GCD + +class TestEuclideanGCD(unittest.TestCase): + def test_gcd(self): + self.assertEqual(fractions.gcd(30,50),GCD.greatest_common_divisor(30,50)) + self.assertEqual(fractions.gcd(55555,123450),GCD.greatest_common_divisor(55555,123450)) + self.assertEqual(fractions.gcd(-30,-50),GCD.greatest_common_divisor(-30,-50)) + self.assertEqual(fractions.gcd(-1234,1234),GCD.greatest_common_divisor(-1234,1234)) + +if __name__ == "__main__": + unittest.main() From 416d261aca116f9cf7baf5217140d993d05a27d0 Mon Sep 17 00:00:00 2001 From: Vedang Mehta Date: Tue, 11 Aug 2015 18:15:26 +0530 Subject: [PATCH 47/62] Update GCD.py --- misc/GCD.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/misc/GCD.py b/misc/GCD.py index 54f27ac..f1daa47 100644 --- a/misc/GCD.py +++ b/misc/GCD.py @@ -15,10 +15,7 @@ """ def greatest_common_divisor(x,y): - if(y == 0): - return x - else: - return greatest_common_divisor(y,x%y) + return x if y == 0 else greatest_common_divisor(y,x%y) if __name__ == "__main__": print(greatest_common_divisor(20,25)) From a607355e6cf2cd4085fd7563c1bf082afdb0abd0 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Sat, 15 Aug 2015 23:44:34 +0530 Subject: [PATCH 48/62] new code for LIS --- dp/kadane.py | 5 +++++ dp/longest_subsequence.py | 37 +++++++++++++------------------------ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/dp/kadane.py b/dp/kadane.py index df12645..499e8f8 100644 --- a/dp/kadane.py +++ b/dp/kadane.py @@ -1,4 +1,9 @@ """ +Problem: The maximum subarray problem is the task of finding the +contiguous subarray within a one-dimensional array of numbers +(containing at least one positive number) which has the largest sum. + +Solution: The recurrence relation that we solve at each step is the following - Let S[i] = be the max value contigous subsequence till the ith element diff --git a/dp/longest_subsequence.py b/dp/longest_subsequence.py index c037ff8..0c2f060 100644 --- a/dp/longest_subsequence.py +++ b/dp/longest_subsequence.py @@ -1,31 +1,20 @@ def longest_seq(seq): - """ returns the longest increasing subseqence + """ returns the longest increasing subseqence in a sequence """ - count = [1] * len(seq) - prev = [0] * len(seq) - for i in range(1, len(seq)): - dist = [] - temp_prev = {} - for j in range(i): - if seq[j] < seq[i]: - dist.append(count[j]) - temp_prev[count[j]] = j - else: - temp_prev[0] = j - dist.append(0) - count[i] = 1 + max(dist) - prev[i] = temp_prev[max(dist)] - - # path - path = [seq[prev.index(max(prev))]] - i = prev.index(max(prev)) - while i>1: - path.append(seq[prev[i]]) - i = prev[i] - return max(count), path[::-1] + cache = [1] * len(nums) + solution = [0] * len(nums) + + for i in range(1, len(nums)): + for j in range(0, i): + if nums[i] > nums[j]: + if cache[j] + 1 > cache[i]: + cache[i] = cache[j] + 1 + solution[i] = j + return max(cache), solution if __name__ == "__main__": seq = [5, 2, 8, 10, 3, 6, 9, 7] seq2 = [0, 8, 3, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] - print longest_seq(seq2) + seq3 = [10, 22, 9, 33, 21, 50, 41, 60, 80] + print longest_seq(seq3) From b81d2ca82023f27d8f4ae43230490ae915329f18 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Sun, 16 Aug 2015 09:52:11 +0530 Subject: [PATCH 49/62] Updated code. Fixes #7 --- dp/longest_subsequence.py | 45 ++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/dp/longest_subsequence.py b/dp/longest_subsequence.py index 0c2f060..e1ac1d2 100644 --- a/dp/longest_subsequence.py +++ b/dp/longest_subsequence.py @@ -1,20 +1,45 @@ -def longest_seq(seq): - """ returns the longest increasing subseqence - in a sequence """ +""" +Problem: http://www.geeksforgeeks.org/dynamic-programming-set-3-longest-increasing-subsequence/ +""" +def longest_increasing_subsequence(nums): + # array used to store the length of the longest subsequence found cache = [1] * len(nums) - solution = [0] * len(nums) + + # array used to store the location of the predecessor in the longest + # subsequence. -1 by default + location = [-1] * len(nums) for i in range(1, len(nums)): for j in range(0, i): if nums[i] > nums[j]: if cache[j] + 1 > cache[i]: cache[i] = cache[j] + 1 - solution[i] = j - return max(cache), solution + location[i] = j + + # finding the max in the cache gives us the + # answer - i.e. length of the LIS + max_value = max(cache) + + # with the answer in hand, we need to build the solution + # using the locations stored + solution = [] + i = cache.index(max_value) + + # we start with the max value i.e. the index of the + # location where the max LIS exists and then + # keep backtracking to build up the solution + while location[i] > -1: + solution.append(nums[i]) + i = location[i] + + # when the loop ends, just append the starting element + solution.append(nums[i]) + + # return the length of the LIS and the solution (in reverse) + return max_value, solution[::-1] if __name__ == "__main__": - seq = [5, 2, 8, 10, 3, 6, 9, 7] - seq2 = [0, 8, 3, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] - seq3 = [10, 22, 9, 33, 21, 50, 41, 60, 80] - print longest_seq(seq3) + assert longest_increasing_subsequence([3, 4, -1, 0, 6, 2, 3]) == (4, [-1, 0, 2, 3]) + assert longest_increasing_subsequence([10, 22, 9, 33, 21, 50, 41, 60, 80]) == (6, [10, 22, 33, 50, 60, 80]) + assert longest_increasing_subsequence([5,0,1,2,3,4,5,6,7,8,9,10,11,12, 2, 8, 10, 3, 6, 9, 7]) == (13, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) From 78089ba1b64695c43cce9ae70c2c28bd5563a920 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Mon, 17 Aug 2015 08:52:16 +0530 Subject: [PATCH 50/62] Fixed wrong tests in spanning trees --- graphs/graph.py | 2 +- tests/graph_algorithms_test.py | 37 +++++++++++++--------------------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/graphs/graph.py b/graphs/graph.py index fb2073e..10364b4 100644 --- a/graphs/graph.py +++ b/graphs/graph.py @@ -4,7 +4,7 @@ class graph(object): methods: add_edge, add_edges, add_node, add_nodes, has_node, has_edge, nodes, edges, neighbors, del_node, del_edge, node_order, - set_edge_weight, get_edge_weight, + set_edge_weight, get_edge_weight """ DEFAULT_WEIGHT = 1 diff --git a/tests/graph_algorithms_test.py b/tests/graph_algorithms_test.py index 28d95a4..dba26c4 100644 --- a/tests/graph_algorithms_test.py +++ b/tests/graph_algorithms_test.py @@ -63,7 +63,7 @@ def test_topological_ordering(self): dag.add_edges([("a", "b"), ("a", "c"), ("a", "e"), ("d", "a")]) dag.add_edges([("g", "b"), ("g", "f"), ("f", "e"), ("h", "f"), ("h", "a")]) order = {o[0]: o[1] for o in topological_ordering(dag)} - self.assertEqual(sum([order[u] < order[v] for (u, v) in + self.assertEqual(sum([order[u] < order[v] for (u, v) in dag.edges()]), len(dag.edges())) # all comparisons are True def test_directed_connected_components(self): @@ -86,7 +86,7 @@ def test_shortest_path_in_directed_graph(self): digr.add_nodes(["a", "b", "c", "d", "e", "f"]) digr.add_edge(("a", "b"), 7) digr.add_edge(("a", "c"), 9) - digr.add_edge(("a", "f"), 14) + digr.add_edge(("a", "f"), 14) digr.add_edge(("f", "e"), 9) digr.add_edge(("c", "f"), 2) digr.add_edge(("c", "d"), 11) @@ -101,33 +101,24 @@ def test_shortest_path_in_directed_graph(self): self.assertEqual(shortest_path(digr, "a")["f"], 11) def test_prims_minimum_spanning_tree(self): - lines = [l for l in open("tests/edges.txt")] - lines = lines[1:] - edges = (l.split() for l in lines) gr = graph() - for (u, v, w) in edges: - if u not in gr.nodes(): - gr.add_node(u) - if v not in gr.nodes(): - gr.add_node(v) - gr.add_edge( (u, v), int(w) ) - + gr.add_nodes(["a", "b", "c", "d"]) + gr.add_edge(("a", "b"), 4) + gr.add_edge(("b", "c"), 3) + gr.add_edge(("a", "c"), 1) + gr.add_edge(("c", "d"), 2) min_cost = minimum_spanning_tree(gr) - self.assertEqual(min_cost, 39) + self.assertEqual(min_cost, 6) def test_kruskals_minimum_spanning_tree(self): - lines = [l for l in open("tests/edges.txt")] - lines = lines[1:] - edges = (l.split() for l in lines) gr = graph() - for (u, v, w) in edges: - if u not in gr.nodes(): - gr.add_node(u) - if v not in gr.nodes(): - gr.add_node(v) - gr.add_edge( (u, v), int(w) ) + gr.add_nodes(["a", "b", "c", "d"]) + gr.add_edge(("a", "b"), 4) + gr.add_edge(("b", "c"), 3) + gr.add_edge(("a", "c"), 1) + gr.add_edge(("c", "d"), 2) min_cost = kruskal_MST(gr) - self.assertEqual(min_cost, 39) + self.assertEqual(min_cost, 6) if __name__ == "__main__": unittest.main() From 5ed4fcb1e532bfeceadc7401ecd13137fe9a0cce Mon Sep 17 00:00:00 2001 From: Vedang Mehta Date: Mon, 17 Aug 2015 20:24:16 +0530 Subject: [PATCH 51/62] Create shuffle.py --- misc/shuffle.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 misc/shuffle.py diff --git a/misc/shuffle.py b/misc/shuffle.py new file mode 100644 index 0000000..2da3d52 --- /dev/null +++ b/misc/shuffle.py @@ -0,0 +1,26 @@ +""" +Fisher-Yates shuffle algorithm implemented in Python. + + +Reference : +https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle +http://www.geeksforgeeks.org/shuffle-a-given-array/ + +Algorithm: +For all N indices of list, swap the element at a given index i with a random number 0 <= j < i. +""" + +from random import randint + +def shuffle(arr): + """ + Shuffle a list. + """ + for i in range(0,len(arr)): + r = randint(0,i) + arr[i],arr[r] = arr[r],arr[i] + +if __name__ == '__main__': + arr = [1,2,3,4,5,6] + shuffle(arr) + print(arr) From 7f6c6524e1a3509d7e555a5ada274d4944c6113a Mon Sep 17 00:00:00 2001 From: Vedang Mehta Date: Mon, 17 Aug 2015 20:41:08 +0530 Subject: [PATCH 52/62] Update shuffle.py --- misc/shuffle.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/shuffle.py b/misc/shuffle.py index 2da3d52..ee039c5 100644 --- a/misc/shuffle.py +++ b/misc/shuffle.py @@ -1,13 +1,12 @@ """ Fisher-Yates shuffle algorithm implemented in Python. - Reference : https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle http://www.geeksforgeeks.org/shuffle-a-given-array/ Algorithm: -For all N indices of list, swap the element at a given index i with a random number 0 <= j < i. +For all N indices of list, swap the element at a given index i with the element at a random index j where 0 <= j <= i. """ from random import randint From f30f609910ebd888a47b37992884ca55fe360964 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Wed, 9 Sep 2015 12:41:05 -0400 Subject: [PATCH 53/62] refactored sieve of eratosthenes --- misc/sieve_of_eratosthenes.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/misc/sieve_of_eratosthenes.py b/misc/sieve_of_eratosthenes.py index 03d38e2..905b3b5 100644 --- a/misc/sieve_of_eratosthenes.py +++ b/misc/sieve_of_eratosthenes.py @@ -1,5 +1,4 @@ """ - Implementation of Sieve of Eratosthenes algorithm to generate all the primes upto N. Reference : https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes @@ -7,23 +6,20 @@ Algorithm : * We have a list of numbers from 1 to N. * Initially, all the numbers are marked as primes. -* We go to every prime number in the list (<= N ^ 1/2) and mark all the multiples of this prime number which are bigger than the number itself as non-primes. - +* We go to every prime number in the list (<= N ^ 1/2) and mark all the multiples + of this prime number which are bigger than the number itself as non-primes. """ from math import sqrt,ceil -def calculate_primes(n): - bool_array = [True] * (n+1) - bool_array[0] = False - bool_array[1] = False - upper_bound = ceil(sqrt(n)) - for i in range(2,upper_bound): - if bool_array[i]: - for j in range(i*i,n+1,i): - bool_array[j] = False - prime_array = [i for i in range(n+1) if bool_array[i]] - return prime_array +def generate_primes(n): + bool_array = [False, False] + [True] * n # start with all values as True, except 0 and 1 + for i in range(2, int(ceil(sqrt(n)))): # only go to till square root of n + if bool_array[i]: # if the number is marked as prime + for j in range(i*i,n+1,i): # iterate through all its multiples + bool_array[j] = False # and mark them as False + primes = [i for i in range(n+1) if bool_array[i]] # return all numbers which are marked as True + return primes if __name__ == "__main__": - print(calculate_primes(50)) + print(generate_primes(50)) From cb0f2e8791027faec1eb7360cf75f8df64a50e74 Mon Sep 17 00:00:00 2001 From: Prakhar Srivastav Date: Wed, 9 Sep 2015 12:42:53 -0400 Subject: [PATCH 54/62] Update sieve_of_eratosthenes.py --- misc/sieve_of_eratosthenes.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/misc/sieve_of_eratosthenes.py b/misc/sieve_of_eratosthenes.py index 905b3b5..03cb2a6 100644 --- a/misc/sieve_of_eratosthenes.py +++ b/misc/sieve_of_eratosthenes.py @@ -1,13 +1,11 @@ """ Implementation of Sieve of Eratosthenes algorithm to generate all the primes upto N. -Reference : https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes - Algorithm : -* We have a list of numbers from 1 to N. -* Initially, all the numbers are marked as primes. -* We go to every prime number in the list (<= N ^ 1/2) and mark all the multiples - of this prime number which are bigger than the number itself as non-primes. + * We have a list of numbers from 1 to N. + * Initially, all the numbers are marked as primes. + * We go to every prime number in the list (<= N ^ 1/2) and mark all the multiples + of this prime number which are bigger than the number itself as non-primes. """ from math import sqrt,ceil @@ -20,6 +18,3 @@ def generate_primes(n): bool_array[j] = False # and mark them as False primes = [i for i in range(n+1) if bool_array[i]] # return all numbers which are marked as True return primes - -if __name__ == "__main__": - print(generate_primes(50)) From ed8e6d8419f7a2891599de7fb1ea8e27cc861635 Mon Sep 17 00:00:00 2001 From: vedangmehta Date: Sun, 24 Jan 2016 23:26:57 +0530 Subject: [PATCH 55/62] Added LCS and updated README --- README.md | 3 +++ dp/lcs.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 dp/lcs.py diff --git a/README.md b/README.md index 1a5a19b..851408c 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Completed - Karatsuba Multiplication - Basic Sorting - Rabin-Miller primality test +- Sieve of Eratosthenes for prime numbers - Binary Search - Counting Inversions in an array - Selecting ith order statistic in an array @@ -34,6 +35,8 @@ Completed - Binary Search Tree - Kandane's Algorithm - Knapsack Problem (0/1 and unbounded) +- Longest Increasing Subsequence +- Longest Common Subsequence - Prefix Tries - Stack ADT (with example problems) - String Reverse diff --git a/dp/lcs.py b/dp/lcs.py new file mode 100644 index 0000000..ae333d8 --- /dev/null +++ b/dp/lcs.py @@ -0,0 +1,34 @@ +""" +Problem : https://en.wikipedia.org/wiki/Longest_common_subsequence_problem +""" + +def longest_common_subsequence(s1, s2): + # lengths of strings s1 and s2 + m, n = len(s1), len(s2) + # to cache the results + cache = [[0 for j in range(n + 1)] for i in range(m + 1)] + for i, character_s1 in enumerate(s1): + for j, character_s2 in enumerate(s2): + if character_s1 == character_s2: + cache[i + 1][j + 1] = cache[i][j] + 1 + else: + cache[i + 1][j + 1] = max(cache[i][j + 1], cache[i + 1][j]) + # LCS is empty by default + sequence = "" + i, j = m, n + # finding the sequence from cache + while i >= 1 and j >= 1: + if s1[i - 1] == s2[j - 1]: + sequence += s1[i - 1] + i, j = i - 1, j - 1 + elif cache[i - 1][j] > cache[i][j - 1]: + i -= 1 + else: + j -= 1 + # returns the length of LCS along with the sequence itself + return (len(sequence), sequence[::-1]) + +if __name__ == "__main__": + assert longest_common_subsequence("ABCD", "BBDABXYDCCAD") == (4, "ABCD") + assert longest_common_subsequence("BANANA", "ATANA") == (4, "AANA") + assert longest_common_subsequence("ABCDEFG", "BDGK") == (3, "BDG") From 5665afe7be13ce56f17d00db24a9290adbfe5058 Mon Sep 17 00:00:00 2001 From: vedangmehta Date: Sun, 24 Jan 2016 23:53:32 +0530 Subject: [PATCH 56/62] Added test for LCS --- tests/lcs_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/lcs_test.py diff --git a/tests/lcs_test.py b/tests/lcs_test.py new file mode 100644 index 0000000..9e1ed15 --- /dev/null +++ b/tests/lcs_test.py @@ -0,0 +1,11 @@ +import unittest +import lcs + +class TestLCS(unittest.TestCase): + def test_lcs(self): + self.assertEqual(lcs.longest_common_subsequence("ABCD", "BBDABXYDCCAD"), (4, "ABCD")) + self.assertEqual(lcs.longest_common_subsequence("BANANA", "ATANA"), (4, "AANA")) + self.assertEqual(lcs.longest_common_subsequence("ABCDEFG", "BDGK"), (3, "BDG")) + +if __name__ == "__main__": + unittest.main() From f20e5ce73a54429b9f1746f4d605b51884cf164b Mon Sep 17 00:00:00 2001 From: vedangmehta Date: Mon, 25 Jan 2016 00:00:29 +0530 Subject: [PATCH 57/62] moved test cases to /tests --- dp/lcs.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dp/lcs.py b/dp/lcs.py index ae333d8..35c0056 100644 --- a/dp/lcs.py +++ b/dp/lcs.py @@ -29,6 +29,4 @@ def longest_common_subsequence(s1, s2): return (len(sequence), sequence[::-1]) if __name__ == "__main__": - assert longest_common_subsequence("ABCD", "BBDABXYDCCAD") == (4, "ABCD") - assert longest_common_subsequence("BANANA", "ATANA") == (4, "AANA") - assert longest_common_subsequence("ABCDEFG", "BDGK") == (3, "BDG") + print(longest_common_subsequence("ABCXYZ","ACBCXZ")) From 2f2cdf3f6b82fe761050629f37304ef1c869592b Mon Sep 17 00:00:00 2001 From: Shank Date: Mon, 26 Sep 2016 22:46:42 +0530 Subject: [PATCH 58/62] modify Queue to use collections.deque instead of a list --- lists/queue.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lists/queue.py b/lists/queue.py index a33fa83..e6883f1 100644 --- a/lists/queue.py +++ b/lists/queue.py @@ -1,6 +1,12 @@ +from collections import deque + + class Queue(object): + """Wrapper around collections.deque to provide the api consistent with + a Queue""" + def __init__(self): - self.items = [] + self.items = deque() def __str__(self): return ("Queue of size: %d" % len(self.items)) @@ -9,11 +15,10 @@ def isEmpty(self): return len(self.items) == 0 def enqueue(self, item): - self.items.insert(0, item) + self.items.append(item) def dequeue(self): - return self.items.pop() + return self.items.popleft() def size(self): return len(self.items) - From 385d753db0a80d7862840af66e980ec99c638919 Mon Sep 17 00:00:00 2001 From: manishrw Date: Sat, 15 Oct 2016 01:03:15 +0530 Subject: [PATCH 59/62] ADDED modular exponentiation in misc --- README.md | 2 ++ misc/__init__.py | 0 misc/modular_exponentiation.py | 22 ++++++++++++++++++++++ tests/modular_exponentiation_test.py | 16 ++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 misc/__init__.py create mode 100644 misc/modular_exponentiation.py create mode 100644 tests/modular_exponentiation_test.py diff --git a/README.md b/README.md index 851408c..1857fde 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Completed - String Reverse - Parenthesis Matching - Infix to Postfix +- Modular exponentiation Tests @@ -52,3 +53,4 @@ Tests python -m tests.heap_test python -m tests.unionfind_test python -m tests.singly_linked_list_test + python -m tests.modular_exponentiation_test diff --git a/misc/__init__.py b/misc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/misc/modular_exponentiation.py b/misc/modular_exponentiation.py new file mode 100644 index 0000000..43c66ee --- /dev/null +++ b/misc/modular_exponentiation.py @@ -0,0 +1,22 @@ +""" +Problem: https://en.wikipedia.org/wiki/Modular_exponentiation +""" + +def modular_exponentiation(base, exp, mod): + if exp < 1: + raise ValueError("Exponentiation should be ve+ int") + if mod == 1: + return 0 + elif mod < 1: + raise ValueError("Modulus should be ve+ int") + #Initialize result to 1 + result = 1 + base %= mod + while exp > 0: + #multiply base to result if exp is odd + if exp % 2 == 1: + result = (result * base) % mod + #Double base and half exp + exp = exp >> 1 + base = (base ** 2) % mod + return result \ No newline at end of file diff --git a/tests/modular_exponentiation_test.py b/tests/modular_exponentiation_test.py new file mode 100644 index 0000000..8ab9f68 --- /dev/null +++ b/tests/modular_exponentiation_test.py @@ -0,0 +1,16 @@ +import os, sys +import unittest +sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) +from misc import modular_exponentiation as me + +class TestLCS(unittest.TestCase): + def test_modular_exponentiation(self): + self.assertEqual(me.modular_exponentiation(2, 10, 100), 24) + self.assertEqual(me.modular_exponentiation(2, 200, 10), 6) + self.assertEqual(me.modular_exponentiation(5, 20, 1), 0) + #self.assertEqual(me.modular_exponentiation(8, 1, 10), 8) + self.assertRaises(ValueError, me.modular_exponentiation, 12, -1, 10) + self.assertRaises(ValueError, me.modular_exponentiation, 12, 5, 0) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From 18a09aa04fc373b6c5fe443382eecfe5d2f757b5 Mon Sep 17 00:00:00 2001 From: manishrw Date: Sun, 16 Oct 2016 01:03:25 +0530 Subject: [PATCH 60/62] Added Modular multiplicative inverse algo Added test case for the same --- README.md | 2 + misc/modular_multiplicative_inverse.py | 42 ++++++++++++++++++++ tests/modular_multiplicative_inverse_test.py | 16 ++++++++ 3 files changed, 60 insertions(+) create mode 100644 misc/modular_multiplicative_inverse.py create mode 100644 tests/modular_multiplicative_inverse_test.py diff --git a/README.md b/README.md index 1857fde..0593178 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Completed - Parenthesis Matching - Infix to Postfix - Modular exponentiation +- Modular multiplicative inverse Tests @@ -54,3 +55,4 @@ Tests python -m tests.unionfind_test python -m tests.singly_linked_list_test python -m tests.modular_exponentiation_test + python -m tests.modular_multiplicative_inverse_test \ No newline at end of file diff --git a/misc/modular_multiplicative_inverse.py b/misc/modular_multiplicative_inverse.py new file mode 100644 index 0000000..36dd550 --- /dev/null +++ b/misc/modular_multiplicative_inverse.py @@ -0,0 +1,42 @@ +""" +Problem: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse +""" +import GCD as gcd + +def modular_multiplicative_inv(a, m): + if m == 1: + return 0 + if m < 1: + raise ValueError('Modulus should be ve+ int > 0') + # check for co-prime condition + if gcd.greatest_common_divisor(a, m) != 1: + raise ValueError('a and m are not co-primes') + + # Make var "a" positive if it's negative + if a < 0: + a %= m + + # Initialise vars + m0 = m + x0 = 0 + x1 = 1 + + while a > 1: + # Calculate quotient q; store m into temp t + q = a / m + t = m + + # Calculate m as remainder(a, m); store temp t into a + m = a % m + a = t + + # Assign x0 into temp t; Calculate x0 and store temp t into x1 + t = x0 + x0 = x1 - q * x0 + x1 = t + + # If x1 is negative then add modulus m0 + if x1 < 0: + x1 += m0 + + return x1 \ No newline at end of file diff --git a/tests/modular_multiplicative_inverse_test.py b/tests/modular_multiplicative_inverse_test.py new file mode 100644 index 0000000..165eeaf --- /dev/null +++ b/tests/modular_multiplicative_inverse_test.py @@ -0,0 +1,16 @@ +import os, sys +import unittest +sys.path.append(os.path.join(os.getcwd(), os.path.pardir)) +from misc import modular_multiplicative_inverse as mmi + +class TestLCS(unittest.TestCase): + def test_modular_multiplicative_inverse(self): + self.assertEqual(mmi.modular_multiplicative_inv(10, 7), 5) + self.assertEqual(mmi.modular_multiplicative_inv(45, 13), 11) + self.assertEqual(mmi.modular_multiplicative_inv(52, 1), 0) + + self.assertRaises(ValueError, mmi.modular_multiplicative_inv, 12, -1) + self.assertRaises(ValueError, mmi.modular_multiplicative_inv, 12, 2) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From fb232f75e9f6ad87b6084dbbea5e8a0949f0a59e Mon Sep 17 00:00:00 2001 From: ayush Date: Tue, 25 Oct 2016 19:06:13 +0530 Subject: [PATCH 61/62] fixed bug in binarysearchtree.py --- trees/binarysearchtree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trees/binarysearchtree.py b/trees/binarysearchtree.py index 14885c0..fb573e3 100644 --- a/trees/binarysearchtree.py +++ b/trees/binarysearchtree.py @@ -100,13 +100,13 @@ def insert(self, value): else: node = self.root while node and node.value != value: + if node.value == value: + return parent = node if node.value < value: node = node.right else: node = node.left - if node.value == value: - return if parent.value > value: parent.left = new_node else: From 36c8cae34ac5cb2c08dd8ffc333dee83809fac59 Mon Sep 17 00:00:00 2001 From: nitikataneja Date: Sun, 30 Oct 2016 18:54:50 +0530 Subject: [PATCH 62/62] readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0593178..6b3577f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Algos in Python +Algorithms in Python ====== Implementations of a few algorithms and datastructures for fun and profit! @@ -55,4 +55,4 @@ Tests python -m tests.unionfind_test python -m tests.singly_linked_list_test python -m tests.modular_exponentiation_test - python -m tests.modular_multiplicative_inverse_test \ No newline at end of file + python -m tests.modular_multiplicative_inverse_test