Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions applications/crack_caesar/crack_caesar.py
Original file line number Diff line number Diff line change
@@ -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
123 changes: 114 additions & 9 deletions hashtable/hashtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ class HashTableEntry:
"""
Linked List hash table key/value pair
"""

def __init__(self, key, value):
self.key = key
self.value = value
Expand All @@ -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):
"""
Expand All @@ -35,7 +38,7 @@ def get_num_slots(self):
Implement this.
"""
# Your code here

return self.capacity

def get_load_factor(self):
"""
Expand All @@ -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):
"""
Expand All @@ -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):
"""
Expand All @@ -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):
"""
Expand All @@ -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):
"""
Expand All @@ -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):
"""
Expand All @@ -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):
"""
Expand All @@ -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)
Expand Down