211 lines
6.4 KiB
Python
211 lines
6.4 KiB
Python
import time
|
||
import random
|
||
import csv
|
||
import os
|
||
|
||
from phone_book import (
|
||
ll_insert, ll_find, ll_delete, ll_list_all,
|
||
ht_make, ht_insert, ht_find, ht_delete, ht_list_all,
|
||
bst_insert, bst_find, bst_delete, bst_list_all,
|
||
)
|
||
|
||
N = 10_000
|
||
REPEATS = 5
|
||
SEARCH_COUNT = 110
|
||
DELETE_COUNT = 50
|
||
HT_SIZE = 256
|
||
|
||
RANDOM_SEED = 42
|
||
random.seed(RANDOM_SEED)
|
||
|
||
OUTPUT_DIR = os.path.dirname(__file__)
|
||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||
CSV_PATH = os.path.join(OUTPUT_DIR, 'results.csv')
|
||
|
||
def generate_records(n):
|
||
records = [(f"User_{i:05d}", f"+7{random.randint(1000000000, 9999999999)}")
|
||
for i in range(n)]
|
||
return records
|
||
|
||
|
||
records_base = generate_records(N)
|
||
|
||
records_shuffled = records_base[:]
|
||
random.shuffle(records_shuffled)
|
||
|
||
records_sorted = sorted(records_base, key=lambda x: x[0])
|
||
|
||
existing_names = [r[0] for r in random.sample(records_base, 100)]
|
||
missing_names = [f"None_{i}" for i in range(10)]
|
||
search_names = existing_names + missing_names
|
||
|
||
delete_names = [r[0] for r in random.sample(records_base, DELETE_COUNT)]
|
||
|
||
def measure(func, *args, **kwargs):
|
||
start = time.perf_counter()
|
||
result = func(*args, **kwargs)
|
||
end = time.perf_counter()
|
||
return end - start, result
|
||
|
||
def bench_linked_list(records, mode_label):
|
||
times = {'insert': [], 'find': [], 'delete': []}
|
||
|
||
for _ in range(REPEATS):
|
||
head = None
|
||
t_start = time.perf_counter()
|
||
for name, phone in records:
|
||
head = ll_insert(head, name, phone)
|
||
times['insert'].append(time.perf_counter() - t_start)
|
||
|
||
t_start = time.perf_counter()
|
||
for name in search_names:
|
||
ll_find(head, name)
|
||
times['find'].append(time.perf_counter() - t_start)
|
||
|
||
t_start = time.perf_counter()
|
||
for name in delete_names:
|
||
head = ll_delete(head, name)
|
||
times['delete'].append(time.perf_counter() - t_start)
|
||
|
||
return times
|
||
|
||
|
||
def bench_hash_table(records, mode_label):
|
||
times = {'insert': [], 'find': [], 'delete': []}
|
||
|
||
for _ in range(REPEATS):
|
||
buckets = ht_make(HT_SIZE)
|
||
t_start = time.perf_counter()
|
||
for name, phone in records:
|
||
ht_insert(buckets, name, phone)
|
||
times['insert'].append(time.perf_counter() - t_start)
|
||
|
||
t_start = time.perf_counter()
|
||
for name in search_names:
|
||
ht_find(buckets, name)
|
||
times['find'].append(time.perf_counter() - t_start)
|
||
|
||
t_start = time.perf_counter()
|
||
for name in delete_names:
|
||
ht_delete(buckets, name)
|
||
times['delete'].append(time.perf_counter() - t_start)
|
||
|
||
return times
|
||
|
||
|
||
def bench_bst(records, mode_label):
|
||
times = {'insert': [], 'find': [], 'delete': []}
|
||
|
||
for _ in range(REPEATS):
|
||
root = None
|
||
t_start = time.perf_counter()
|
||
for name, phone in records:
|
||
root = bst_insert(root, name, phone)
|
||
times['insert'].append(time.perf_counter() - t_start)
|
||
|
||
t_start = time.perf_counter()
|
||
for name in search_names:
|
||
bst_find(root, name)
|
||
times['find'].append(time.perf_counter() - t_start)
|
||
|
||
t_start = time.perf_counter()
|
||
for name in delete_names:
|
||
root = bst_delete(root, name)
|
||
times['delete'].append(time.perf_counter() - t_start)
|
||
|
||
return times
|
||
|
||
def avg(lst):
|
||
return sum(lst) / len(lst)
|
||
|
||
|
||
def run_all():
|
||
print(f"Запуск бенчмарков: N={N}, повторений={REPEATS}\n")
|
||
print(f"{'Структура':<15} {'Режим':<12} {'Операция':<10} "
|
||
f"{'Среднее (с)':<14} {'Все замеры'}")
|
||
print("-" * 80)
|
||
|
||
all_results = [["Структура", "Режим", "Операция", "Среднее (с)"] +
|
||
[f"Замер_{i+1}" for i in range(REPEATS)]]
|
||
|
||
datasets = [
|
||
(records_shuffled, "случайный"),
|
||
(records_sorted, "сортированный"),
|
||
]
|
||
|
||
benchmarks = [
|
||
("LinkedList", bench_linked_list),
|
||
("HashTable", bench_hash_table),
|
||
("BST", bench_bst),
|
||
]
|
||
|
||
for ds_records, ds_mode in datasets:
|
||
for struct_name, bench_func in benchmarks:
|
||
print(f"\n [{struct_name}] режим: {ds_mode}")
|
||
if struct_name == "BST" and ds_mode == "сортированный":
|
||
import sys
|
||
sys.setrecursionlimit(50_000)
|
||
|
||
times = bench_func(ds_records, ds_mode)
|
||
|
||
for op, op_times in times.items():
|
||
mean = avg(op_times)
|
||
row = [struct_name, ds_mode, op, f"{mean:.6f}"] + \
|
||
[f"{t:.6f}" for t in op_times]
|
||
all_results.append(row)
|
||
|
||
print(f" {op:<10} среднее={mean:.6f}с "
|
||
f"замеры={[f'{t:.4f}' for t in op_times]}")
|
||
|
||
with open(CSV_PATH, 'w', newline='', encoding='utf-8') as f:
|
||
writer = csv.writer(f)
|
||
writer.writerows(all_results)
|
||
|
||
print(f"\n✅ Результаты сохранены в: {CSV_PATH}")
|
||
return all_results
|
||
|
||
def smoke_test():
|
||
print("=== Smoke Test ===\n")
|
||
|
||
test_data = [("Alice", "111"), ("Bob", "222"), ("Charlie", "333")]
|
||
|
||
head = None
|
||
for name, phone in test_data:
|
||
head = ll_insert(head, name, phone)
|
||
assert ll_find(head, "Alice") == "111"
|
||
assert ll_find(head, "Bob") == "222"
|
||
assert ll_find(head, "Nobody") is None
|
||
head = ll_delete(head, "Bob")
|
||
assert ll_find(head, "Bob") is None
|
||
sorted_ll = ll_list_all(head)
|
||
assert sorted_ll == [("Alice", "111"), ("Charlie", "333")]
|
||
print("✅ LinkedList — OK")
|
||
|
||
buckets = ht_make(16)
|
||
for name, phone in test_data:
|
||
ht_insert(buckets, name, phone)
|
||
assert ht_find(buckets, "Charlie") == "333"
|
||
assert ht_find(buckets, "Nobody") is None
|
||
ht_delete(buckets, "Alice")
|
||
assert ht_find(buckets, "Alice") is None
|
||
sorted_ht = ht_list_all(buckets)
|
||
assert sorted_ht == [("Bob", "222"), ("Charlie", "333")]
|
||
print("✅ HashTable — OK")
|
||
|
||
root = None
|
||
for name, phone in test_data:
|
||
root = bst_insert(root, name, phone)
|
||
assert bst_find(root, "Alice") == "111"
|
||
assert bst_find(root, "Nobody") is None
|
||
root = bst_delete(root, "Alice")
|
||
assert bst_find(root, "Alice") is None
|
||
sorted_bst = bst_list_all(root)
|
||
assert sorted_bst == [("Bob", "222"), ("Charlie", "333")]
|
||
print("✅ BST — OK")
|
||
|
||
print("\nВсе тесты пройдены!\n")
|
||
|
||
if __name__ == "__main__":
|
||
smoke_test()
|
||
results = run_all()
|