2026-rff_mp/rezults.py
2026-05-21 19:38:10 +03:00

288 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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} раз")