diff --git a/konnovaea/docs/отчет.ipynb b/konnovaea/docs/отчет.ipynb index e69de29..5d2e54d 100644 --- a/konnovaea/docs/отчет.ipynb +++ b/konnovaea/docs/отчет.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d7f65344", + "metadata": {}, + "source": [ + "# Отчёт \n", + "## Телефонный справочник: реализация и сравнение структур данных\n", + "\n", + "**Студент:** Коннова Е.А.\n", + "**Группа:** 429\n", + "**Дата:**" + ] + }, + { + "cell_type": "markdown", + "id": "f69aa231", + "metadata": {}, + "source": [ + "## Введение\n", + "\n", + "### О чем работа.\n", + "В данной работе рассматриваются три базовые структуры данных:\n", + "- Связный список (LinkedList)\n", + "- Хеш-таблица (HashTable)\n", + "- Двоичное дерево поиска (BST)\n", + "\n", + "Они применяются для хранения записей телефонного справочника.\n", + "\n", + "### Цель всей работы\n", + "Реализовать три структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций (вставка, поиск, удаление).\n", + "\n", + "### Задачи по достижению цели\n", + "1. Реализовать связный список с операциями insert, find, delete, list_all\n", + "2. Реализовать хеш-таблицу на основе связных списков\n", + "3. Реализовать двоичное дерево поиска\n", + "4. Сгенерировать тестовые данные (10000 записей)\n", + "5. Провести замеры времени для каждой структуры (5 повторений)\n", + "6. Сравнить результаты и сделать выводы" + ] + }, + { + "cell_type": "markdown", + "id": "56e2f617", + "metadata": {}, + "source": [ + "## Часть 1. Общая информация о структурах данных\n", + "\n", + "### 1.1 Для неспециалистов\n", + "\n", + "**Что такое структура данных?**\n", + "Это способ организации и хранения данных в компьютере.\n", + "\n", + "**Три структуры из работы:**\n", + "\n", + "| Структура | Как работает | Пример из жизни |\n", + "|-----------|--------------|-----------------|\n", + "| Связный список | Цепочка элементов, где каждый знает следующий | Верёвка с узелками |\n", + "| Хеш-таблица | Массив корзин, элемент попадает в корзину по номеру | Картотека с ящиками |\n", + "| Двоичное дерево | Иерархическая структура: левые меньше, правые больше | Телефонный справочник |\n", + "\n", + "### 1.2 Обзор технологий\n", + "\n", + "**Связный список**\n", + "- Узел: `{'name': str, 'phone': str, 'next': None}`\n", + "- Вставка: O(1) в начало, O(n) в конец\n", + "- Поиск: O(n) - линейный обход\n", + "- Удаление: O(n) - сначала найти\n", + "\n", + "**Хеш-таблица**\n", + "- Корзины: список из None или голов списков\n", + "- Хеш-функция: `hash = (hash * 31 + ord(ch)) % size`\n", + "- Вставка/поиск/удаление: O(1) в среднем\n", + "\n", + "**Двоичное дерево поиска (BST)**\n", + "- Узел: `{'name': str, 'phone': str, 'left': None, 'right': None}`\n", + "- Вставка/поиск/удаление: O(log n) в среднем, O(n) в худшем\n", + "\n", + "### 1.3 Обоснование выбора подхода\n", + "\n", + "**Почему именно эти структуры?**\n", + "1. Они фундаментальны и изучаются в курсе\n", + "2. Показывают разные компромиссы (скорость vs порядок)\n", + "3. Позволяют наглядно сравнить производительность\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "d9327709", + "metadata": {}, + "source": [ + "## Часть 2. Техническая реализация\n", + "\n", + "### 2.1 Постановка задачи\n", + "\n", + "Реализовать телефонный справочник с операциями:\n", + "- `insert(name, phone)` — добавить или обновить запись\n", + "- `find(name)` — вернуть телефон или None\n", + "- `delete(name)` — удалить запись\n", + "- `list_all()` — вернуть все записи, отсортированные по имени\n", + "\n", + "### 2.2 Верхнеуровневое решение\n", + "\n", + "**Связный список (LinkedList)**\n", + "- `ll_insert(head, name, phone)` — добавление в конец, возвращает голову\n", + "- `ll_find(head, name)` — линейный поиск, возвращает телефон или None\n", + "- `ll_delete(head, name)` — удаление с перепривязкой, возвращает голову\n", + "- `ll_list_all(head)` — сбор всех записей и сортировка\n", + "\n", + "**Хеш-таблица (HashTable)**\n", + "- `hash_function(name, size)` — ключ → номер корзины\n", + "- `ht_create(size)` — создание таблицы\n", + "- `ht_insert(buckets, name, phone)` — вызов ll_insert для нужной корзины\n", + "- `ht_find(buckets, name)` — вызов ll_find для нужной корзины\n", + "- `ht_delete(buckets, name)` — вызов ll_delete для нужной корзины\n", + "- `ht_list_all(buckets)` — сбор из всех корзин + сортировка\n", + "\n", + "**Двоичное дерево (BST)**\n", + "- `bst_insert(root, name, phone)` — итеративная вставка\n", + "- `bst_find(root, name)` — поиск\n", + "- `bst_delete(root, name)` — удаление с поиском преемника\n", + "- `bst_list_all(root)` — in-order обход (уже отсортировано)" + ] + }, + { + "cell_type": "markdown", + "id": "c1cd08d8", + "metadata": {}, + "source": [ + "## Часть 3. Эксперименты и результаты\n", + "\n", + "### 3.1 Инструменты и методика\n", + "\n", + "**Параметры эксперимента:**\n", + "- Количество записей: 10 000\n", + "- Количество повторений: 5\n", + "- Поиск: 100 существующих + 10 несуществующих\n", + "- Удаление: 50 случайных записей\n", + "- Режимы: случайный порядок, отсортированный порядок\n", + "\n", + "### 3.2 Результаты" + ] + }, + { + "cell_type": "markdown", + "id": "94634c57", + "metadata": {}, + "source": [ + "![Таблица результатов](data/table_results.png)\n", + "\n", + "*Таблица 1 - Результаты экспериментов (среднее время в секундах)*" + ] + }, + { + "cell_type": "markdown", + "id": "5689bbd0", + "metadata": {}, + "source": [ + "### 3.3 Графики\n", + "\n", + "#### График 1: Время вставки 10000 записей\n", + "\n", + "![Вставка](data/graph_insert.png)\n", + "\n", + "#### График 2: Время поиска 110 записей\n", + "\n", + "![Поиск](data/graph_search.png)\n", + "\n", + "#### График 3: Время удаления 50 записей\n", + "\n", + "![Удаление](data/graph_delete.png)" + ] + }, + { + "cell_type": "markdown", + "id": "5561d9dd", + "metadata": {}, + "source": [ + "### 3.4 Сравнение и анализ\n", + "\n", + "**Как порядок входных данных влияет на BST?**\n", + "\n", + "| Режим | Вставка | Поиск | Удаление |\n", + "|-------|---------|-------|----------|\n", + "| Случайный | 0.026 сек | 0.00026 сек | 0.00017 сек |\n", + "| Отсортированный | 4.931 сек | 0.047 сек | 0.023 сек |\n", + "\n", + "Вывод: На случайных данных BST работает быстро (O(log n)). На отсортированных данных BST вырождается в связный список (O(n)). Работает медленее в 190 раз.\n", + "\n", + "**Техническая ошибка:** Из-за ограничения глубины рекурсии в Python (1000 вызовов) рекурсивная реализация BST не смогла бы обработать 10000 записей. Поэтому все операции BST были реализованы итеративно.\n", + "\n", + "**Почему хеш-таблица не чувствительна к порядку?**\n", + "\n", + "Хеш-функция распределяет записи по корзинам независимо от порядка вставки. Распределение по корзинам равномерное.\n", + "\n", + "**Почему связный список всегда медленен при поиске?**\n", + "\n", + "Связный список не имеет индексов. Поэтому нужно перебирать элементы последовательно. Сложность поиска - O(n).\n", + "\n", + "**Как удаление работает в каждой структуре?**\n", + "\n", + "| Структура | Сложность |\n", + "|-----------|-----------|\n", + "| LinkedList | O(n) |\n", + "| HashTable | O(1) |\n", + "| BST | O(log n) / O(n) |\n", + "\n", + "В связных списках сначала нужно найти, потом перепривязать. В хеш-таблице сразу находишь корзину по хешу. В двоичном дереве нужно найти узел и перестроить поддеревья." + ] + }, + { + "cell_type": "markdown", + "id": "a57d1502", + "metadata": {}, + "source": [ + "## Заключение\n", + "\n", + "### Выводы из каждой части\n", + "\n", + "**Из части 1:**\n", + "- Каждая структура имеет свои теоретические характеристики\n", + "- Связный список - прост, но медленен\n", + "- Хеш-таблица - быстра, но не сохраняет порядок\n", + "- BST - быстр и сохраняет порядок, но требует балансировки\n", + "\n", + "**Из части 2:**\n", + "- Все три структуры успешно реализованы\n", + "- Хеш-таблица использует связный список для корзин\n", + "- BST написан итеративно для избежания RecursionError\n", + "\n", + "**Из части 3:**\n", + "- Эксперименты подтвердили теоретические оценки\n", + "- BST на отсортированных данных деградирует\n", + "- Хеш-таблица стабильна независимо от порядка\n", + "\n", + "### Итоговая рекомендация\n", + "\n", + "| Сценарий | Рекомендация | Причина |\n", + "|----------|--------------|---------|\n", + "| Частый поиск по ключу | Хеш-таблица | O(1) |\n", + "| Частые вставки/удаления | Хеш-таблица | Стабильная скорость |\n", + "| Нужен отсортированный вывод | Сбалансированное дерево | In-order обход |\n", + "| Данные поступают отсортированно | Хеш-таблица | BST деградирует |\n", + "| Мало данных (<100) | Любая | Разница незаметна |\n", + "\n", + "**Для телефонного справочника лучший выбор - хеш-таблица.**" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/konnovaea/make_graphs.py b/konnovaea/make_graphs.py index 99312f6..e23e7b6 100644 --- a/konnovaea/make_graphs.py +++ b/konnovaea/make_graphs.py @@ -9,15 +9,12 @@ os.makedirs('docs/data', exist_ok=True) structures = ['LinkedList', 'HashTable', 'BST'] -# Вставка 10000 записей random_insert = [0.0037545, 0.015088, 0.026280] sorted_insert = [0.0017544, 0.011369, 4.930788] -# Поиск 110 записей random_search = [0.00000962, 0.0001646, 0.0002592] sorted_search = [0.00000858, 0.00014016, 0.047126] -# Удаление 50 записей random_delete = [0.0000079, 0.00009824, 0.00016984] sorted_delete = [0.00000294, 0.00005878, 0.023013] @@ -56,7 +53,7 @@ ax.grid(True, alpha=0.3, axis='y') plt.tight_layout() plt.savefig('docs/data/graph_insert.png', dpi=150, bbox_inches='tight') plt.close() -print(" График 1 сохранён: docs/data/graph_insert.png") + # график поиск fig, ax = plt.subplots(figsize=(12, 7)) @@ -89,7 +86,7 @@ ax.grid(True, alpha=0.3, axis='y') plt.tight_layout() plt.savefig('docs/data/graph_search.png', dpi=150, bbox_inches='tight') plt.close() -print(" График 2 сохранён: docs/data/graph_search.png") + # график удаление fig, ax = plt.subplots(figsize=(12, 7)) @@ -122,5 +119,5 @@ ax.grid(True, alpha=0.3, axis='y') plt.tight_layout() plt.savefig('docs/data/graph_delete.png', dpi=150, bbox_inches='tight') plt.close() -print(" График 3 сохранён: docs/data/graph_delete.png") + diff --git a/konnovaea/make_tables.py b/konnovaea/make_tables.py index fe4c880..211d6a5 100644 --- a/konnovaea/make_tables.py +++ b/konnovaea/make_tables.py @@ -32,5 +32,3 @@ for i, row in enumerate(data): plt.title('Результаты экспериментов (среднее время в секундах)', fontsize=14, fontweight='bold', pad=20) plt.savefig('docs/data/table_results.png', dpi=200, bbox_inches='tight', facecolor='white') plt.close() - -print("Таблица сохранена: docs/data/table_results.png") \ No newline at end of file