From bf2dc504792d6845cdd9421797f16a588f0b7e60 Mon Sep 17 00:00:00 2001 From: not why Date: Mon, 25 May 2026 00:51:26 +0300 Subject: [PATCH] added experiment --- soninrv/docs/data/lab1/phonebook.py | 143 +++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/soninrv/docs/data/lab1/phonebook.py b/soninrv/docs/data/lab1/phonebook.py index 7e9d99d..d33111a 100644 --- a/soninrv/docs/data/lab1/phonebook.py +++ b/soninrv/docs/data/lab1/phonebook.py @@ -161,4 +161,145 @@ def bst_list_all(root): node = stack.pop() result.append((node['name'], node['phone'])) node = node['right'] - return result \ No newline at end of file + return result + +""" +Экспериментальная часть: замер производительности трёх структур данных. +""" + +import time +import csv +import random +import os +import sys + +sys.setrecursionlimit(30000) # BST с отсортированными данными — глубокая рекурсия + + +# ── Параметры ────────────────────────────────────────────────────────────── +N = 10_000 # размер набора +REPEATS = 5 # повторений каждого замера +SEARCH_N = 100 # запросов на поиск (существующих) +SEARCH_MISS = 10 # запросов на поиск (отсутствующих) +DELETE_N = 50 # удалений + +random.seed(42) + +# ── Генерация данных ─────────────────────────────────────────────────────── +records_sorted = [(f"User_{i:05d}", f"+7-000-{i:07d}") for i in range(N)] +records_shuffled = records_sorted[:] +random.shuffle(records_shuffled) + +search_names_hit = [records_sorted[i][0] for i in random.sample(range(N), SEARCH_N)] +search_names_miss = [f"None_{i:04d}" for i in range(SEARCH_MISS)] +search_names = search_names_hit + search_names_miss + +delete_names = [records_sorted[i][0] for i in random.sample(range(N), DELETE_N)] + +# ── Вспомогательные функции ──────────────────────────────────────────────── + +def build_ll(records): + head = None + for name, phone in records: + head = ll_insert(head, name, phone) + return head + +def build_ht(records): + buckets = ht_create() + for name, phone in records: + ht_insert(buckets, name, phone) + return buckets + +def build_bst(records): + root = None + for name, phone in records: + root = bst_insert(root, name, phone) + return root + +STRUCTURES = { + 'LinkedList': { + 'build': build_ll, + 'find': ll_find, + 'delete': lambda ds, name: ll_delete(ds, name), # возвращает новый head + 'list_all': ll_list_all, + 'mutable': False, # ll_delete возвращает новую голову + }, + 'HashTable': { + 'build': build_ht, + 'find': ht_find, + 'delete': lambda ds, name: ht_delete(ds, name), # in-place, returns None + 'list_all': ht_list_all, + 'mutable': True, + }, + 'BST': { + 'build': build_bst, + 'find': bst_find, + 'delete': lambda ds, name: bst_delete(ds, name), # возвращает новый корень + 'list_all': bst_list_all, + 'mutable': False, + }, +} + +MODES = { + 'shuffled': records_shuffled, + 'sorted': records_sorted, +} + +# ── Замер ────────────────────────────────────────────────────────────────── + +def measure(fn, *args, repeats=REPEATS): + times = [] + for _ in range(repeats): + t0 = time.perf_counter() + fn(*args) + times.append(time.perf_counter() - t0) + return times + +rows = [["structure", "mode", "operation", "run", "time_sec"]] + +for struct_name, ops in STRUCTURES.items(): + for mode_name, records in MODES.items(): + print(f" {struct_name} / {mode_name} ...", flush=True) + + # ── А. Вставка ────────────────────────────────────────────────── + insert_times = [] + for run in range(REPEATS): + t0 = time.perf_counter() + ds = ops['build'](records) + insert_times.append(time.perf_counter() - t0) + rows.append([struct_name, mode_name, "insert", run + 1, insert_times[-1]]) + + # Строим структуру один раз для поиска и удаления + ds = ops['build'](records) + + # ── Б. Поиск ──────────────────────────────────────────────────── + def do_search(ds=ds): + for name in search_names: + ops['find'](ds, name) + + search_times = measure(do_search) + for run, t in enumerate(search_times, 1): + rows.append([struct_name, mode_name, "find", run, t]) + + # ── В. Удаление ───────────────────────────────────────────────── + # Удаление изменяет структуру, поэтому каждый раз пересобираем + delete_times = [] + for run in range(REPEATS): + ds2 = ops['build'](records) + t0 = time.perf_counter() + for name in delete_names: + result = ops['delete'](ds2, name) + if result is not None: # ll / bst возвращают новую голову/корень + ds2 = result + delete_times.append(time.perf_counter() - t0) + rows.append([struct_name, mode_name, "delete", run + 1, delete_times[-1]]) + + print(f" insert avg={sum(insert_times)/REPEATS:.4f}s " + f"find avg={sum(search_times)/REPEATS:.4f}s " + f"delete avg={sum(delete_times)/REPEATS:.4f}s") + + +with open("results.csv", "w", newline="", encoding="utf-8") as f: + csv.writer(f).writerows(rows) + +print("\nРезультаты сохранены в docs/data/results.csv") \ No newline at end of file