From 8784facc47f284ef14a6acd81b0a713da25bde39 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 28 Oct 2020 17:55:21 -0600 Subject: [PATCH 01/19] filled out DJB2 hasher --- hashtable/hashtable.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index 0205f0ba9..0f47f43c0 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -21,7 +21,9 @@ class HashTable: """ def __init__(self, capacity): - # Your code here + self.capacity = capacity + self.table = [None] * capacity + self.count = 0 def get_num_slots(self): @@ -34,7 +36,8 @@ def get_num_slots(self): Implement this. """ - # Your code here + return len(self.table) + def get_load_factor(self): @@ -43,7 +46,7 @@ def get_load_factor(self): Implement this. """ - # Your code here + return self.count / self.get_num_slots() def fnv1(self, key): @@ -62,7 +65,10 @@ def djb2(self, key): Implement this, and/or FNV-1. """ - # Your code here + hash = 5381 + for s in key: + hash = ((hash << 5) + hash) + ord(s) + return hash def hash_index(self, key): From 6489ba0bcffd1fa0a97d7614f50f727725a9d22d Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 28 Oct 2020 18:56:35 -0600 Subject: [PATCH 02/19] implemented get, delete and resize --- hashtable/hashtable.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index 0f47f43c0..fbb1b84f6 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -57,6 +57,7 @@ def fnv1(self, key): """ # Your code here + pass def djb2(self, key): @@ -88,6 +89,10 @@ def put(self, key, value): Implement this. """ # Your code here + index = self.hash_index(key) + self.table[index] = HashTableEntry(key, value) + self.count += 1 + def delete(self, key): @@ -99,6 +104,14 @@ def delete(self, key): Implement this. """ # Your code here + delete_at_index = self.hash_index(key) + + if self.table[delete_at_index] is None: + print("No key found") + return + + self.table[delete_at_index] = None + self.count -= 1 def get(self, key): @@ -110,6 +123,9 @@ def get(self, key): Implement this. """ # Your code here + index = self.hash_index(key) + + return self.table[index].value def resize(self, new_capacity): @@ -120,6 +136,16 @@ def resize(self, new_capacity): Implement this. """ # Your code here + old_table = self.table + self.table = [None] * new_capacity + self.capacity = new_capacity + + for entry in old_table: + while entry is not None: + old_key = entry.key + old_value = entry.value + index = self.hash_index(old_key) + self.table[index] = HashTableEntry(old_key, old_value) From 590910a7fd0da4af7f9fedd7095addd8c870e8e9 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Thu, 29 Oct 2020 21:07:45 -0600 Subject: [PATCH 03/19] updated hash_table --- hashtable/hashtable.py | 64 ++++++++++++++++++------------ hashtable/linkedList.py | 78 +++++++++++++++++++++++++++++++++++++ hashtable/test_hashtable.py | 4 +- 3 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 hashtable/linkedList.py diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index fbb1b84f6..5181a54b7 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -23,7 +23,7 @@ class HashTable: def __init__(self, capacity): self.capacity = capacity self.table = [None] * capacity - self.count = 0 + # self.count = 0 def get_num_slots(self): @@ -36,7 +36,8 @@ def get_num_slots(self): Implement this. """ - return len(self.table) + # return len(self.table) + pass @@ -46,7 +47,7 @@ def get_load_factor(self): Implement this. """ - return self.count / self.get_num_slots() + # return self.count / self.get_num_slots() def fnv1(self, key): @@ -69,7 +70,7 @@ def djb2(self, key): hash = 5381 for s in key: hash = ((hash << 5) + hash) + ord(s) - return hash + return hash & 0xFFFFFFFF def hash_index(self, key): @@ -89,9 +90,17 @@ def put(self, key, value): Implement this. """ # Your code here - index = self.hash_index(key) - self.table[index] = HashTableEntry(key, value) - self.count += 1 + # index = self.hash_index(key) + # entry = self.table[index] + + # if self.count == (len(self.table)): + # self.resize(len(self.table) + 1) + + # if entry is None: + # self.table[index] = HashTableEntry(key, value) + # self.count += 1 + + self.table[self.hash_index(key)] = value @@ -104,14 +113,19 @@ def delete(self, key): Implement this. """ # Your code here - delete_at_index = self.hash_index(key) + # delete_at_index = self.hash_index(key) + + # if self.table[delete_at_index] is None: + # print("No key found") + # return - if self.table[delete_at_index] is None: - print("No key found") - return + # self.table[delete_at_index] = None + # self.count -= 1 - self.table[delete_at_index] = None - self.count -= 1 + value = self.table[self.hash_index(key)] + if value == None: + print("No value for index") + self.table[self.hash_index(key)] = None def get(self, key): @@ -123,9 +137,9 @@ def get(self, key): Implement this. """ # Your code here - index = self.hash_index(key) + # index = self.hash_index(key) - return self.table[index].value + return self.table[self.hash_index(key)] def resize(self, new_capacity): @@ -136,16 +150,16 @@ def resize(self, new_capacity): Implement this. """ # Your code here - old_table = self.table - self.table = [None] * new_capacity - self.capacity = new_capacity - - for entry in old_table: - while entry is not None: - old_key = entry.key - old_value = entry.value - index = self.hash_index(old_key) - self.table[index] = HashTableEntry(old_key, old_value) + # old_table = self.table + # self.table = [None] * new_capacity + # self.capacity = new_capacity + + # for entry in old_table: + # while entry is not None: + # old_key = entry.key + # old_value = entry.value + # index = self.hash_index(old_key) + # self.table[index] = HashTableEntry(old_key, old_value) diff --git a/hashtable/linkedList.py b/hashtable/linkedList.py new file mode 100644 index 000000000..2bb545ffa --- /dev/null +++ b/hashtable/linkedList.py @@ -0,0 +1,78 @@ +class Node: + def __init__(self, value): + self.value = value + self.next = None + +class LinkedList: + def __init__(self): + self.head = None + + def __repr__(self): + currStr = "" + curr = self.head + while curr != None: + currStr += f"{str(curr.value)} ->" + curr = curr.next + return currStr + + def find(self, value): + # return node with value + curr = self.head + + #walk through the LL and check the value + while curr != None: + if curr.value == value: + return curr + curr = curr.next + + return None + + def delete(self, value): + curr = self.head + + # special case for remove head + if curr.value == value: + self.head = curr.next + curr.next = None + return curr + + prev = None + + while curr != None: + if curr.value == value: + prev.next = curr.next + curr.next = None + return curr + else: + prev = curr + curr = curr.next + + return None + + # insert node at head of list + def add_to_head(self, node): + node.next = self.head + self.head = node + + # overwrite node or insert node at head + def insert_at_head_or_overwrite(self, node): + existing_node = self.find(node.value) + if existing_node != None: + existing_node.value = node.value + else: + self.add_to_head(node) + + +a = Node(1) +b = Node(2) +c = Node(3) + +ll = LinkedList() + +ll.add_to_head(c) +ll.add_to_head(b) +ll.add_to_head(a) +ll.insert_at_head_or_overwrite(c) +ll.delete(2) + +print(ll) \ No newline at end of file diff --git a/hashtable/test_hashtable.py b/hashtable/test_hashtable.py index 12ae9e388..2ef35ad42 100644 --- a/hashtable/test_hashtable.py +++ b/hashtable/test_hashtable.py @@ -147,9 +147,9 @@ def test_hash_table_removes_correctly(self): return_value = ht.get("key-7") self.assertTrue(return_value is None) return_value = ht.get("key-8") - self.assertTrue(return_value is "val-8") + self.assertTrue(return_value == "val-8") return_value = ht.get("key-9") - self.assertTrue(return_value is "val-9") + self.assertTrue(return_value == "val-9") ht.delete("key-9") ht.delete("key-8") From 94f99f5d6b1010352a5b7807562ea2c20300a149 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 1 Nov 2020 09:11:37 -0700 Subject: [PATCH 04/19] added LinkedList and put method --- hashtable/hashtable.py | 133 +++++++++++++++++++++++++--------------- hashtable/linkedList.py | 20 +++--- 2 files changed, 95 insertions(+), 58 deletions(-) diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index 5181a54b7..25ba09005 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -1,12 +1,66 @@ -class HashTableEntry: - """ - Linked List hash table key/value pair - """ - def __init__(self, key, value): - self.key = key - self.value = value - self.next = None - +class Node: + def __init__(self, value): + self.value = value + self.next = None + +class LinkedList: + def __init__(self): + self.head = None + + def __repr__(self): + currStr = "" + curr = self.head + while curr != None: + currStr += f"{str(curr.value)} ->" + curr = curr.next + return currStr + + def find(self, value): + # return node with value + curr = self.head + + #walk through the LL and check the value + while curr != None: + if curr.value == value: + return curr + curr = curr.next + + return None + + def delete(self, value): + curr = self.head + + # special case for remove head + if curr.value == value: + self.head = curr.next + curr.next = None + return curr + + prev = None + + while curr != None: + if curr.value == value: + prev.next = curr.next + curr.next = None + return curr + else: + prev = curr + curr = curr.next + + return None + + # insert node at head of list + def add_to_head(self, node): + node.next = self.head + self.head = node + + # overwrite node or insert node at head + def insert_at_head_or_overwrite(self, node): + existing_node = self.find(node.value) + if existing_node != None: + existing_node.value = node.value + else: + self.add_to_head(node) # Hash table can't have fewer than this many slots MIN_CAPACITY = 8 @@ -23,7 +77,7 @@ class HashTable: def __init__(self, capacity): self.capacity = capacity self.table = [None] * capacity - # self.count = 0 + self.count = 0 def get_num_slots(self): @@ -36,8 +90,7 @@ def get_num_slots(self): Implement this. """ - # return len(self.table) - pass + return len(self.table) @@ -47,26 +100,14 @@ def get_load_factor(self): Implement this. """ - # return self.count / self.get_num_slots() + return self.count / self.get_num_slots() def fnv1(self, key): - """ - FNV-1 Hash, 64-bit - - Implement this, and/or DJB2. - """ - - # Your code here pass def djb2(self, key): - """ - DJB2 hash, 32-bit - - Implement this, and/or FNV-1. - """ hash = 5381 for s in key: hash = ((hash << 5) + hash) + ord(s) @@ -74,33 +115,29 @@ def djb2(self, key): def hash_index(self, key): - """ - Take an arbitrary key and return a valid integer index - between within the storage capacity of the hash table. - """ - #return self.fnv1(key) % self.capacity return self.djb2(key) % self.capacity def put(self, key, value): - """ - Store the value with the given key. - - Hash collisions should be handled with Linked List Chaining. - - Implement this. - """ - # Your code here - # index = self.hash_index(key) - # entry = self.table[index] - - # if self.count == (len(self.table)): - # self.resize(len(self.table) + 1) - - # if entry is None: - # self.table[index] = HashTableEntry(key, value) - # self.count += 1 + # hash the key and get the index + index = self.hash_index(key) + # check if the table at index is empty + if self.table[index] == None: + # if it is -> initialize new LinkedList + ll = LinkedList() + # set table at index to newly created LL + self.table[index] = ll + # add created node to the head of the LL + ll.add_to_head(Node(value)) + # increase count + self.count += 1 + else: + # if table at index already has a LL, get reference to current LL + curr_ll: LinkedList = self.table[index] + # set the new Node as the head + curr_ll.add_to_head(Node(value)) + # increase count + self.count += 1 - self.table[self.hash_index(key)] = value diff --git a/hashtable/linkedList.py b/hashtable/linkedList.py index 2bb545ffa..49c0e1f93 100644 --- a/hashtable/linkedList.py +++ b/hashtable/linkedList.py @@ -63,16 +63,16 @@ def insert_at_head_or_overwrite(self, node): self.add_to_head(node) -a = Node(1) -b = Node(2) -c = Node(3) +# a = Node(1) +# b = Node(2) +# c = Node(3) -ll = LinkedList() +# ll = LinkedList() -ll.add_to_head(c) -ll.add_to_head(b) -ll.add_to_head(a) -ll.insert_at_head_or_overwrite(c) -ll.delete(2) +# ll.add_to_head(c) +# ll.add_to_head(b) +# ll.add_to_head(a) +# ll.insert_at_head_or_overwrite(c) +# ll.delete(2) -print(ll) \ No newline at end of file +# print(ll) \ No newline at end of file From 457b16cf34f645ac06df4308c2a5f5f96b1f1b26 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 1 Nov 2020 20:31:02 -0700 Subject: [PATCH 05/19] Added delete and check_and_resize methods --- hashtable/hashtable.py | 97 +++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 59 deletions(-) diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index 25ba09005..25abaa5e2 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -1,6 +1,7 @@ class Node: - def __init__(self, value): + def __init__(self, value, key): self.value = value + self.key = key self.next = None class LinkedList: @@ -27,11 +28,11 @@ def find(self, value): return None - def delete(self, value): + def delete(self, key): curr = self.head # special case for remove head - if curr.value == value: + if curr.key == key: self.head = curr.next curr.next = None return curr @@ -39,7 +40,7 @@ def delete(self, value): prev = None while curr != None: - if curr.value == value: + if curr.key == key: prev.next = curr.next curr.next = None return curr @@ -127,76 +128,48 @@ def put(self, key, value): # set table at index to newly created LL self.table[index] = ll # add created node to the head of the LL - ll.add_to_head(Node(value)) + ll.add_to_head(Node(value, key)) # increase count self.count += 1 + self.check_and_resize() else: # if table at index already has a LL, get reference to current LL curr_ll: LinkedList = self.table[index] # set the new Node as the head - curr_ll.add_to_head(Node(value)) + curr_ll.add_to_head(Node(value, key)) # increase count self.count += 1 + self.check_and_resize() def delete(self, key): - """ - Remove the value stored with the given key. - - Print a warning if the key is not found. - - Implement this. - """ - # Your code here - # delete_at_index = self.hash_index(key) - - # if self.table[delete_at_index] is None: - # print("No key found") - # return - # self.table[delete_at_index] = None - # self.count -= 1 - - value = self.table[self.hash_index(key)] - if value == None: - print("No value for index") - self.table[self.hash_index(key)] = None + # value = self.table[self.hash_index(key)] + # if value == None: + # print("No value for index") + # self.table[self.hash_index(key)] = None + index = self.hash_index(key) + ll: LinkedList = self.table[index] + ll.delete(key) + self.check_and_resize() def get(self, key): - """ - Retrieve the value stored with the given key. - - Returns None if the key is not found. - - Implement this. - """ - # Your code here - # index = self.hash_index(key) return self.table[self.hash_index(key)] def resize(self, new_capacity): - """ - Changes the capacity of the hash table and - rehashes all key/value pairs. - - Implement this. - """ - # Your code here - # old_table = self.table - # self.table = [None] * new_capacity - # self.capacity = new_capacity + old_table = self.table + self.table = [None] * new_capacity - # for entry in old_table: - # while entry is not None: - # old_key = entry.key - # old_value = entry.value - # index = self.hash_index(old_key) - # self.table[index] = HashTableEntry(old_key, old_value) + def check_and_resize(self): + if self.get_load_factor() > 0.7: + self.resize(self.capacity * 2) + elif self.get_load_factor() < 0.2: + self.resize(self.capacity / 2) @@ -222,15 +195,21 @@ def resize(self, new_capacity): for i in range(1, 13): print(ht.get(f"line_{i}")) - # Test resizing - old_capacity = ht.get_num_slots() - ht.resize(ht.capacity * 2) - new_capacity = ht.get_num_slots() - - print(f"\nResized from {old_capacity} to {new_capacity}.\n") + ht.delete("line_1") - # Test if data intact after resizing + print("deleted") for i in range(1, 13): print(ht.get(f"line_{i}")) - print("") + # Test resizing + # old_capacity = ht.get_num_slots() + # ht.resize(ht.capacity * 2) + # new_capacity = ht.get_num_slots() + + # print(f"\nResized from {old_capacity} to {new_capacity}.\n") + + # # Test if data intact after resizing + # for i in range(1, 13): + # print(ht.get(f"line_{i}")) + + # print("") From 870fe4709cb550361bb7aeea674e5a1bba16a6d7 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Sun, 1 Nov 2020 21:16:54 -0700 Subject: [PATCH 06/19] Added resize method --- hashtable/hashtable.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index 25abaa5e2..ce572f6a5 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -164,12 +164,26 @@ def get(self, key): def resize(self, new_capacity): old_table = self.table self.table = [None] * new_capacity + self.capacity = new_capacity + + for ll in old_table: + while ll != None: + index = self.hash_index(ll.node.key) + entry = self.table[index] + if entry is None: + entry = Node(node.value, node.key) + else: + while entry.next != None: + entry = entry.next + entry.next = Node(node.value, node.key) + + node = node.next def check_and_resize(self): if self.get_load_factor() > 0.7: self.resize(self.capacity * 2) elif self.get_load_factor() < 0.2: - self.resize(self.capacity / 2) + self.resize(self.capacity // 2) From db93eaee4e5322c01f301c9b9df36f6cea9af69f Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Mon, 2 Nov 2020 21:30:29 -0700 Subject: [PATCH 07/19] Followed guided lecture --- hashtable/hashtable.py | 109 +++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 42 deletions(-) diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index ce572f6a5..39b2b6af5 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -1,9 +1,19 @@ class Node: - def __init__(self, value, key): + def __init__(self, value): self.value = value - self.key = key self.next = None +class HashTableEntry: + def __init__(self, key, value): + self.key = key + self.value = value + + def __eq__(self, other): + if isinstance(other, HashTableEntry): + return self.key == other.key + return False + + class LinkedList: def __init__(self): self.head = None @@ -28,11 +38,11 @@ def find(self, value): return None - def delete(self, key): + def delete(self, value): curr = self.head # special case for remove head - if curr.key == key: + if curr.value == value: self.head = curr.next curr.next = None return curr @@ -40,7 +50,7 @@ def delete(self, key): prev = None while curr != None: - if curr.key == key: + if curr.value == value: prev.next = curr.next curr.next = None return curr @@ -60,8 +70,10 @@ def insert_at_head_or_overwrite(self, node): existing_node = self.find(node.value) if existing_node != None: existing_node.value = node.value + return False else: self.add_to_head(node) + return True # Hash table can't have fewer than this many slots MIN_CAPACITY = 8 @@ -94,7 +106,6 @@ def get_num_slots(self): return len(self.table) - def get_load_factor(self): """ Return the load factor for this hash table. @@ -125,65 +136,79 @@ def put(self, key, value): if self.table[index] == None: # if it is -> initialize new LinkedList ll = LinkedList() - # set table at index to newly created LL + ll.add_to_head(Node(HashTableEntry(key, value))) self.table[index] = ll - # add created node to the head of the LL - ll.add_to_head(Node(value, key)) # increase count self.count += 1 - self.check_and_resize() else: # if table at index already has a LL, get reference to current LL curr_ll: LinkedList = self.table[index] # set the new Node as the head - curr_ll.add_to_head(Node(value, key)) + did_add_new_node = curr_ll.insert_at_head_or_overwrite(Node(HashTableEntry(key, value))) + if did_add_new_node: # increase count - self.count += 1 - self.check_and_resize() + self.count += 1 + + if self.get_load_factor() > 0.7: + self.resize(self.get_num_slots() * 2) def delete(self, key): - - # value = self.table[self.hash_index(key)] - # if value == None: - # print("No value for index") - # self.table[self.hash_index(key)] = None index = self.hash_index(key) - ll: LinkedList = self.table[index] - ll.delete(key) - self.check_and_resize() + if self.table[index] != None: + ll: LinkedList = self.table[index] + did_delete_node = ll.delete(HashTableEntry(key, None)) + if did_delete_node != None: + self.count -= 1 + if self.get_load_factor() < 0.2: + self.resize(self.get_num_slots() / 2) + else: + print("Warning Node not found") def get(self, key): + index = self.hash_index(key) + if self.table[index] != None: + ll: LinkedList = self.table[index] + node = ll.find(HashTableEntry(key, None)) + if node != None: + return node.value.value + return None - return self.table[self.hash_index(key)] def resize(self, new_capacity): old_table = self.table - self.table = [None] * new_capacity + self.table = [None] * int(new_capacity) + self.count = 0 self.capacity = new_capacity - for ll in old_table: - while ll != None: - index = self.hash_index(ll.node.key) - entry = self.table[index] - if entry is None: - entry = Node(node.value, node.key) + for element in old_table: + if element == None: + continue + curr_node: Node = element.head + while curr_node != None: + temp = curr_node.next + curr_node.next = None + index = self.hash_index(curr_node.value.key) + + if self.table[index] != None: + self.table[index].add_to_head(curr_node) else: - while entry.next != None: - entry = entry.next - entry.next = Node(node.value, node.key) + ll = LinkedList() + ll.add_to_head(curr_node) + self.table[index] = ll - node = node.next + curr_node = temp + self.count += 1 def check_and_resize(self): if self.get_load_factor() > 0.7: self.resize(self.capacity * 2) elif self.get_load_factor() < 0.2: - self.resize(self.capacity // 2) + self.resize(self.capacity / 2) @@ -216,14 +241,14 @@ def check_and_resize(self): print(ht.get(f"line_{i}")) # Test resizing - # old_capacity = ht.get_num_slots() - # ht.resize(ht.capacity * 2) - # new_capacity = ht.get_num_slots() + old_capacity = ht.get_num_slots() + ht.resize(ht.capacity * 2) + new_capacity = ht.get_num_slots() - # print(f"\nResized from {old_capacity} to {new_capacity}.\n") + print(f"\nResized from {old_capacity} to {new_capacity}.\n") - # # Test if data intact after resizing - # for i in range(1, 13): - # print(ht.get(f"line_{i}")) + # Test if data intact after resizing + for i in range(1, 13): + print(ht.get(f"line_{i}")) - # print("") + print("") From 170ac825aa5b61d96023086ecc30c46f769e8689 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Tue, 3 Nov 2020 09:16:52 -0700 Subject: [PATCH 08/19] Update lookup_table.py --- applications/lookup_table/lookup_table.py | 26 +++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/applications/lookup_table/lookup_table.py b/applications/lookup_table/lookup_table.py index 05b7d37fa..f68e18951 100644 --- a/applications/lookup_table/lookup_table.py +++ b/applications/lookup_table/lookup_table.py @@ -1,22 +1,30 @@ -# Your code here +import math +import random +dictionary = {} -def slowfun_too_slow(x, y): - v = math.pow(x, y) - v = math.factorial(v) - v //= (x + y) - v %= 982451653 +# def slowfun_too_slow(x, y): +# v = math.pow(x, y) +# v = math.factorial(v) +# v //= (x + y) +# v %= 982451653 - return v +# return v def slowfun(x, y): """ Rewrite slowfun_too_slow() in here so that the program produces the same output, but completes quickly instead of taking ages to run. """ - # Your code here - + if (x, y) in dictionary: + return dictionary[(x, y)] + v = math.pow(x, y) + v = math.factorial(v) + v //= (x + y) + v %= 982451653 + dictionary[(x, y)] = v + return v # Do not modify below this line! From 0e569584e382bc9d77666e0401ab3e728ce5800c Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Tue, 3 Nov 2020 19:43:51 -0700 Subject: [PATCH 09/19] Completed expensive_seq --- applications/expensive_seq/expensive_seq.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/applications/expensive_seq/expensive_seq.py b/applications/expensive_seq/expensive_seq.py index 5c82b8453..866d437c5 100644 --- a/applications/expensive_seq/expensive_seq.py +++ b/applications/expensive_seq/expensive_seq.py @@ -1,9 +1,18 @@ # Your code here - +dictionary = {} def expensive_seq(x, y, z): - # Your code here + if (x, y, z) in dictionary: + return dictionary[(x, y, z)] + + result = 0 + if x <= 0: + result = y + z + if x > 0: + result = expensive_seq(x-1,y+1,z) + expensive_seq(x-2,y+2,z*2) + expensive_seq(x-3,y+3,z*3) + dictionary[(x, y, z)] = result + return result if __name__ == "__main__": From 6e55e5483e1a8b09cd67f31569558f8f06518b0a Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Tue, 3 Nov 2020 20:03:56 -0700 Subject: [PATCH 10/19] Completed word_count --- applications/word_count/word_count.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/applications/word_count/word_count.py b/applications/word_count/word_count.py index a20546425..dc2dabc32 100644 --- a/applications/word_count/word_count.py +++ b/applications/word_count/word_count.py @@ -1,6 +1,22 @@ def word_count(s): - # Your code here + dictionary = {} + special_characters = ['"', ":", ";", ",", ".", "-", "+", "=", "/", "|", "[", "]", "{", "}", "(", ")", "*", "^", "&", "\\"] + lower_case = s.lower() + + for i in special_characters: + lower_case = lower_case.replace(i, "") + + split_string = lower_case.split() + if split_string.count == 0: + return dictionary + + for word in split_string: + if word in dictionary: + dictionary[word] += 1 + else: + dictionary[word] = 1 + return dictionary if __name__ == "__main__": From 8d719fa23e5847d8966421a2b33f6f27263e9658 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Tue, 3 Nov 2020 20:23:15 -0700 Subject: [PATCH 11/19] Completed no_dups --- applications/no_dups/no_dups.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/applications/no_dups/no_dups.py b/applications/no_dups/no_dups.py index caa162c8c..3a551c3f2 100644 --- a/applications/no_dups/no_dups.py +++ b/applications/no_dups/no_dups.py @@ -1,6 +1,21 @@ + def no_dups(s): - # Your code here + array = [] + result_string = "" + split_string = s.split() + + if s == "": + return result_string + + for word in split_string: + if word not in array: + array.append(word) + result_string += word + " " + + if result_string.endswith(" "): + return result_string[:-1] + return result_string if __name__ == "__main__": From 647d6495bb926ea3515b8e71c5c349b8d8390836 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Tue, 3 Nov 2020 20:56:19 -0700 Subject: [PATCH 12/19] got project to read input.txt file --- applications/markov/markov.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/markov/markov.py b/applications/markov/markov.py index 1d138db10..589d8316e 100644 --- a/applications/markov/markov.py +++ b/applications/markov/markov.py @@ -1,9 +1,13 @@ import random +import os.path + +input_text = os.path.join(os.path.dirname(__file__), 'input.txt') # Read in all the words in one go -with open("input.txt") as f: +with open(input_text) as f: words = f.read() +print(words) # TODO: analyze which words can follow other words # Your code here From 4c8ed072455d2ab4e7f99bd0173dd4f6f13dd6c0 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 4 Nov 2020 09:56:16 -0700 Subject: [PATCH 13/19] Created dictionary that contains words that can follow other words --- applications/markov/markov.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/applications/markov/markov.py b/applications/markov/markov.py index 589d8316e..ebec5ce64 100644 --- a/applications/markov/markov.py +++ b/applications/markov/markov.py @@ -7,11 +7,19 @@ with open(input_text) as f: words = f.read() -print(words) +# print(words) # TODO: analyze which words can follow other words -# Your code here +split_words = words.split() +word_dictionary = {} + +for index, word in enumerate(split_words): + if word not in word_dictionary and index != len(split_words) - 1: + word_dictionary[word] = split_words[index + 1] + " " + elif word in word_dictionary: + word_dictionary[word] += split_words[index + 1] + " " + +print(word_dictionary) # TODO: construct 5 random sentences # Your code here - From b43c8fc692f8fe9ed90f871f12a19debf88c16ba Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 4 Nov 2020 10:13:51 -0700 Subject: [PATCH 14/19] Finished constructing 5 randomly generated sentences --- applications/markov/markov.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/applications/markov/markov.py b/applications/markov/markov.py index ebec5ce64..50de817ae 100644 --- a/applications/markov/markov.py +++ b/applications/markov/markov.py @@ -14,12 +14,23 @@ for index, word in enumerate(split_words): if word not in word_dictionary and index != len(split_words) - 1: - word_dictionary[word] = split_words[index + 1] + " " + word_dictionary[word] = [split_words[index + 1]] elif word in word_dictionary: - word_dictionary[word] += split_words[index + 1] + " " - -print(word_dictionary) + word_dictionary[word] += [split_words[index + 1]] +# print(word_dictionary) # TODO: construct 5 random sentences -# Your code here +stop_chars = [".", "?", "!"] + +for _ in range(5): + begin_word = random.choice(list(word_dictionary.keys())) + curr_word = begin_word + sentence = "" + + while curr_word[-1] not in stop_chars: + sentence += curr_word + " " + word = word_dictionary[curr_word] + curr_word = random.choice(list(word)) + + print(sentence) From 0807a47b580f9f974b021d3fe1b3ffbfbcf575e8 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 4 Nov 2020 15:04:41 -0700 Subject: [PATCH 15/19] Finished histo.py --- applications/histo/histo.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/applications/histo/histo.py b/applications/histo/histo.py index 6014a8e13..17c3d03da 100644 --- a/applications/histo/histo.py +++ b/applications/histo/histo.py @@ -1,2 +1,24 @@ -# Your code here +import os +import collections +input_text = os.path.join(os.path.dirname(__file__), 'robin.txt') + +# Read in all the words in one go +with open(input_text) as f: + words = f.read() + +lower_case_words = words.lower() +split_words = lower_case_words.split() +dictionary = {} + +for word in split_words: + if word not in dictionary: + dictionary[word] = 1 + elif word in dictionary: + dictionary[word] += 1 + +sorted_dictionary = {k: v for k, v in sorted(dictionary.items(), key=lambda item: item[1], reverse=True)} + +for key, value in sorted_dictionary.items(): + hash_number = "#" * value + print(f"{key:15} {hash_number}") From 281bac08a288b152035dea2480b6c95d114d93c0 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 4 Nov 2020 15:18:42 -0700 Subject: [PATCH 16/19] Updated to add correct space after largest word --- applications/histo/histo.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/histo/histo.py b/applications/histo/histo.py index 17c3d03da..99bec6788 100644 --- a/applications/histo/histo.py +++ b/applications/histo/histo.py @@ -10,8 +10,11 @@ lower_case_words = words.lower() split_words = lower_case_words.split() dictionary = {} +largest_word = 0 for word in split_words: + if len(word) > largest_word: + largest_word = len(word) if word not in dictionary: dictionary[word] = 1 elif word in dictionary: @@ -21,4 +24,5 @@ for key, value in sorted_dictionary.items(): hash_number = "#" * value - print(f"{key:15} {hash_number}") + space = largest_word + 2 + print(f"{key:{space}} {hash_number}") From df14b518b4b2531892875fed1af451b4a82d9405 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 4 Nov 2020 16:42:39 -0700 Subject: [PATCH 17/19] Sorted encrypted data by frequency of character --- applications/crack_caesar/crack_caesar.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/applications/crack_caesar/crack_caesar.py b/applications/crack_caesar/crack_caesar.py index 1418f0ef3..5596663f9 100644 --- a/applications/crack_caesar/crack_caesar.py +++ b/applications/crack_caesar/crack_caesar.py @@ -1,5 +1,20 @@ -# Use frequency analysis to find the key to ciphertext.txt, and then -# decode it. +import os -# Your code here +input_text = os.path.join(os.path.dirname(__file__), 'cyphertext.txt') +# Read in all the words in one go +with open(input_text) as f: + words = f.read() + +lowercase_words = words.lower() +encrypted_dict = {} + +for char in lowercase_words: + if char.isalpha() != True: + continue + if char in encrypted_dict: + encrypted_dict[char] += 1 + else: + encrypted_dict[char] = 1 + +sorted_encrypted_dict = {k: v for k, v in sorted(encrypted_dict.items(), key=lambda item: item[1], reverse=True)} From 8e77ba31075268af3263e66aff5189b42f4ccb7f Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 4 Nov 2020 16:58:16 -0700 Subject: [PATCH 18/19] Cracked the cypher code! --- applications/crack_caesar/crack_caesar.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/applications/crack_caesar/crack_caesar.py b/applications/crack_caesar/crack_caesar.py index 5596663f9..8174fd251 100644 --- a/applications/crack_caesar/crack_caesar.py +++ b/applications/crack_caesar/crack_caesar.py @@ -9,6 +9,7 @@ lowercase_words = words.lower() encrypted_dict = {} +# Get frequency of characters in encryption for char in lowercase_words: if char.isalpha() != True: continue @@ -17,4 +18,21 @@ else: encrypted_dict[char] = 1 +# Sort characters by frequency sorted_encrypted_dict = {k: v for k, v in sorted(encrypted_dict.items(), key=lambda item: item[1], reverse=True)} + +# Use sorted encryption dictionary to replace characters +encryption_key = ['e', 't', 'a', 'o', 'h', 'n', 'r', 'i', 's', 'd', 'l', 'w', 'u', 'g', 'f', 'b', 'm', 'y', 'c', 'p', 'k', 'v', 'q', 'j', 'x', 'z'] +transfer_dict = {} +result = "" + +for index, key in enumerate(sorted_encrypted_dict): + transfer_dict[key] = encryption_key[index] + +for char in lowercase_words: + if char.isalpha() != True: + result += char + if char in transfer_dict: + result += transfer_dict[char] + +print(result) From 67de9a1c270cba34af1961019e900baaf3476321 Mon Sep 17 00:00:00 2001 From: Matt Martindale Date: Wed, 4 Nov 2020 16:59:27 -0700 Subject: [PATCH 19/19] Cracked the code! --- applications/crack_caesar/crack_caesar.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/crack_caesar/crack_caesar.py b/applications/crack_caesar/crack_caesar.py index 8174fd251..1b5fab885 100644 --- a/applications/crack_caesar/crack_caesar.py +++ b/applications/crack_caesar/crack_caesar.py @@ -26,9 +26,11 @@ transfer_dict = {} result = "" +# Create a tranfer Dictionary for index, key in enumerate(sorted_encrypted_dict): transfer_dict[key] = encryption_key[index] +# Use Transfer dictionary to replace characters in CypherText.txt for char in lowercase_words: if char.isalpha() != True: result += char