forked from UNN/2026-rff_mp
345 lines
6.5 KiB
Python
345 lines
6.5 KiB
Python
|
|
import random
|
||
|
|
import time
|
||
|
|
import csv
|
||
|
|
import sys
|
||
|
|
sys.setrecursionlimit(30000)
|
||
|
|
|
||
|
|
|
||
|
|
# =========================
|
||
|
|
# LINKED LIST
|
||
|
|
# =========================
|
||
|
|
|
||
|
|
def ll_insert(head, name, phone):
|
||
|
|
current = head
|
||
|
|
|
||
|
|
while current:
|
||
|
|
if current["name"] == name:
|
||
|
|
current["phone"] = phone
|
||
|
|
return head
|
||
|
|
current = current["next"]
|
||
|
|
|
||
|
|
new_node = {
|
||
|
|
"name": name,
|
||
|
|
"phone": phone,
|
||
|
|
"next": head
|
||
|
|
}
|
||
|
|
|
||
|
|
return new_node
|
||
|
|
|
||
|
|
|
||
|
|
def ll_find(head, name):
|
||
|
|
current = head
|
||
|
|
|
||
|
|
while current:
|
||
|
|
if current["name"] == name:
|
||
|
|
return current["phone"]
|
||
|
|
current = current["next"]
|
||
|
|
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def ll_delete(head, name):
|
||
|
|
if head is None:
|
||
|
|
return None
|
||
|
|
|
||
|
|
if head["name"] == name:
|
||
|
|
return head["next"]
|
||
|
|
|
||
|
|
current = head
|
||
|
|
|
||
|
|
while current["next"]:
|
||
|
|
if current["next"]["name"] == name:
|
||
|
|
current["next"] = current["next"]["next"]
|
||
|
|
return head
|
||
|
|
|
||
|
|
current = current["next"]
|
||
|
|
|
||
|
|
return head
|
||
|
|
|
||
|
|
|
||
|
|
def ll_list_all(head):
|
||
|
|
result = []
|
||
|
|
|
||
|
|
current = head
|
||
|
|
|
||
|
|
while current:
|
||
|
|
result.append((current["name"], current["phone"]))
|
||
|
|
current = current["next"]
|
||
|
|
|
||
|
|
return sorted(result)
|
||
|
|
|
||
|
|
|
||
|
|
# =========================
|
||
|
|
# HASH TABLE
|
||
|
|
# =========================
|
||
|
|
|
||
|
|
TABLE_SIZE = 1000
|
||
|
|
|
||
|
|
|
||
|
|
def hash_func(name):
|
||
|
|
return sum(ord(c) for c in name) % TABLE_SIZE
|
||
|
|
|
||
|
|
|
||
|
|
def ht_insert(buckets, name, phone):
|
||
|
|
index = hash_func(name)
|
||
|
|
buckets[index] = ll_insert(buckets[index], name, phone)
|
||
|
|
|
||
|
|
|
||
|
|
def ht_find(buckets, name):
|
||
|
|
index = hash_func(name)
|
||
|
|
return ll_find(buckets[index], name)
|
||
|
|
|
||
|
|
|
||
|
|
def ht_delete(buckets, name):
|
||
|
|
index = hash_func(name)
|
||
|
|
buckets[index] = ll_delete(buckets[index], name)
|
||
|
|
|
||
|
|
|
||
|
|
def ht_list_all(buckets):
|
||
|
|
result = []
|
||
|
|
|
||
|
|
for bucket in buckets:
|
||
|
|
result.extend(ll_list_all(bucket))
|
||
|
|
|
||
|
|
return sorted(result)
|
||
|
|
|
||
|
|
|
||
|
|
# =========================
|
||
|
|
# BST
|
||
|
|
# =========================
|
||
|
|
|
||
|
|
def bst_insert(root, name, phone):
|
||
|
|
if root is None:
|
||
|
|
return {
|
||
|
|
"name": name,
|
||
|
|
"phone": phone,
|
||
|
|
"left": None,
|
||
|
|
"right": None
|
||
|
|
}
|
||
|
|
|
||
|
|
if name < root["name"]:
|
||
|
|
root["left"] = bst_insert(root["left"], name, phone)
|
||
|
|
|
||
|
|
elif name > root["name"]:
|
||
|
|
root["right"] = bst_insert(root["right"], name, phone)
|
||
|
|
|
||
|
|
else:
|
||
|
|
root["phone"] = phone
|
||
|
|
|
||
|
|
return root
|
||
|
|
|
||
|
|
|
||
|
|
def bst_find(root, name):
|
||
|
|
if root is None:
|
||
|
|
return None
|
||
|
|
|
||
|
|
if name == root["name"]:
|
||
|
|
return root["phone"]
|
||
|
|
|
||
|
|
if name < root["name"]:
|
||
|
|
return bst_find(root["left"], name)
|
||
|
|
|
||
|
|
return bst_find(root["right"], name)
|
||
|
|
|
||
|
|
|
||
|
|
def bst_min(node):
|
||
|
|
current = node
|
||
|
|
|
||
|
|
while current["left"]:
|
||
|
|
current = current["left"]
|
||
|
|
|
||
|
|
return current
|
||
|
|
|
||
|
|
|
||
|
|
def bst_delete(root, name):
|
||
|
|
if root is None:
|
||
|
|
return None
|
||
|
|
|
||
|
|
if name < root["name"]:
|
||
|
|
root["left"] = bst_delete(root["left"], name)
|
||
|
|
|
||
|
|
elif name > root["name"]:
|
||
|
|
root["right"] = bst_delete(root["right"], name)
|
||
|
|
|
||
|
|
else:
|
||
|
|
if root["left"] is None:
|
||
|
|
return root["right"]
|
||
|
|
|
||
|
|
if root["right"] is None:
|
||
|
|
return root["left"]
|
||
|
|
|
||
|
|
temp = bst_min(root["right"])
|
||
|
|
|
||
|
|
root["name"] = temp["name"]
|
||
|
|
root["phone"] = temp["phone"]
|
||
|
|
|
||
|
|
root["right"] = bst_delete(root["right"], temp["name"])
|
||
|
|
|
||
|
|
return root
|
||
|
|
|
||
|
|
|
||
|
|
def bst_list_all(root):
|
||
|
|
if root is None:
|
||
|
|
return []
|
||
|
|
|
||
|
|
return (
|
||
|
|
bst_list_all(root["left"])
|
||
|
|
+ [(root["name"], root["phone"])]
|
||
|
|
+ bst_list_all(root["right"])
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
# =========================
|
||
|
|
# TEST DATA
|
||
|
|
# =========================
|
||
|
|
|
||
|
|
N = 10000
|
||
|
|
|
||
|
|
records = [
|
||
|
|
(f"User_{i:05d}", str(random.randint(100000, 999999)))
|
||
|
|
for i in range(N)
|
||
|
|
]
|
||
|
|
|
||
|
|
records_shuffled = records[:]
|
||
|
|
random.shuffle(records_shuffled)
|
||
|
|
|
||
|
|
records_sorted = sorted(records)
|
||
|
|
|
||
|
|
|
||
|
|
# =========================
|
||
|
|
# BENCHMARK
|
||
|
|
# =========================
|
||
|
|
|
||
|
|
results = [
|
||
|
|
["Structure", "Mode", "Operation", "Time"]
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def benchmark_linked_list(records_input, mode):
|
||
|
|
global results
|
||
|
|
|
||
|
|
head = None
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, phone in records_input:
|
||
|
|
head = ll_insert(head, name, phone)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["LinkedList", mode, "insert", end - start])
|
||
|
|
|
||
|
|
sample = random.sample(records_input, 100)
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, _ in sample:
|
||
|
|
ll_find(head, name)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["LinkedList", mode, "find", end - start])
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, _ in sample[:50]:
|
||
|
|
head = ll_delete(head, name)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["LinkedList", mode, "delete", end - start])
|
||
|
|
|
||
|
|
|
||
|
|
def benchmark_hash_table(records_input, mode):
|
||
|
|
global results
|
||
|
|
|
||
|
|
buckets = [None] * TABLE_SIZE
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, phone in records_input:
|
||
|
|
ht_insert(buckets, name, phone)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["HashTable", mode, "insert", end - start])
|
||
|
|
|
||
|
|
sample = random.sample(records_input, 100)
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, _ in sample:
|
||
|
|
ht_find(buckets, name)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["HashTable", mode, "find", end - start])
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, _ in sample[:50]:
|
||
|
|
ht_delete(buckets, name)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["HashTable", mode, "delete", end - start])
|
||
|
|
|
||
|
|
|
||
|
|
def benchmark_bst(records_input, mode):
|
||
|
|
global results
|
||
|
|
|
||
|
|
root = None
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, phone in records_input:
|
||
|
|
root = bst_insert(root, name, phone)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["BST", mode, "insert", end - start])
|
||
|
|
|
||
|
|
sample = random.sample(records_input, 100)
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, _ in sample:
|
||
|
|
bst_find(root, name)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["BST", mode, "find", end - start])
|
||
|
|
|
||
|
|
start = time.perf_counter()
|
||
|
|
|
||
|
|
for name, _ in sample[:50]:
|
||
|
|
root = bst_delete(root, name)
|
||
|
|
|
||
|
|
end = time.perf_counter()
|
||
|
|
|
||
|
|
results.append(["BST", mode, "delete", end - start])
|
||
|
|
|
||
|
|
|
||
|
|
# =========================
|
||
|
|
# RUN TESTS
|
||
|
|
# =========================
|
||
|
|
|
||
|
|
benchmark_linked_list(records_shuffled, "random")
|
||
|
|
benchmark_linked_list(records_sorted, "sorted")
|
||
|
|
|
||
|
|
benchmark_hash_table(records_shuffled, "random")
|
||
|
|
benchmark_hash_table(records_sorted, "sorted")
|
||
|
|
|
||
|
|
benchmark_bst(records_shuffled, "random")
|
||
|
|
benchmark_bst(records_sorted, "sorted")
|
||
|
|
|
||
|
|
|
||
|
|
# =========================
|
||
|
|
# SAVE CSV
|
||
|
|
# =========================
|
||
|
|
|
||
|
|
with open("results.csv", "w", newline="") as f:
|
||
|
|
writer = csv.writer(f)
|
||
|
|
writer.writerows(results)
|
||
|
|
|
||
|
|
print("Done! Results saved to results.csv")
|