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} с - Упорядоченный режим: {ht_sorted_insert:.4f} с - Различие: {ht_sorted_insert/ht_random_insert:.2f} ### 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. **Связный список — нишевый инструмент**, эффективный только при работе с малыми объёмами данных. """ 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} раз")