diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..614e5c2 Binary files /dev/null and b/.DS_Store differ diff --git a/osipovamd/.DS_Store b/osipovamd/.DS_Store new file mode 100644 index 0000000..a0ef153 Binary files /dev/null and b/osipovamd/.DS_Store differ diff --git a/osipovamd/docs/.DS_Store b/osipovamd/docs/.DS_Store new file mode 100644 index 0000000..c9ae2c2 Binary files /dev/null and b/osipovamd/docs/.DS_Store differ diff --git a/osipovamd/docs/Task1.docx b/osipovamd/docs/Task1.docx new file mode 100644 index 0000000..0ff50ec Binary files /dev/null and b/osipovamd/docs/Task1.docx differ diff --git a/osipovamd/docs/data/.DS_Store b/osipovamd/docs/data/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/osipovamd/docs/data/.DS_Store differ diff --git a/osipovamd/docs/results.csv b/osipovamd/docs/results.csv new file mode 100644 index 0000000..d3f701d --- /dev/null +++ b/osipovamd/docs/results.csv @@ -0,0 +1,19 @@ +Структура,Режим,Операция,Замер1,Замер2,Замер3,Среднее +LinkedList,случайный,вставка,0.014099,0.014181,0.014180,0.014153 +LinkedList,случайный,поиск,0.001849,0.001841,0.001897,0.001862 +LinkedList,случайный,удаление,0.000859,0.000863,0.000825,0.000849 +LinkedList,отсортированный,вставка,0.012913,0.012512,0.012566,0.012663 +LinkedList,отсортированный,поиск,0.001616,0.001457,0.001696,0.001590 +LinkedList,отсортированный,удаление,0.000992,0.000931,0.000940,0.000954 +HashTable,случайный,вставка,0.001368,0.001415,0.001385,0.001389 +HashTable,случайный,поиск,0.000162,0.000153,0.000146,0.000153 +HashTable,случайный,удаление,0.000076,0.000075,0.000075,0.000075 +HashTable,отсортированный,вставка,0.001275,0.001258,0.001376,0.001303 +HashTable,отсортированный,поиск,0.000169,0.000139,0.000140,0.000149 +HashTable,отсортированный,удаление,0.000080,0.000080,0.000079,0.000080 +BST,случайный,вставка,0.000514,0.000482,0.000507,0.000501 +BST,случайный,поиск,0.000081,0.000056,0.000055,0.000064 +BST,случайный,удаление,0.000042,0.000038,0.000039,0.000040 +BST,отсортированный,вставка,0.018947,0.018836,0.018665,0.018816 +BST,отсортированный,поиск,0.002172,0.002200,0.001973,0.002115 +BST,отсортированный,удаление,0.001400,0.001407,0.001390,0.001399 diff --git a/osipovamd/docs/task1.py b/osipovamd/docs/task1.py new file mode 100644 index 0000000..f6f1e25 --- /dev/null +++ b/osipovamd/docs/task1.py @@ -0,0 +1,493 @@ +import time +import random +import csv +import matplotlib.pyplot as plt +import numpy as np +import sys +from collections import defaultdict + +# Увеличиваем лимит рекурсии для BST +sys.setrecursionlimit(10000) + + +def ll_insert(head, name, phone): + current = head + while current: + if current['name'] == name: + current['phone'] = phone + return head + current = current['next'] + new_node = {'name': name, 'phone': phone, 'next': head} + return new_node + +def ll_find(head, name): + current = head + while current: + if current['name'] == name: + return current['phone'] + current = current['next'] + return None + +def ll_delete(head, name): + if not head: + return None + if head['name'] == name: + return head['next'] + current = head + while current['next']: + if current['next']['name'] == name: + current['next'] = current['next']['next'] + return head + current = current['next'] + return head + +def ll_list_all(head): + records = [] + current = head + while current: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) + return records + +def hash_function(name, size): + return sum(ord(c) for c in name) % size + +def ht_create(size=1000): + return [None] * size + +def ht_insert(buckets, name, phone): + index = hash_function(name, len(buckets)) + buckets[index] = ll_insert(buckets[index], name, phone) + +def ht_find(buckets, name): + index = hash_function(name, len(buckets)) + return ll_find(buckets[index], name) + +def ht_delete(buckets, name): + index = hash_function(name, len(buckets)) + buckets[index] = ll_delete(buckets[index], name) + +def ht_list_all(buckets): + records = [] + for bucket in buckets: + current = bucket + while current: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) + return records + +def bst_insert(root, name, phone): + """Итеративная вставка для избежания RecursionError""" + new_node = {'name': name, 'phone': phone, 'left': None, 'right': None} + + if root is None: + return new_node + + current = root + while True: + if name < current['name']: + if current['left'] is None: + current['left'] = new_node + break + current = current['left'] + elif name > current['name']: + if current['right'] is None: + current['right'] = new_node + break + current = current['right'] + else: + current['phone'] = phone + break + + return root + +def bst_find(root, name): + current = root + while current: + if name == current['name']: + return current['phone'] + elif name < current['name']: + current = current['left'] + else: + current = current['right'] + return None + +def bst_find_min(node): + current = node + while current and current['left']: + current = current['left'] + return current + +def bst_delete(root, name): + if root is None: + return None + + if name < root['name']: + root['left'] = bst_delete(root['left'], name) + elif name > root['name']: + root['right'] = bst_delete(root['right'], name) + else: + if root['left'] is None: + return root['right'] + elif root['right'] is None: + return root['left'] + + min_node = bst_find_min(root['right']) + root['name'] = min_node['name'] + root['phone'] = min_node['phone'] + root['right'] = bst_delete(root['right'], min_node['name']) + + return root + +def bst_list_all(root): + records = [] + stack = [] + current = root + + while stack or current: + while current: + stack.append(current) + current = current['left'] + current = stack.pop() + records.append((current['name'], current['phone'])) + current = current['right'] + + return records + +def copy_linked_list(head): + if not head: + return None + new_head = {'name': head['name'], 'phone': head['phone'], 'next': None} + current_new = new_head + current_old = head['next'] + while current_old: + current_new['next'] = {'name': current_old['name'], 'phone': current_old['phone'], 'next': None} + current_new = current_new['next'] + current_old = current_old['next'] + return new_head + +def copy_bst(node): + if not node: + return None + return { + 'name': node['name'], + 'phone': node['phone'], + 'left': copy_bst(node['left']), + 'right': copy_bst(node['right']) + } + + +def generate_test_data(N=10000): + + names = [f"User_{i:05d}" for i in range(N)] + records = [(name, f"+7-999-{random.randint(1000000, 9999999)}") for name in names] + + records_shuffled = records.copy() + random.shuffle(records_shuffled) + + records_sorted = sorted(records, key=lambda x: x[0]) + + return records_shuffled, records_sorted + +def get_test_queries(records, num_existing=100, num_nonexisting=10): + existing_names = [name for name, _ in random.sample(records, min(num_existing, len(records)))] + nonexisting_names = [f"None_{i:05d}" for i in range(num_nonexisting)] + + queries = existing_names + nonexisting_names + random.shuffle(queries) + + return queries + +def get_delete_names(records, num_to_delete=50): + return [name for name, _ in random.sample(records, min(num_to_delete, len(records)))] + + +def measure_insertion(structure_type, records, repeats=3): + times = [] + + for _ in range(repeats): + if structure_type == "LinkedList": + structure = None + insert_func = ll_insert + elif structure_type == "HashTable": + structure = ht_create(2000) + insert_func = ht_insert + elif structure_type == "BST": + structure = None + insert_func = bst_insert + else: + raise ValueError(f"Unknown structure: {structure_type}") + + start = time.perf_counter() + + for name, phone in records: + if structure_type == "HashTable": + insert_func(structure, name, phone) + else: + structure = insert_func(structure, name, phone) + + end = time.perf_counter() + times.append(end - start) + + return times + +def measure_search(structure_type, structure, queries, repeats=3): + times = [] + + for _ in range(repeats): + start = time.perf_counter() + + for name in queries: + if structure_type == "LinkedList": + ll_find(structure, name) + elif structure_type == "HashTable": + ht_find(structure, name) + elif structure_type == "BST": + bst_find(structure, name) + + end = time.perf_counter() + times.append(end - start) + + return times + +def measure_deletion(structure_type, structure, names_to_delete, repeats=3): + times = [] + + for _ in range(repeats): + if structure_type == "LinkedList": + temp_structure = copy_linked_list(structure) + delete_func = ll_delete + + elif structure_type == "HashTable": + temp_structure = structure.copy() + for i in range(len(temp_structure)): + if temp_structure[i]: + temp_structure[i] = copy_linked_list(temp_structure[i]) + delete_func = ht_delete + + elif structure_type == "BST": + temp_structure = copy_bst(structure) + delete_func = bst_delete + + start = time.perf_counter() + + for name in names_to_delete: + if structure_type == "HashTable": + delete_func(temp_structure, name) + else: + temp_structure = delete_func(temp_structure, name) + + end = time.perf_counter() + times.append(end - start) + + return times + +def run_experiment(N=2000): + """Запускает все эксперименты и возвращает результаты""" + + print(f"Генерация тестовых данных (N={N})...") + records_shuffled, records_sorted = generate_test_data(N) + + queries = get_test_queries(records_shuffled, num_existing=100, num_nonexisting=10) + delete_names = get_delete_names(records_shuffled, num_to_delete=50) + + structures = ["LinkedList", "HashTable", "BST"] + modes = ["случайный", "отсортированный"] + + results = [] + + print("\nНачало экспериментов:") + print("=" * 60) + + for structure in structures: + print(f"\nТестирование {structure}...") + + for mode in modes: + print(f" Режим: {mode}") + records = records_shuffled if mode == "случайный" else records_sorted + + # Вставка + print(f" Измерение вставки...") + try: + insert_times = measure_insertion(structure, records, repeats=3) + avg_insert = sum(insert_times) / len(insert_times) + except RecursionError: + print(f" ОШИБКА: Превышена глубина рекурсии при вставке в {structure} для {mode} режима") + continue + + print(f" Создание финальной структуры...") + if structure == "LinkedList": + final_structure = None + for name, phone in records: + final_structure = ll_insert(final_structure, name, phone) + elif structure == "HashTable": + final_structure = ht_create(2000) + for name, phone in records: + ht_insert(final_structure, name, phone) + elif structure == "BST": + final_structure = None + for name, phone in records: + final_structure = bst_insert(final_structure, name, phone) + + print(f" Измерение поиска...") + search_times = measure_search(structure, final_structure, queries, repeats=3) + avg_search = sum(search_times) / len(search_times) + + print(f" Измерение удаления...") + deletion_times = measure_deletion(structure, final_structure, delete_names, repeats=3) + avg_deletion = sum(deletion_times) / len(deletion_times) + + results.append({ + "Структура": structure, + "Режим": mode, + "Операция": "вставка", + "Замеры": insert_times, + "Среднее": avg_insert + }) + results.append({ + "Структура": structure, + "Режим": mode, + "Операция": "поиск", + "Замеры": search_times, + "Среднее": avg_search + }) + results.append({ + "Структура": structure, + "Режим": mode, + "Операция": "удаление", + "Замеры": deletion_times, + "Среднее": avg_deletion + }) + + print(f" Вставка: {avg_insert:.6f} сек") + print(f" Поиск: {avg_search:.6f} сек") + print(f" Удаление: {avg_deletion:.6f} сек") + + return results + + +# ============================================================ +# СОХРАНЕНИЕ РЕЗУЛЬТАТОВ В CSV +# ============================================================ + +import os +import csv +from datetime import datetime + +def save_to_csv(results, filename="results.csv"): + save_dir = "/Users/mariiaos/2026-rff_mp/osipovamd/docs" + filepath = os.path.join(save_dir, filename) + + with open(filepath, "w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(["Структура", "Режим", "Операция", "Замер1", "Замер2", "Замер3", "Среднее"]) + + for res in results: + row = [ + res["Структура"], + res["Режим"], + res["Операция"], + *[f"{t:.6f}" for t in res["Замеры"]], + f"{res['Среднее']:.6f}" + ] + writer.writerow(row) + + print(f"\nРезультаты сохранены в: {filepath}") + return filepath + +def plot_results(results): + + if not results: + print("Нет данных для построения графиков!") + return + + plt.style.use('seaborn-v0_8-darkgrid') + fig, axes = plt.subplots(1, 3, figsize=(15, 5)) + + operations = ["вставка", "поиск", "удаление"] + structures = ["LinkedList", "HashTable", "BST"] + modes = ["случайный", "отсортированный"] + + colors = {'LinkedList': '#FF6B6B', 'HashTable': '#4ECDC4', 'BST': '#45B7D1'} + + for idx, operation in enumerate(operations): + ax = axes[idx] + + x = np.arange(len(modes)) + width = 0.25 + multiplier = 0 + + for structure in structures: + values = [] + for mode in modes: + found = False + for res in results: + if (res["Структура"] == structure and + res["Режим"] == mode and + res["Операция"] == operation): + values.append(res["Среднее"]) + found = True + break + if not found: + values.append(0) + + if max(values) > 0: + offset = width * multiplier + bars = ax.bar(x + offset, values, width, label=structure, color=colors[structure]) + multiplier += 1 + + ax.set_xlabel('Режим данных', fontsize=12) + ax.set_ylabel('Время (секунды)', fontsize=12) + ax.set_title(f'{operation.capitalize()}', fontsize=14, fontweight='bold') + ax.set_xticks(x + width) + ax.set_xticklabels(modes) + ax.legend(loc='upper left') + ax.grid(True, alpha=0.3) + + plt.suptitle('Сравнение производительности структур данных', + fontsize=16, fontweight='bold') + plt.tight_layout() + plt.savefig('performance_comparison.png', dpi=300, bbox_inches='tight') + plt.show() + + print("\nГрафик сохранён как 'performance_comparison.png'") + + +if __name__ == "__main__": + print("=" * 60) + print("ТЕСТИРОВАНИЕ ПРОИЗВОДИТЕЛЬНОСТИ СТРУКТУР ДАННЫХ") + print("=" * 60) + + results = run_experiment(N=1000) + save_to_csv(results) + + if results: + print("\nПостроение графиков...") + plot_results(results) + + print("\n" + "=" * 60) + print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ (среднее время в секундах)") + print("=" * 60) + print(f"{'Структура':<12} {'Режим':<12} {'Вставка':<10} {'Поиск':<10} {'Удаление':<10}") + print("-" * 60) + + for structure in ["LinkedList", "HashTable", "BST"]: + for mode in ["случайный", "отсортированный"]: + insert_time = search_time = delete_time = 0 + for res in results: + if res["Структура"] == structure and res["Режим"] == mode: + if res["Операция"] == "вставка": + insert_time = res["Среднее"] + elif res["Операция"] == "поиск": + search_time = res["Среднее"] + elif res["Операция"] == "удаление": + delete_time = res["Среднее"] + + if insert_time > 0 or search_time > 0 or delete_time > 0: + print(f"{structure:<12} {mode:<12} {insert_time:<10.6f} {search_time:<10.6f} {delete_time:<10.6f}") + else: + print("\nЭксперимент не дал результатов из-за ошибок.") + + print("\nЭксперимент завершён!") \ No newline at end of file diff --git a/osipovamd/docs/~$Task1.docx b/osipovamd/docs/~$Task1.docx new file mode 100644 index 0000000..8d3e3d2 Binary files /dev/null and b/osipovamd/docs/~$Task1.docx differ