import random import time import sys import csv import os import matplotlib.pyplot as plt import numpy as np sys.setrecursionlimit(10000) ll_head = None def ll_insert(name, phone): global ll_head cur = ll_head while cur is not None: if cur['name'] == name: cur['phone'] = phone return cur = cur['next'] new_node = {'name': name, 'phone': phone, 'next': ll_head} ll_head = new_node def ll_find(name): cur = ll_head while cur is not None: if cur['name'] == name: return cur['phone'] cur = cur['next'] return None def ll_delete(name): global ll_head if ll_head is None: return if ll_head['name'] == name: ll_head = ll_head['next'] return prev = ll_head cur = ll_head['next'] while cur is not None: if cur['name'] == name: prev['next'] = cur['next'] return prev = cur cur = cur['next'] def ll_list_all(): records = [] cur = ll_head while cur is not None: records.append((cur['name'], cur['phone'])) cur = cur['next'] records.sort(key=lambda x: x[0]) return records BUCKET_COUNT = 10 buckets = [None] * BUCKET_COUNT def hash_func(name): s = 0 for ch in name: s += ord(ch) return s % BUCKET_COUNT def ht_insert(name, phone): global buckets idx = hash_func(name) cur = buckets[idx] while cur is not None: if cur['name'] == name: cur['phone'] = phone return cur = cur['next'] new_node = {'name': name, 'phone': phone, 'next': buckets[idx]} buckets[idx] = new_node def ht_find(name): idx = hash_func(name) cur = buckets[idx] while cur is not None: if cur['name'] == name: return cur['phone'] cur = cur['next'] return None def ht_delete(name): global buckets idx = hash_func(name) head = buckets[idx] if head is None: return if head['name'] == name: buckets[idx] = head['next'] return prev = head cur = head['next'] while cur is not None: if cur['name'] == name: prev['next'] = cur['next'] return prev = cur cur = cur['next'] def ht_list_all(): all_records = [] for head in buckets: cur = head while cur is not None: all_records.append((cur['name'], cur['phone'])) cur = cur['next'] all_records.sort(key=lambda x: x[0]) return all_records bst_root = None def bst_create_node(name, phone): return {'name': name, 'phone': phone, 'left': None, 'right': None} def bst_insert(name, phone): global bst_root if bst_root is None: bst_root = bst_create_node(name, phone) return current = bst_root while True: if name == current['name']: current['phone'] = phone return elif name < current['name']: if current['left'] is None: current['left'] = bst_create_node(name, phone) return current = current['left'] else: if current['right'] is None: current['right'] = bst_create_node(name, phone) return current = current['right'] def bst_find(name): current = bst_root while current is not None: if name == current['name']: return current['phone'] elif name < current['name']: current = current['left'] else: current = current['right'] return None def find_min(node): while node['left'] is not None: node = node['left'] return node def bst_delete(name): global bst_root def delete_rec(node): if node is None: return None if name < node['name']: node['left'] = delete_rec(node['left']) elif name > node['name']: node['right'] = delete_rec(node['right']) else: if node['left'] is None: return node['right'] if node['right'] is None: return node['left'] min_node = find_min(node['right']) node['name'] = min_node['name'] node['phone'] = min_node['phone'] node['right'] = delete_rec(node['right']) return node bst_root = delete_rec(bst_root) def bst_list_all(): result = [] def inorder(node): if node is None: return inorder(node['left']) result.append((node['name'], node['phone'])) inorder(node['right']) inorder(bst_root) return result def generate_records(n): records = [] for i in range(1, n+1): name = f"User_{i:05d}" phone = f"{random.randint(100,999)}-{random.randint(1000,9999)}" records.append((name, phone)) return records def run_experiment(): N = 1000 base = generate_records(N) shuffled = base.copy() random.shuffle(shuffled) sorted_records = sorted(base, key=lambda x: x[0]) structures = [ ('LinkedList', ll_insert, ll_find, ll_delete, ll_list_all), ('HashTable', ht_insert, ht_find, ht_delete, ht_list_all), ('BST', bst_insert, bst_find, bst_delete, bst_list_all) ] all_results = [] # для CSV: список словарей repeats = 5 for mode_name, data in [('random', shuffled), ('sorted', sorted_records)]: for struct_name, ins, fnd, dele, lst in structures: print(f"Testing {struct_name} on {mode_name}...") for rep in range(repeats): # сброс структур global ll_head, buckets, bst_root ll_head = None buckets = [None] * BUCKET_COUNT bst_root = None # вставка t0 = time.perf_counter() for name, phone in data: ins(name, phone) t1 = time.perf_counter() insert_time = t1 - t0 # поиск 110 записей (100 существующих + 10 несуществующих) existing = [name for name, _ in data] sample = random.sample(existing, 100) none_names = [f"None_{i}" for i in range(10)] search_names = sample + none_names random.shuffle(search_names) t0 = time.perf_counter() for name in search_names: fnd(name) t1 = time.perf_counter() find_time = t1 - t0 # удаление 10 записей to_delete = random.sample(existing, 10) t0 = time.perf_counter() for name in to_delete: dele(name) t1 = time.perf_counter() delete_time = t1 - t0 all_results.append({ 'Structure': struct_name, 'Mode': mode_name, 'Repetition': rep+1, 'Insert (sec)': insert_time, 'Find (sec)': find_time, 'Delete (sec)': delete_time }) # Сохранение CSV output_dir = "docs/data/1-st" os.makedirs(output_dir, exist_ok=True) csv_path = os.path.join(output_dir, "experiment_results.csv") with open(csv_path, 'w', newline='', encoding='utf-8') as f: fieldnames = ['Structure', 'Mode', 'Repetition', 'Insert (sec)', 'Find (sec)', 'Delete (sec)'] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(all_results) print(f"\nРезультаты сохранены в {csv_path}") avg_data = {} for r in all_results: key = (r['Structure'], r['Mode']) if key not in avg_data: avg_data[key] = {'Insert': [], 'Find': [], 'Delete': []} avg_data[key]['Insert'].append(r['Insert (sec)']) avg_data[key]['Find'].append(r['Find (sec)']) avg_data[key]['Delete'].append(r['Delete (sec)']) structures_list = ['LinkedList', 'HashTable', 'BST'] modes_list = ['random', 'sorted'] insert_vals = {mode: [] for mode in modes_list} find_vals = {mode: [] for mode in modes_list} delete_vals = {mode: [] for mode in modes_list} for mode in modes_list: for struct in structures_list: key = (struct, mode) if key in avg_data: insert_avg = sum(avg_data[key]['Insert']) / len(avg_data[key]['Insert']) find_avg = sum(avg_data[key]['Find']) / len(avg_data[key]['Find']) delete_avg = sum(avg_data[key]['Delete']) / len(avg_data[key]['Delete']) else: insert_avg = find_avg = delete_avg = 0 insert_vals[mode].append(insert_avg) find_vals[mode].append(find_avg) delete_vals[mode].append(delete_avg) # Рисуем три столбчатые диаграммы fig, axes = plt.subplots(1, 3, figsize=(15, 5)) x = np.arange(len(structures_list)) width = 0.35 for ax, op_data, op_label, ylabel in zip( axes, [insert_vals, find_vals, delete_vals], ['Insert', 'Find', 'Delete'], ['Время вставки (с)', 'Время поиска (с)', 'Время удаления (с)'] ): random_vals = op_data['random'] sorted_vals = op_data['sorted'] ax.bar(x - width/2, random_vals, width, label='Случайный порядок', color='skyblue') ax.bar(x + width/2, sorted_vals, width, label='Отсортированный порядок', color='salmon') ax.set_xticks(x) ax.set_xticklabels(structures_list) ax.set_ylabel(ylabel) ax.set_title(op_label) ax.legend() plt.tight_layout() png_path = os.path.join(output_dir, "performance_comparison.png") plt.savefig(png_path, dpi=150) print(f"График сохранён в {png_path}") plt.show() # Вывод средних значений в консоль (для отчёта) print("\nСредние значения (сек):") print("Структура\tРежим\tВставка\tПоиск\tУдаление") for (struct, mode), vals in avg_data.items(): ins_avg = sum(vals['Insert'])/len(vals['Insert']) find_avg = sum(vals['Find'])/len(vals['Find']) del_avg = sum(vals['Delete'])/len(vals['Delete']) print(f"{struct}\t{mode}\t{ins_avg:.6f}\t{find_avg:.6f}\t{del_avg:.6f}") if __name__ == '__main__': run_experiment()