diff --git a/README.md b/README.md index f766a6189..c524b4970 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Task: Implement a basic hash table without collision resolution. 4. Implement the `put()`, `get()`, and `delete()` methods. You can test this with: + ``` python test_hashtable_no_collisions.py diff --git a/applications/crack_caesar/crack_caesar.py b/applications/crack_caesar/crack_caesar.py index 1418f0ef3..857376a63 100644 --- a/applications/crack_caesar/crack_caesar.py +++ b/applications/crack_caesar/crack_caesar.py @@ -1,5 +1,24 @@ # Use frequency analysis to find the key to ciphertext.txt, and then # decode it. + # Your code here +# U - make a table with letter frequency in it ? +# use the table to iderate through the ciphertext.text +# the most fequent letter in the ciphertext will == the most common letter in the alphabel +# to create a new table of common code . + +# p +# E +# R +from csv import reader +with open("ciphertext.txt") as file: + csv_reader = reader(file) + for letter in csv_reader: + print(letter) + +text_letter = letter + +def frequencyLetter(): + pass diff --git a/hashtable/hashtable.py b/hashtable/hashtable.py index 0205f0ba9..853a3e617 100644 --- a/hashtable/hashtable.py +++ b/hashtable/hashtable.py @@ -2,6 +2,7 @@ class HashTableEntry: """ Linked List hash table key/value pair """ + def __init__(self, key, value): self.key = key self.value = value @@ -22,7 +23,9 @@ class HashTable: def __init__(self, capacity): # Your code here - + self.capacity = capacity + self.load = 0 + self.table = [None] * self.capacity def get_num_slots(self): """ @@ -35,7 +38,7 @@ def get_num_slots(self): Implement this. """ # Your code here - + return self.capacity def get_load_factor(self): """ @@ -44,7 +47,10 @@ def get_load_factor(self): Implement this. """ # Your code here - + # dont do yet + # number of items divided by number of buckets + # try to keep between 20% and 70% + return self.load / self.capacity def fnv1(self, key): """ @@ -54,7 +60,21 @@ def fnv1(self, key): """ # Your code here - + # what is a FNV-1 Hash, 64 bit? + # Non-crytographic hash function created by Glenn Fowler, London Curt Noll, and Kiem-Phong Vo. + # Constatnts + # Start with an initail hash value + FNV_prime = 1099511628211 + offset_basis = 14695981039346656037 + key_of_data_bytes = key.encode() + + hash = offset_basis + for letter in key_of_data_bytes: + hash = hash * FNV_prime + # Use the XOR operator ^ between two values to perform bitwise "exclusive or" on their binary representations. When used between two integers, the XOR operator returns an integer. + hash = hash ^ letter + + return hash def djb2(self, key): """ @@ -64,14 +84,13 @@ def djb2(self, key): """ # Your code here - 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 + return self.fnv1(key) % self.capacity + # return self.djb2(key) % self.capacity def put(self, key, value): """ @@ -82,7 +101,24 @@ def put(self, key, value): Implement this. """ # Your code here + # hashed_string = self.hash_index(key) + # if self.table[hashed_string] != None: + # print("warning collistion!!!") + + # self.table[hashed_string] = value + + # self.load += 1 + hashed_string = self.hash_index(key) + new_node = HashTableEntry(key, value) + curr = self.table[hashed_string] + + if self.table is not None: + self.table[hashed_string] = new_node + self.table[hashed_string].next = curr + else: + self.table = new_node + self.load += 1 def delete(self, key): """ @@ -93,7 +129,39 @@ def delete(self, key): Implement this. """ # Your code here + # hashed_string = self.hash_index(key) + # if self.table == None: + # print("warning no key!!") + + # else: + + # self.table[hashed_string] = None + # self.load -= 1 + hashed_string = self.hash_index(key) + removed = False + + if self.table[hashed_string].key == key: + self.table[hashed_string] = self.table[hashed_string].next + self.load -= 1 + removed = True + + else: + curr = self.table[hashed_string] + prev_node = None + + while curr is not None: + if curr.key == key: + prev_node.next = curr.next + self.load -= 1 + removed = True + + return + + prev_node = curr + curr = curr.next + if removed == False: + print("warning no key!!") def get(self, key): """ @@ -105,6 +173,20 @@ def get(self, key): """ # Your code here + # hashed_string = self.hash_index(key) + + # value = self.table[hashed_string] + + # return value if value else None + hashed_string = self.hash_index(key) + current_node = self.table[hashed_string] + + while current_node is not None: + if current_node.key == key: + return current_node.value + current_node = current_node.next + + return None def resize(self, new_capacity): """ @@ -114,8 +196,31 @@ def resize(self, new_capacity): Implement this. """ # Your code here - - + # save your old storage in a new variable + # make a new storage , with new capacity + # then loop over your old starage + # for each index, if its not not + # iterate through the linked list + # for each node , call put for key and value + # that way, its rehashed and inserted to new storage + # self.load remain the same + # self.capacity + # + new_hashtable = HashTable(new_capacity) + + for i in range(self.capacity): + if self.table[i] is not None: + entry = self.table[i] + while entry: + new_hashtable.put(entry.key, entry.value) + entry = entry.next + + self.table = new_hashtable.table + self.capacity = new_capacity + self.load = new_hashtable.load + + +hash_table = HashTable(100) if __name__ == "__main__": ht = HashTable(8)