Adding hash algorithms
This commit is contained in:
207
examples/hash_tables_demo.py
Normal file
207
examples/hash_tables_demo.py
Normal file
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
Demonstration of hash table implementations and their usage.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
|
||||
from src.hash_tables import (
|
||||
DirectAddressTable,
|
||||
HashTableOpenAddressing,
|
||||
HashTableSeparateChaining
|
||||
)
|
||||
from src.hash_functions import (
|
||||
division_hash,
|
||||
string_hash_polynomial,
|
||||
string_hash_simple
|
||||
)
|
||||
|
||||
|
||||
def demo_direct_address_table():
|
||||
"""Demonstrate direct-address table."""
|
||||
print("=" * 60)
|
||||
print("Direct-Address Table Demonstration")
|
||||
print("=" * 60)
|
||||
|
||||
# Create table for keys in range [0, 99]
|
||||
table = DirectAddressTable(100)
|
||||
|
||||
# Insert some values
|
||||
table.insert(5, "Alice")
|
||||
table.insert(42, "Bob")
|
||||
table.insert(99, "Charlie")
|
||||
|
||||
print("\nInserted key-value pairs:")
|
||||
print(" Key 5 ->", table.search(5))
|
||||
print(" Key 42 ->", table.search(42))
|
||||
print(" Key 99 ->", table.search(99))
|
||||
|
||||
# Search
|
||||
print("\nSearching for key 42:", table.search(42))
|
||||
print("Searching for key 10:", table.search(10)) # Not found
|
||||
|
||||
# Delete
|
||||
table.delete(42)
|
||||
print("\nAfter deleting key 42:")
|
||||
print(" Key 42 ->", table.search(42)) # None
|
||||
print()
|
||||
|
||||
|
||||
def demo_open_addressing():
|
||||
"""Demonstrate open addressing hash table."""
|
||||
print("=" * 60)
|
||||
print("Open Addressing Hash Table Demonstration")
|
||||
print("=" * 60)
|
||||
|
||||
# Test with linear probing
|
||||
print("\n--- Linear Probing ---")
|
||||
ht_linear = HashTableOpenAddressing(10, probe_type='linear')
|
||||
|
||||
keys = [10, 22, 31, 4, 15, 28, 17, 88, 59]
|
||||
for key in keys:
|
||||
ht_linear.insert(key, f"Value_{key}")
|
||||
|
||||
print(f"Inserted {len(keys)} keys")
|
||||
print(f"Load factor: {ht_linear._load_factor():.2f}")
|
||||
|
||||
print("\nSearching for keys:")
|
||||
for key in [10, 22, 88, 99]:
|
||||
result = ht_linear.search(key)
|
||||
print(f" Key {key}: {'Found' if result else 'Not found'}")
|
||||
|
||||
# Test with quadratic probing
|
||||
print("\n--- Quadratic Probing ---")
|
||||
# Use larger table size for quadratic probing to avoid probe sequence issues
|
||||
ht_quad = HashTableOpenAddressing(20, probe_type='quadratic')
|
||||
|
||||
for key in keys:
|
||||
ht_quad.insert(key, f"Value_{key}")
|
||||
|
||||
print(f"Inserted {len(keys)} keys")
|
||||
print(f"Load factor: {ht_quad._load_factor():.2f}")
|
||||
|
||||
# Test with double hashing
|
||||
print("\n--- Double Hashing ---")
|
||||
# Use larger table size for double hashing to ensure all keys can be inserted
|
||||
ht_double = HashTableOpenAddressing(20, probe_type='double')
|
||||
|
||||
for key in keys:
|
||||
ht_double.insert(key, f"Value_{key}")
|
||||
|
||||
print(f"Inserted {len(keys)} keys")
|
||||
print(f"Load factor: {ht_double._load_factor():.2f}")
|
||||
print()
|
||||
|
||||
|
||||
def demo_separate_chaining():
|
||||
"""Demonstrate separate chaining hash table."""
|
||||
print("=" * 60)
|
||||
print("Separate Chaining Hash Table Demonstration")
|
||||
print("=" * 60)
|
||||
|
||||
ht = HashTableSeparateChaining(10)
|
||||
|
||||
keys = [10, 22, 31, 4, 15, 28, 17, 88, 59, 71]
|
||||
for key in keys:
|
||||
ht.insert(key, f"Value_{key}")
|
||||
|
||||
print(f"\nInserted {len(keys)} keys")
|
||||
print(f"Load factor: {ht._load_factor():.2f}")
|
||||
|
||||
chain_lengths = ht.get_chain_lengths()
|
||||
print(f"Chain lengths: {chain_lengths}")
|
||||
print(f"Average chain length: {sum(chain_lengths) / len(chain_lengths):.2f}")
|
||||
print(f"Maximum chain length: {max(chain_lengths)}")
|
||||
|
||||
print("\nSearching for keys:")
|
||||
for key in [10, 22, 88, 99]:
|
||||
result = ht.search(key)
|
||||
print(f" Key {key}: {'Found' if result else 'Not found'}")
|
||||
|
||||
# Delete some keys
|
||||
print("\nDeleting keys 22 and 88:")
|
||||
ht.delete(22)
|
||||
ht.delete(88)
|
||||
print(f" Key 22: {'Found' if ht.search(22) else 'Not found'}")
|
||||
print(f" Key 88: {'Found' if ht.search(88) else 'Not found'}")
|
||||
print()
|
||||
|
||||
|
||||
def demo_hash_functions():
|
||||
"""Demonstrate different hash functions."""
|
||||
print("=" * 60)
|
||||
print("Hash Function Demonstration")
|
||||
print("=" * 60)
|
||||
|
||||
keys = [10, 22, 31, 4, 15, 28, 17, 88, 59, 71]
|
||||
table_size = 11
|
||||
|
||||
print(f"\nKeys: {keys}")
|
||||
print(f"Table size: {table_size}\n")
|
||||
|
||||
# Division method
|
||||
print("Division method (h(k) = k mod m):")
|
||||
for key in keys[:5]:
|
||||
hash_val = division_hash(key, table_size)
|
||||
print(f" h({key}) = {hash_val}")
|
||||
|
||||
# String hashing
|
||||
print("\nString hash functions:")
|
||||
string_keys = ["hello", "world", "hash", "table", "test"]
|
||||
|
||||
print("Simple string hash (BAD - prone to collisions):")
|
||||
for key in string_keys:
|
||||
hash_val = string_hash_simple(key, table_size)
|
||||
print(f" h('{key}') = {hash_val}")
|
||||
|
||||
print("\nPolynomial string hash (GOOD - better distribution):")
|
||||
for key in string_keys:
|
||||
hash_val = string_hash_polynomial(key, table_size)
|
||||
print(f" h('{key}') = {hash_val}")
|
||||
print()
|
||||
|
||||
|
||||
def demo_collision_comparison():
|
||||
"""Demonstrate collision behavior with different hash functions."""
|
||||
print("=" * 60)
|
||||
print("Collision Comparison Demonstration")
|
||||
print("=" * 60)
|
||||
|
||||
# Generate test keys
|
||||
keys = list(range(100, 200))
|
||||
table_size = 50
|
||||
|
||||
from src.hash_functions import (
|
||||
division_hash,
|
||||
multiplication_hash,
|
||||
string_hash_simple,
|
||||
string_hash_polynomial
|
||||
)
|
||||
|
||||
hash_funcs = {
|
||||
'Division': division_hash,
|
||||
'Multiplication': lambda k, s: multiplication_hash(k, s),
|
||||
}
|
||||
|
||||
print(f"\nTesting with {len(keys)} keys and table size {table_size}\n")
|
||||
|
||||
for name, hash_func in hash_funcs.items():
|
||||
hash_values = [hash_func(k, table_size) for k in keys]
|
||||
collisions = len(keys) - len(set(hash_values))
|
||||
collision_rate = collisions / len(keys) * 100
|
||||
|
||||
print(f"{name} method:")
|
||||
print(f" Collisions: {collisions}")
|
||||
print(f" Collision rate: {collision_rate:.2f}%")
|
||||
print(f" Buckets used: {len(set(hash_values))}/{table_size}")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo_direct_address_table()
|
||||
demo_open_addressing()
|
||||
demo_separate_chaining()
|
||||
demo_hash_functions()
|
||||
demo_collision_comparison()
|
||||
|
||||
Reference in New Issue
Block a user