2026-rff_mp/zhigalovrd/lab1/docs/report.md

207 lines
12 KiB
Markdown
Raw Normal View History

2026-05-23 17:54:44 +00:00
report_md = '''# Отчёт: Сравнение структур данных для телефонного справочника
## Цель работы
Реализовать три структуры данных «с нуля» в процедурной парадигме (без классов), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций: вставки, поиска и удаления.
---
## 1. Реализация структур данных
### 1.1 Связный список (`linked_list.py`)
Узел представлен словарём:
```python
{'name': str, 'phone': str, 'next': Node | None}
```
**Операции:**
| Функция | Описание | Сложность |
|---------|----------|-----------|
| `ll_insert(head, name, phone)` | Вставка в конец (или обновление) | O(n) |
| `ll_find(head, name)` | Линейный поиск | O(n) |
| `ll_delete(head, name)` | Удаление с перестройкой связей | O(n) |
| `ll_list_all(head)` | Сбор всех записей + сортировка | O(n log n) |
### 1.2 Хеш-таблица (`hash_table.py`)
Хранится как список бакетов фиксированной длины. Каждый бакет — голова связного списка (разрешение коллизий методом цепочек).
**Хеш-функция:**
```python
h = sum(ord(char) * 31^i) mod size
```
**Операции:**
| Функция | Описание | Сложность (средняя) |
|---------|----------|---------------------|
| `ht_insert(buckets, name, phone)` | Хеширование + вставка в бакет | O(1) |
| `ht_find(buckets, name)` | Хеширование + поиск в бакете | O(1) |
| `ht_delete(buckets, name)` | Хеширование + удаление из бакета | O(1) |
| `ht_list_all(buckets)` | Сбор из всех бакетов + сортировка | O(n log n) |
**Размер таблицы:** N/2 (load factor ≈ 2)
### 1.3 Двоичное дерево поиска (`bst.py`)
Узел представлен словарём:
```python
{'name': str, 'phone': str, 'left': Node | None, 'right': Node | None}
```
**Операции:**
| Функция | Описание | Сложность (средняя / худшая) |
|---------|----------|------------------------------|
| `bst_insert(root, name, phone)` | Рекурсивная вставка | O(log n) / O(n) |
| `bst_find(root, name)` | Рекурсивный поиск | O(log n) / O(n) |
| `bst_delete(root, name)` | Удаление (0/1/2 потомка) | O(log n) / O(n) |
| `bst_list_all(root)` | In-order обход | O(n) |
---
## 2. Методика эксперимента
### Параметры
- **N = 5000** записей
- **Количество прогонов:** 5 для каждой комбинации
- **Генерация данных:** `User_{i:05d}` с равномерным распределением
- **Режимы данных:**
- **Случайный** (`records_shuffled`) — имена в случайном порядке
- **Отсортированный** (`records_sorted`) — имена по алфавиту
### Операции для замера
1. **Вставка:** все N записей
2. **Поиск:** 100 существующих + 10 несуществующих имён = 110 вызовов
3. **Удаление:** 50 случайных записей
### Инструменты
- `time.perf_counter()` для замера времени
- `matplotlib` для визуализации
- `csv` для сохранения результатов
---
## 3. Результаты экспериментов
### 3.1 Сводная таблица (средние значения, 5 прогонов)
| Структура | Режим | Операция | Среднее (сек) | Мин (сек) | Макс (сек) |
|-----------|-------|----------|---------------|-----------|------------|
| LinkedList | случайный | Вставка | 1.287 | 1.279 | 1.301 |
| LinkedList | случайный | Поиск | 0.024 | 0.024 | 0.025 |
| LinkedList | случайный | Удаление | 0.016 | 0.016 | 0.016 |
| LinkedList | отсортированный | Вставка | 1.165 | 1.156 | 1.176 |
| LinkedList | отсортированный | Поиск | 0.020 | 0.020 | 0.021 |
| LinkedList | отсортированный | Удаление | 0.014 | 0.014 | 0.014 |
| HashTable | случайный | Вставка | 0.025 | 0.010 | 0.079 |
| HashTable | случайный | Поиск | 0.0002 | 0.0002 | 0.0002 |
| HashTable | случайный | Удаление | 0.0001 | 0.0001 | 0.0001 |
| HashTable | отсортированный | Вставка | 0.010 | 0.010 | 0.010 |
| HashTable | отсортированный | Поиск | 0.0002 | 0.0002 | 0.0002 |
| HashTable | отсортированный | Удаление | 0.0001 | 0.0001 | 0.0001 |
| BST | случайный | Вставка | 0.018 | 0.016 | 0.021 |
| BST | случайный | Поиск | 0.0003 | 0.0002 | 0.0003 |
| BST | случайный | Удаление | 0.0002 | 0.0002 | 0.0002 |
| BST | отсортированный | Вставка | **3.388** | 3.372 | 3.416 |
| BST | отсортированный | Поиск | 0.052 | 0.051 | 0.055 |
| BST | отсортированный | Удаление | 0.037 | 0.037 | 0.038 |
---
## 4. Анализ результатов
### 4.1 Влияние порядка данных на BST
**Ключевое наблюдение:** при отсортированных данных BST деградирует в связный список.
- **Случайный порядок:** вставка 5000 записей занимает **0.018 сек** — дерево сбалансировано, высота ~log₂(5000) ≈ 13.
- **Отсортированный порядок:** вставка занимает **3.388 сек** — дерево вырождается в линейную цепочку, высота = 5000.
**Вывод:** BST крайне чувствителен к порядку входных данных. Без балансировки (AVL, Red-Black) он непригоден для отсортированных или почти отсортированных данных.
### 4.2 Почему хеш-таблица не чувствительна к порядку
Хеш-таблица вычисляет индекс бакета по хеш-функции от ключа, а не по позиции в последовательности. Порядок вставки не влияет на распределение по бакетам:
- **Случайный:** 0.025 сек
- **Отсортированный:** 0.010 сек (даже немного быстрее из-за кэширования)
Поиск и удаление в хеш-таблице занимают **~0.0002 сек** — практически константное время O(1).
### 4.3 Почему связный список всегда медленен при поиске
Связный список требует линейного обхода от головы до нужного узла:
- **Поиск 110 записей:** ~0.024 сек (в среднем ~0.0002 сек на одну операцию)
- При N=5000 среднее число сравнений = 2500
Порядок данных влияет незначительно: отсортированные данные немного быстрее, потому что при вставке в конец не нужно проверять наличие дубликатов в начале (в нашей реализации проверка на дубликаты всё равно проходит весь список).
### 4.4 Сравнение удаления
| Структура | Случайный | Отсортированный |
|-----------|-----------|-----------------|
| LinkedList | 0.016 сек | 0.014 сек |
| HashTable | 0.0001 сек | 0.0001 сек |
| BST | 0.0002 сек | 0.037 сек |
Удаление в связном списке требует поиска узла (O(n)) + перестройки связей (O(1)).
Удаление в хеш-таблице — поиск в бакете (O(1) в среднем).
Удаление в BST — поиск + перестройка дерева. При вырожденном дереве — O(n).
---
## 5. Выводы и рекомендации
### Какую структуру выбрать?
| Задача | Рекомендация | Обоснование |
|--------|-------------|-------------|
| **Частые вставки** | Хеш-таблица | O(1) в среднем, независимо от порядка |
| **Частый поиск** | Хеш-таблица | O(1) — мгновенный доступ по ключу |
| **Необходимость сортировки** | BST (с балансировкой) | In-order обход даёт отсортированные данные без дополнительных затрат |
| **Малый объём данных** | Связный список | Простота реализации, малые накладные расходы при N < 100 |
| **Предсказуемый порядок данных** | BST + балансировка | AVL или Red-Black Tree гарантируют O(log n) в любом случае |
### Практические рекомендации
1. **Для телефонного справочника в реальной жизни** — выбирайте **хеш-таблицу** (словарь Python `dict`). Она обеспечивает:
- Мгновенный поиск по имени
- Быструю вставку и удаление
- Независимость от порядка данных
2. **Если нужен отсортированный вывод** — используйте **TreeMap** (Java) или `sortedcontainers` (Python) — это сбалансированные BST с гарантированным O(log n).
3. **Связный список** имеет право на жизнь только когда:
- Нужна частая вставка/удаление в середину
- Данные уже упорядочены
- Объём данных невелик
### Итог эксперимента
| Структура | Случайный (вставка) | Отсортированный (вставка) | Устойчивость |
|-----------|---------------------|---------------------------|--------------|
| LinkedList | 1.29 сек | 1.16 сек | ✅ Стабильна, но медленна |
| HashTable | 0.025 сек | 0.010 сек | ✅ Лучшая устойчивость |
| BST | 0.018 сек | **3.39 сек** | ❌ Катастрофа при sorted |
---
## Приложения
- **Исходный код:** `src/linked_list.py`, `src/hash_table.py`, `src/bst.py`, `src/experiment.py`
- **Сырые данные:** `docs/data/results_raw.csv`
- **Сводная таблица:** `docs/data/results_summary.csv`
- **Графики:** `docs/data/charts.png`
---
*Отчёт подготовлен в рамках лабораторной работы по дисциплине «Структуры данных».*
'''
with open('/mnt/agents/output/lab1/docs/report.md', 'w', encoding='utf-8') as f:
f.write(report_md)
print("✅ report.md создан")