forked from UNN/2026-rff_mp
133 lines
4.7 KiB
Python
133 lines
4.7 KiB
Python
"""Экспериментальное сравнение структур данных."""
|
|
|
|
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}")
|