diff --git a/shared/notes-and-examples/2026.03.04/homework.py b/shared/notes-and-examples/2026.03.04/homework.py index 1df6226..7bd8b2d 100644 --- a/shared/notes-and-examples/2026.03.04/homework.py +++ b/shared/notes-and-examples/2026.03.04/homework.py @@ -5,3 +5,28 @@ # sample_dict = Dictionary() # sample_dict.set("test string", "test value") # sample_dict.set("foo", "bar") + +class Dictionary: + backing: "list" = [None] * 100 + + def hash(self, key: str): + ord_total = 0; + for letter in key: + ord_total += ord(letter) + return ord_total % len(self.backing) + + def get(self, key: str): + index = self.hash(key) + return self.backing[index] + + def set(self, key: str, value): + index = self.hash(key) + self.backing[index] = value + +sample_dict = Dictionary() +sample_dict.set("test string", "test value") +sample_dict.set("foo", "bar") +print(sample_dict.get("test string")) +print(sample_dict.get("foo")) + +print(sample_dict.backing) diff --git a/shared/notes-and-examples/2026.03.18/hash_collision.py b/shared/notes-and-examples/2026.03.18/hash_collision.py new file mode 100644 index 0000000..0eae9a5 --- /dev/null +++ b/shared/notes-and-examples/2026.03.18/hash_collision.py @@ -0,0 +1,23 @@ +class Dictionary: + backing: "list" = [None] * 100 + + def hash(self, key: str): + ord_total = 0; + for letter in key: + ord_total += ord(letter) + return ord_total % len(self.backing) + + def get(self, key: str): + index = self.hash(key) + return self.backing[index] + + def set(self, key: str, value): + index = self.hash(key) + self.backing[index] = value + +sample_dict = Dictionary() +sample_dict.set("test string", "test value") +sample_dict.set("tets string", "this value will overwrite the other one") +print(sample_dict.get("test string")) + +print(sample_dict.backing) diff --git a/shared/notes-and-examples/2026.03.18/linear_probing.py b/shared/notes-and-examples/2026.03.18/linear_probing.py new file mode 100644 index 0000000..d49256e --- /dev/null +++ b/shared/notes-and-examples/2026.03.18/linear_probing.py @@ -0,0 +1,44 @@ +# some variants of this: +# "quad" probing: use an exponential instead of just adding + +class Dictionary: + backing: "list" = [None] * 100 + + def hash(self, key: str): + ord_total = 0; + for letter in key: + ord_total += ord(letter) + return ord_total % len(self.backing) + + def get_actual_index_recursion(self, key: str, num_attempts = 0): + index = self.hash(key) + num_attempts + + if self.backing[index] is not None and self.backing[index][0] != key: + return self.get_actual_index_recursion(key, num_attempts + 1) + + return index + + def get_actual_index_loop(self, key: str): + # the loop version; works pretty much the same + index = self.hash(key) + while self.backing[index] is not None and self.backing[index][0] != key: + index += 1 + + return index + + def get(self, key: str): + index = self.get_actual_index_recursion(key) + return self.backing[index][1] + + def set(self, key: str, value): + index = self.get_actual_index_recursion(key) + # instead of storing just values, store tuples (key-value pairs) + self.backing[index] = (key, value) + +sample_dict = Dictionary() +sample_dict.set("test string", "test value") +sample_dict.set("tets string", "this value will *not* overwrite the other one") +print(sample_dict.get("test string")) +print(sample_dict.get("tets string")) + +print(sample_dict.backing) diff --git a/shared/notes-and-examples/2026.03.18/repeated_hashing.py b/shared/notes-and-examples/2026.03.18/repeated_hashing.py new file mode 100644 index 0000000..0adf84c --- /dev/null +++ b/shared/notes-and-examples/2026.03.18/repeated_hashing.py @@ -0,0 +1,47 @@ +# also called "double hashing" + +class Dictionary: + backing: "list" = [None] * 100 + + def hash(self, key: str): + ord_total = 0; + for letter in key: + ord_total += ord(letter) + return ord_total % len(self.backing) + + def secondary_hash(self, key: str): + # never actually use this; this is just a demo + return len(key) + 1 # ensure never 0 + + def get_actual_index( + self, + key: str, + num_attempts = 0, + ): + # handles repeated hashing + index = self.hash(key) + num_attempts * self.secondary_hash(key) + + # for retrieving a value: + # if key at index matches key, we have the correct index + # otherwise, hash again + if self.backing[index] is not None and self.backing[index][0] != key: + return self.get_actual_index(key, num_attempts + 1) + + return index + + def get(self, key: str): + index = self.get_actual_index(key) + return self.backing[index][1] + + def set(self, key: str, value): + index = self.get_actual_index(key) + # instead of storing just values, store tuples (key-value pairs) + self.backing[index] = (key, value) + +sample_dict = Dictionary() +sample_dict.set("test string", "test value") +sample_dict.set("tets string", "this value will *not* overwrite the other one") +print(sample_dict.get("test string")) +print(sample_dict.get("tets string")) + +print(sample_dict.backing)