"""Экспериментальное сравнение структур данных.""" import argparse import csv import random from pathlib import Path from statistics import mean from time import perf_counter from phonebook import ( bst_delete, bst_find, bst_insert, ht_delete, ht_find, ht_insert, ll_delete, ll_find, ll_insert, ) def generate_records(size): return [(f"User_{index:05d}", f"+7{index:010d}") for index in range(size)] def measure_once(structure, records, existing_names, missing_names, deleted_names, bucket_count): started = perf_counter() if structure == "LinkedList": data = None for name, phone in records: data = ll_insert(data, name, phone) insert_time = perf_counter() - started started = perf_counter() for name in existing_names + missing_names: ll_find(data, name) find_time = perf_counter() - started started = perf_counter() for name in deleted_names: data = ll_delete(data, name) delete_time = perf_counter() - started elif structure == "HashTable": data = [None] * bucket_count for name, phone in records: ht_insert(data, name, phone) insert_time = perf_counter() - started started = perf_counter() for name in existing_names + missing_names: ht_find(data, name) find_time = perf_counter() - started started = perf_counter() for name in deleted_names: ht_delete(data, name) delete_time = perf_counter() - started else: data = None for name, phone in records: data = bst_insert(data, name, phone) insert_time = perf_counter() - started started = perf_counter() for name in existing_names + missing_names: bst_find(data, name) find_time = perf_counter() - started started = perf_counter() for name in deleted_names: data = bst_delete(data, name) delete_time = perf_counter() - started return {"insert": insert_time, "find_110": find_time, "delete_50": delete_time} def run_experiment(size=3000, repeats=5, seed=2026, output_dir="docs/data"): rng = random.Random(seed) sorted_records = generate_records(size) shuffled_records = sorted_records.copy() rng.shuffle(shuffled_records) modes = {"shuffled": shuffled_records, "sorted": sorted_records} rows = [] for mode, records in modes.items(): names = [record[0] for record in records] test_cases = [] for run in range(1, repeats + 1): test_cases.append(( run, rng.sample(names, min(100, size)), [f"None_{index}" for index in range(10)], rng.sample(names, min(50, size)), )) for structure in ("LinkedList", "HashTable", "BST"): for run, existing, missing, deleted in test_cases: timings = measure_once(structure, records, existing, missing, deleted, max(17, size * 2 + 1)) for operation, elapsed in timings.items(): rows.append({ "structure": structure, "mode": mode, "operation": operation, "run": run, "time_seconds": elapsed, }) output = Path(output_dir) output.mkdir(parents=True, exist_ok=True) raw_path = output / "results_raw.csv" with raw_path.open("w", newline="", encoding="utf-8-sig") as file: writer = csv.DictWriter(file, fieldnames=rows[0].keys()) writer.writeheader() writer.writerows(rows) groups = {} for row in rows: key = (row["structure"], row["mode"], row["operation"]) groups.setdefault(key, []).append(row["time_seconds"]) summary = [ {"structure": key[0], "mode": key[1], "operation": key[2], "mean_seconds": mean(values)} for key, values in groups.items() ] summary_path = output / "results_summary.csv" with summary_path.open("w", newline="", encoding="utf-8-sig") as file: writer = csv.DictWriter(file, fieldnames=summary[0].keys()) writer.writeheader() writer.writerows(summary) return raw_path, summary_path if __name__ == "__main__": parser = argparse.ArgumentParser(description="Сравнение структур телефонного справочника") parser.add_argument("--size", type=int, default=3000) parser.add_argument("--repeats", type=int, default=5) args = parser.parse_args() raw, summary = run_experiment(args.size, args.repeats) print(f"Полные замеры: {raw}\nСредние значения: {summary}")