2026-rff_mp/rezults.py

288 lines
15 KiB
Python
Raw Normal View History

2026-05-21 16:38:10 +00:00
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} раз")