import csv import os import numpy as np from matplotlib import pyplot as plt def ensure_directories(): os.makedirs('docs/data', exist_ok=True) def save_to_csv(results, filename="docs/data/results.csv"): ensure_directories() with open(filename, 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerow(['Структура', 'Режим', 'Операция', 'Повтор1', 'Повтор2', 'Повтор3', 'Повтор4', 'Повтор5', 'Среднее', 'Стд_откл']) for res in results: struct_name = res['structure'] mode = res['mode'] for op, times, mean, std in [ ('вставка', res['insert_all'], res['insert_mean'], res['insert_std']), ('поиск', res['find_all'], res['find_mean'], res['find_std']), ('удаление', res['delete_all'], res['delete_mean'], res['delete_std']) ]: row = [struct_name, mode, op] + times + [mean, std] writer.writerow(row) def plot_results(results, filename="docs/performance_chart.png"): ensure_directories() struct_names = { 'linkedlist': 'LinkedList', 'hashtable': 'HashTable', 'bst': 'BST' } operations = ['insert', 'find', 'delete'] op_names = {'insert': 'Вставка', 'find': 'Поиск', 'delete': 'Удаление'} random_data = {} sorted_data = {} for res in results: struct_name = struct_names.get(res['structure'], res['structure']) mode = res['mode'] if mode == 'случайный': random_data[struct_name] = { 'insert': res['insert_mean'], 'find': res['find_mean'], 'delete': res['delete_mean'] } else: sorted_data[struct_name] = { 'insert': res['insert_mean'], 'find': res['find_mean'], 'delete': res['delete_mean'] } structure_order = ['LinkedList', 'HashTable', 'BST'] fig, axes = plt.subplots(1, 3, figsize=(15, 5)) for idx, op in enumerate(operations): ax = axes[idx] x = np.arange(len(structure_order)) width = 0.35 random_means = [] sorted_means = [] for struct in structure_order: if struct in random_data: random_means.append(random_data[struct][op]) else: random_means.append(0) if struct in sorted_data: sorted_means.append(sorted_data[struct][op]) else: sorted_means.append(0) if not random_means and not sorted_means: print(f" Нет данных для операции {op}") continue bars1 = ax.bar(x - width/2, random_means, width, label='Случайный порядок', color='skyblue') bars2 = ax.bar(x + width/2, sorted_means, width, label='Отсортированный порядок', color='salmon') ax.set_xlabel('Структура данных') ax.set_ylabel('Время (секунды)') ax.set_title(f'{op_names.get(op, op)}') ax.set_xticks(x) ax.set_xticklabels(structure_order) ax.legend() for bar in bars1 + bars2: height = bar.get_height() if height > 0: ax.annotate(f'{height:.3f}', xy=(bar.get_x() + bar.get_width() / 2, height), xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=8) plt.tight_layout() plt.savefig(filename, dpi=150) plt.show() def save_report_md(results, filename="docs/report.md"): ensure_directories() results_dict = {} for res in results: key = (res['structure'], res['mode']) results_dict[key] = res def get_val(struct, mode, field): key = (struct, mode) if key in results_dict: return results_dict[key][field] return 0.0 ll_random_insert = get_val('linkedlist', 'случайный', 'insert_mean') ll_random_find = get_val('linkedlist', 'случайный', 'find_mean') ll_random_delete = get_val('linkedlist', 'случайный', 'delete_mean') ll_sorted_insert = get_val('linkedlist', 'отсортированный', 'insert_mean') ll_sorted_find = get_val('linkedlist', 'отсортированный', 'find_mean') ll_sorted_delete = get_val('linkedlist', 'отсортированный', 'delete_mean') ht_random_insert = get_val('hashtable', 'случайный', 'insert_mean') ht_random_find = get_val('hashtable', 'случайный', 'find_mean') ht_random_delete = get_val('hashtable', 'случайный', 'delete_mean') ht_sorted_insert = get_val('hashtable', 'отсортированный', 'insert_mean') ht_sorted_find = get_val('hashtable', 'отсортированный', 'find_mean') ht_sorted_delete = get_val('hashtable', 'отсортированный', 'delete_mean') bst_random_insert = get_val('bst', 'случайный', 'insert_mean') bst_random_find = get_val('bst', 'случайный', 'find_mean') bst_random_delete = get_val('bst', 'случайный', 'delete_mean') bst_sorted_insert = get_val('bst', 'отсортированный', 'insert_mean') bst_sorted_find = get_val('bst', 'отсортированный', 'find_mean') bst_sorted_delete = get_val('bst', 'отсортированный', 'delete_mean') from datetime import datetime report_content = f"""# Отчёт по лабораторной работе ## Цель работы Реализовать три структуры данных «с нуля» (связный список, хеш-таблица, двоичное дерево поиска), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций. ## Параметры эксперимента - Количество записей: 10000 - Количество повторов каждого теста: 5 - Размер хеш-таблицы: 1000 корзин ## Результаты экспериментов ### 1. Связный список | Режим | Вставка (сек) | Поиск (сек) | Удаление (сек) | |-------|---------------|-------------|----------------| | Случайный | {ll_random_insert:.4f} | {ll_random_find:.4f} | {ll_random_delete:.4f} | | Отсортированный | {ll_sorted_insert:.4f} | {ll_sorted_find:.4f} | {ll_sorted_delete:.4f} | ### 2. Хеш-таблица | Режим | Вставка (сек) | Поиск (сек) | Удаление (сек) | |-------|---------------|-------------|----------------| | Случайный | {ht_random_insert:.4f} | {ht_random_find:.4f} | {ht_random_delete:.4f} | | Отсортированный | {ht_sorted_insert:.4f} | {ht_sorted_find:.4f} | {ht_sorted_delete:.4f} | ### 3. Двоичное дерево поиска (BST) | Режим | Вставка (сек) | Поиск (сек) | Удаление (сек) | |-------|---------------|-------------|----------------| | Случайный | {bst_random_insert:.4f} | {bst_random_find:.4f} | {bst_random_delete:.4f} | | Отсортированный | {bst_sorted_insert:.4f} | {bst_sorted_find:.4f} | {bst_sorted_delete:.4f} | ## Анализ результатов ### 1. Влияние порядка данных на BST На отсортированных данных BST деградирует с O(log n) до O(n). Время вставки увеличилось с {bst_random_insert:.4f} до {bst_sorted_insert:.4f} секунд — в {bst_sorted_insert/bst_random_insert:.1f} раз. ### 2. Почему хеш-таблица не чувствительна к порядку Хеш-функция распределяет элементы случайно, порядок ввода не влияет на позицию элемента. Разница между случайным и отсортированным порядком: - Вставка: {ht_random_insert:.4f} vs {ht_sorted_insert:.4f} - Отношение: {ht_sorted_insert/ht_random_insert:.2f}x (почти не чувствительна) ### 3. Почему связный список медленный при поиске Поиск требует последовательного прохода O(n) без возможности индексации. Поэтому связный список хорош только когда записей мало. Для больших телефонных справочников он не подходит. Сравнение скорости поиска (случайные данные): - LinkedList: {ll_random_find:.4f} сек - HashTable: {ht_random_find:.4f} сек (в {ll_random_find/ht_random_find:.1f} раз быстрее) - BST: {bst_random_find:.4f} сек ### 4. Сравнение удаления | Структура | Сложность | Время на 50 удалений (случайные данные) | |-----------|-----------|------------------------------------------| | Связный список | O(n) | {ll_random_delete:.4f} сек | | Хеш-таблица | O(1) в среднем | {ht_random_delete:.4f} сек | | BST | O(log n) в среднем | {bst_random_delete:.4f} сек | ## Вывод: | Задача | Рекомендация | Почему | |--------|-------------|--------| | Частый поиск | Хеш-таблица | O(1) в среднем, не зависит от порядка | | Частые вставки/удаления | Хеш-таблица | Амортизированное O(1) | | Нужен отсортированный вывод | Сбалансированное дерево (AVL/Red-Black) | In-order обход даёт сортировку | | Мало данных (<100 элементов) | Связный список или массив | Простота, накладные расходы не оправданы | | Последовательный доступ (очередь/стек) | Связный список | Вставка/удаление в начало/конец за O(1) | ## Заключение Эксперимент наглядно демонстрирует: 1. **BST без балансировки опасен** — на отсортированных данных он деградирует до O(n) 2. **Хеш-таблица стабильна** — её производительность не зависит от порядка входных данных 3. **Связный список** подходит только для специфических задач с малым объёмом данных ## Дата выполнения {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} """ with open(filename, 'w', encoding='utf-8') as f: f.write(report_content) def print_analysis(results): print("\n" + "="*60) print("Анализ резов") print("="*60) best_insert = min(results, key=lambda x: x['insert_mean']) best_find = min(results, key=lambda x: x['find_mean']) best_delete = min(results, key=lambda x: x['delete_mean']) print(f"\n Лучшая для вставки: {best_insert['structure']} ({best_insert['mode']}) - {best_insert['insert_mean']:.4f} сек") print(f" Лучшая для поиска: {best_find['structure']} ({best_find['mode']}) - {best_find['find_mean']:.4f} сек") print(f" Лучшая для удаления: {best_delete['structure']} ({best_delete['mode']}) - {best_delete['delete_mean']:.4f} сек") bst_random = None bst_sorted = None for res in results: if res['structure'] == 'bst' and res['mode'] == 'случайный': bst_random = res elif res['structure'] == 'bst' and res['mode'] == 'отсортированный': bst_sorted = res if bst_random and bst_sorted: print("\n Влияние порядка данных на BST:") print(f" Вставка: случайный {bst_random['insert_mean']:.4f} сек vs отсортированный {bst_sorted['insert_mean']:.4f} сек") print(f" Деградация в {bst_sorted['insert_mean']/bst_random['insert_mean']:.1f}x") ht_random = None ht_sorted = None for res in results: if res['structure'] == 'hashtable' and res['mode'] == 'случайный': ht_random = res elif res['structure'] == 'hashtable' and res['mode'] == 'отсортированный': ht_sorted = res if ht_random and ht_sorted: print("\n Чувствительность хеш-таблицы к порядку:") print(f" Вставка: случайный {ht_random['insert_mean']:.4f} сек vs отсортированный {ht_sorted['insert_mean']:.4f} сек") print(f" Отношение: {ht_sorted['insert_mean']/ht_random['insert_mean']:.2f}x (почти не чувствительна)") ll_random = None for res in results: if res['structure'] == 'linkedlist' and res['mode'] == 'случайный': ll_random = res elif res['structure'] == 'hashtable' and res['mode'] == 'случайный': ht_random = res if ll_random and ht_random: print("\n Сравнение скорости поиска:") print(f" LinkedList: {ll_random['find_mean']:.4f} сек") print(f" HashTable: {ht_random['find_mean']:.4f} сек") print(f" HashTable быстрее в {ll_random['find_mean']/ht_random['find_mean']:.1f} раз")