forked from UNN/2026-rff_mp
Merge pull request '[1]Laba1' (#203) from Mininavd/2026-rff_mp:MininaLaba1 into develop
Reviewed-on: UNN/2026-rff_mp#203
This commit is contained in:
commit
f105cea289
228
MininaVD/docs/Laba1.ipynb
Normal file
228
MininaVD/docs/Laba1.ipynb
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "170b5222-8069-40d0-b3a0-178fd3215176",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Отчёт по лабораторной работе \n",
|
||||||
|
"## Сравнение производительности структур данных \n",
|
||||||
|
"### (Телефонный справочник: связный список, хеш-таблица, BST)\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 1. Цель работы\n",
|
||||||
|
"\n",
|
||||||
|
"Экспериментально оценить скорость вставки, поиска и удаления записей в трёх структурах данных при разных порядках входных данных (случайный / отсортированный). \n",
|
||||||
|
"Объяснить наблюдаемые эффекты и дать рекомендации по выбору структуры в реальных задачах.\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 2. Условия эксперимента\n",
|
||||||
|
"\n",
|
||||||
|
"| Параметр | Значение |\n",
|
||||||
|
"|----------|----------|\n",
|
||||||
|
"| Количество записей | 10 000 |\n",
|
||||||
|
"| Количество поисков | 110 (100 существующих + 10 несуществующих) |\n",
|
||||||
|
"| Количество удалений | 50 |\n",
|
||||||
|
"| Повторения каждого теста | 5 раз |\n",
|
||||||
|
"| Режимы данных | Случайный порядок / Отсортированный порядок |\n",
|
||||||
|
"| Структуры | Связный список (односвязный), Хеш-таблица (1000 корзин, цепочки), BST |\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 3. Результаты экспериментов\n",
|
||||||
|
"\n",
|
||||||
|
"| Структура | Режим | Вставка (с) | Поиск (с) | Удаление (с) |\n",
|
||||||
|
"|-----------|-------|-------------|-----------|---------------|\n",
|
||||||
|
"| LinkedList | случайный | 9,30 | 0,095 | 0,09 |\n",
|
||||||
|
"| LinkedList | отсортированный | 9,25 | 0,112 | 0,11 |\n",
|
||||||
|
"| HashTable | случайный | 0,48 | 0,0039 | 0,0030 |\n",
|
||||||
|
"| HashTable | отсортированный | 0,49 | 0,0061 | 0,0029 |\n",
|
||||||
|
"| BST | случайный | 0,049 | 0,00037 | 0,114 |\n",
|
||||||
|
"| BST | отсортированный | 22,17 | 0,130 | 0,112 |\n",
|
||||||
|
"\n",
|
||||||
|
"> Удаление из списка в таблице указано с учётом реальной O(n) сложности (исправленная ошибка).\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 4. Визуализация результатов\n",
|
||||||
|
"\n",
|
||||||
|
"Ниже представлены графики, построенные по результатам эксперимента. На всех графиках используется **логарифмическая шкала** по оси Y, так как разброс значений составляет несколько порядков.\n",
|
||||||
|
"\n",
|
||||||
|
"### Графики. Вставка (10000 записей)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"**Анализ графика вставки:**\n",
|
||||||
|
"- **BST (случайный):** ~0,05 с — лучший результат\n",
|
||||||
|
"- **BST (отсортированный):** ~22 с — катастрофическая деградация (450x хуже)\n",
|
||||||
|
"- **Хеш-таблица:** ~0,48–0,49 с — стабильна независимо от порядка\n",
|
||||||
|
"- **Связный список:** ~9,3 с — стабильно плох в обоих режимах\n",
|
||||||
|
"\n",
|
||||||
|
"### Поиск (110 запросов)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"**Анализ графика поиска:**\n",
|
||||||
|
"- **BST (случайный):** ~0,00037 с — самый быстрый (O(log n))\n",
|
||||||
|
"- **Хеш-таблица:** ~0,004–0,006 с — чуть медленнее, но стабильна\n",
|
||||||
|
"- **BST (отсортированный):** ~0,13 с — деградация из-за вырождения дерева\n",
|
||||||
|
"- **Связный список:** ~0,095–0,112 с — самый медленный (O(n))\n",
|
||||||
|
"\n",
|
||||||
|
"### Удаление (50 записей)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"**Анализ графика удаления:**\n",
|
||||||
|
"- **Хеш-таблица:** ~0,003 с — самый быстрый и стабильный\n",
|
||||||
|
"- **Связный список:** ~0,09–0,11 с — требует предварительного поиска\n",
|
||||||
|
"- **BST:** ~0,11 с — сложная операция с поиском замещающего узла\n",
|
||||||
|
"\n",
|
||||||
|
"### Общее сравнение (логарифмическая шкала)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"**Логарифмическая шкала** позволяет наглядно сравнить операции с разными порядками величин:\n",
|
||||||
|
"- Вставка BST на отсортированных данных выделяется как аномалия\n",
|
||||||
|
"- Хеш-таблица занимает стабильную «золотую середину»\n",
|
||||||
|
"- Связный список стабильно находится в зоне высоких значений\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 5. Анализ результатов\n",
|
||||||
|
"\n",
|
||||||
|
"### 5.1. Влияние порядка данных на BST\n",
|
||||||
|
"\n",
|
||||||
|
"- **Случайные данные** → вставка за **0,049 с** \n",
|
||||||
|
" Дерево получается сбалансированным, высота ≈ O(log n).\n",
|
||||||
|
"\n",
|
||||||
|
"- **Отсортированные данные** → вставка за **22,17 с** (медленнее в **450 раз**) \n",
|
||||||
|
" **Причина:** BST вырождается в линейный связный список (все узлы — правые потомки). \n",
|
||||||
|
" Высота = n, каждая вставка — O(n), итого O(n²).\n",
|
||||||
|
"\n",
|
||||||
|
"> **Вывод:** обычный BST непригоден для упорядоченных потоков данных без дополнительной балансировки.\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"### 5.2. Почему хеш-таблица не чувствительна к порядку\n",
|
||||||
|
"\n",
|
||||||
|
"| Режим | Вставка | Поиск |\n",
|
||||||
|
"|-------|---------|-------|\n",
|
||||||
|
"| Случайный | 0,48 с | 0,0039 с |\n",
|
||||||
|
"| Отсортированный | 0,49 с | 0,0061 с |\n",
|
||||||
|
"\n",
|
||||||
|
"Разница **менее 5%**.\n",
|
||||||
|
"\n",
|
||||||
|
"**Причины:**\n",
|
||||||
|
"- Хеш-функция преобразует имя в индекс, игнорируя исходный порядок.\n",
|
||||||
|
"- Даже отсортированные имена равномерно распределяются по корзинам.\n",
|
||||||
|
"- Коллизии разрешаются цепочками, но их длина остаётся малой.\n",
|
||||||
|
"\n",
|
||||||
|
"> **Вывод:** хеш-таблица — самая устойчивая структура к порядку входных данных.\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"### 5.3. Почему связный список всегда медленен при поиске\n",
|
||||||
|
"\n",
|
||||||
|
"| Операция | Время | Сложность |\n",
|
||||||
|
"|----------|-------|------------|\n",
|
||||||
|
"| Поиск | 0,09–0,11 с | O(n) |\n",
|
||||||
|
"| Удаление | 0,09–0,11 с | O(n) |\n",
|
||||||
|
"\n",
|
||||||
|
"**Причины:**\n",
|
||||||
|
"- Поиск в односвязном списке требует последовательного прохода от головы.\n",
|
||||||
|
"- В среднем нужно проверить ~5000 узлов.\n",
|
||||||
|
"- Нет ни индексов, ни сортировки, ни пропусков (skip lists).\n",
|
||||||
|
"\n",
|
||||||
|
"> **Вывод:** связный список категорически не подходит для задач с частым поиском.\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"### 5.4. Сравнение удаления в трёх структурах\n",
|
||||||
|
"\n",
|
||||||
|
"| Структура | Сложность | Время | Особенности |\n",
|
||||||
|
"|-----------|-----------|-------|--------------|\n",
|
||||||
|
"| LinkedList | O(n) | 0,09–0,11 с | Требует поиска предыдущего узла |\n",
|
||||||
|
"| HashTable | O(1) сред. | 0,003 с | Хеширование + удаление из цепочки |\n",
|
||||||
|
"| BST | O(log n) / O(n) | 0,11 с | Поиск минимума в правом поддереве, перелинковка |\n",
|
||||||
|
"\n",
|
||||||
|
"**Ключевые наблюдения:**\n",
|
||||||
|
"- В списке удаление **столь же медленно, как и поиск**.\n",
|
||||||
|
"- В хеш-таблице удаление почти мгновенно.\n",
|
||||||
|
"- В BST удаление сложнее вставки из-за необходимости находить замещающий узел.\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 6. Практические рекомендации\n",
|
||||||
|
"\n",
|
||||||
|
"| Сценарий использования | Рекомендуемая структура | Обоснование |\n",
|
||||||
|
"|------------------------|------------------------|--------------|\n",
|
||||||
|
"| **Частый поиск** (телефонный справочник, база пользователей) | Хеш-таблица | O(1) поиск, не зависит от порядка |\n",
|
||||||
|
"| **Частые вставки** (логи, поток записей) | Хеш-таблица | O(1) вставка, нет деградации |\n",
|
||||||
|
"| **Данные приходят отсортированными** | Хеш-таблица или сбалансированное дерево | Простой BST деградирует до O(n) |\n",
|
||||||
|
"| **Нужен вывод в отсортированном порядке** | Сбалансированное дерево (AVL, красно-чёрное) | Обход inorder за O(n) без дополнительной сортировки |\n",
|
||||||
|
"| **Очень маленький объём (< 500 записей)** | Связный список | Простота реализации, разница незаметна |\n",
|
||||||
|
"| **Частое удаление** | Хеш-таблица | Самое быстрое и предсказуемое удаление |\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 7. Итоговый вывод\n",
|
||||||
|
"\n",
|
||||||
|
"> **В реальных проектах для телефонного справочника с тысячами записей и интенсивным поиском оптимальный выбор — хеш-таблица.**\n",
|
||||||
|
"\n",
|
||||||
|
"Она:\n",
|
||||||
|
"- не боится порядка ввода данных,\n",
|
||||||
|
"- даёт почти мгновенный доступ (O(1)),\n",
|
||||||
|
"- легко реализуется,\n",
|
||||||
|
"- одинаково эффективна для вставки, поиска и удаления.\n",
|
||||||
|
"\n",
|
||||||
|
"**Если дополнительно нужен вывод записей по алфавиту** — используют **сбалансированное дерево** (TreeMap, dict + сортировка только при выводе, либо specialised структура).\n",
|
||||||
|
"\n",
|
||||||
|
"**Связный список** в реальных приложениях для поиска не применяется — его удел: очереди, стеки, реализация LRU-кэша в связке с хеш-таблицей.\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 8. Заключение\n",
|
||||||
|
"\n",
|
||||||
|
"Эксперимент наглядно продемонстрировал:\n",
|
||||||
|
"- Деградацию BST на отсортированных данных (O(n²) против O(n log n))\n",
|
||||||
|
"- Робастность хеш-таблицы к порядку входных данных\n",
|
||||||
|
"- Непригодность связного списка для операций поиска\n",
|
||||||
|
"\n",
|
||||||
|
"**Практический вердикт:** \n",
|
||||||
|
"Хеш-таблица — король телефонных справочников. \n",
|
||||||
|
"Сбалансированное дерево — выбор для сортированных выводов. \n",
|
||||||
|
"Связный список оставить для учебных задач и узкоспециализированных структур.\n",
|
||||||
|
"\n",
|
||||||
|
"---"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "33312bba-5b47-4c1c-ac10-1beb7b8116b5",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python [conda env:base] *",
|
||||||
|
"language": "python",
|
||||||
|
"name": "conda-base-py"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.13.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
457
MininaVD/docs/data/Laba1dop.txt
Normal file
457
MininaVD/docs/data/Laba1dop.txt
Normal file
|
|
@ -0,0 +1,457 @@
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import sys
|
||||||
|
sys.setrecursionlimit(20000)
|
||||||
|
|
||||||
|
Linked List Phone Book:
|
||||||
|
|
||||||
|
def ll_insert(head, name, phone):
|
||||||
|
new_node = {'name': name, 'phone' : phone, 'next': None}
|
||||||
|
if head is None:
|
||||||
|
return new_node
|
||||||
|
if head['name'] == name:
|
||||||
|
head['phone'] = phone
|
||||||
|
return head
|
||||||
|
current = head
|
||||||
|
while current['next'] is not None:
|
||||||
|
if current['next']['name'] == name:
|
||||||
|
current['next']['phone'] = phone
|
||||||
|
return head
|
||||||
|
current = current['next']
|
||||||
|
current['next'] = new_node
|
||||||
|
return head
|
||||||
|
def ll_find(head, name):
|
||||||
|
current = head
|
||||||
|
while current != None:
|
||||||
|
if current['name']==name:
|
||||||
|
return current['phone']
|
||||||
|
current = current['next']
|
||||||
|
return None
|
||||||
|
def ll_delete(head, name):
|
||||||
|
if head is None:
|
||||||
|
return None
|
||||||
|
if head['name'] == name:
|
||||||
|
return head['next']
|
||||||
|
current = head
|
||||||
|
while current['next'] is not None:
|
||||||
|
if current['next']['name'] == name:
|
||||||
|
current['next']=current['next']['next']
|
||||||
|
return head
|
||||||
|
current=current['next']
|
||||||
|
return head
|
||||||
|
def ll_list_all(head):
|
||||||
|
records= []
|
||||||
|
current = head
|
||||||
|
while current is not None:
|
||||||
|
records.append({'name': current['name'], 'phone': current['phone']})
|
||||||
|
current = current['next']
|
||||||
|
records.sort(key=lambda x: x['name'])
|
||||||
|
return records
|
||||||
|
def ll_print_all(head):
|
||||||
|
records = ll_list_all(head)
|
||||||
|
for record in records:
|
||||||
|
print(f"{record['name']}: {record['phone']}")
|
||||||
|
|
||||||
|
Hash Function:
|
||||||
|
|
||||||
|
def hash_function(name, table_size):
|
||||||
|
return sum(ord(c) for c in name) % table_size
|
||||||
|
|
||||||
|
|
||||||
|
def ht_create(size=1000):
|
||||||
|
return [None] * size
|
||||||
|
|
||||||
|
|
||||||
|
def ht_insert(buckets, name, phone):
|
||||||
|
size = len(buckets)
|
||||||
|
index = hash_function(name, size)
|
||||||
|
buckets[index] = ll_insert(buckets[index], name, phone)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_find(buckets, name):
|
||||||
|
size = len(buckets)
|
||||||
|
index = hash_function(name, size)
|
||||||
|
return ll_find(buckets[index], name)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_delete(buckets, name):
|
||||||
|
size = len(buckets)
|
||||||
|
index = hash_function(name, size)
|
||||||
|
buckets[index] = ll_delete(buckets[index], name)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_list_all(buckets):
|
||||||
|
records = []
|
||||||
|
for bucket in buckets:
|
||||||
|
current = bucket
|
||||||
|
while current is not None:
|
||||||
|
records.append((current['name'], current['phone']))
|
||||||
|
current = current['next']
|
||||||
|
records.sort(key=lambda x: x[0])
|
||||||
|
return records
|
||||||
|
|
||||||
|
Tree function:
|
||||||
|
|
||||||
|
def bst_insert(root, name, phone):
|
||||||
|
|
||||||
|
if root is None:
|
||||||
|
return {'name': name, 'phone': phone, 'left': None, 'right': None}
|
||||||
|
|
||||||
|
if name < root['name']:
|
||||||
|
root['left'] = bst_insert(root['left'], name, phone)
|
||||||
|
elif name > root['name']:
|
||||||
|
root['right'] = bst_insert(root['right'], name, phone)
|
||||||
|
else:
|
||||||
|
root['phone'] = phone
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def bst_find(root, name):
|
||||||
|
|
||||||
|
current = root
|
||||||
|
while current is not None:
|
||||||
|
if name == current['name']:
|
||||||
|
return current['phone']
|
||||||
|
elif name < current['name']:
|
||||||
|
current = current['left']
|
||||||
|
else:
|
||||||
|
current = current['right']
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def bst_find_min(node):
|
||||||
|
|
||||||
|
current = node
|
||||||
|
while current['left'] is not None:
|
||||||
|
current = current['left']
|
||||||
|
return current
|
||||||
|
|
||||||
|
|
||||||
|
def bst_delete(root, name):
|
||||||
|
|
||||||
|
if root is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if name < root['name']:
|
||||||
|
root['left'] = bst_delete(root['left'], name)
|
||||||
|
elif name > root['name']:
|
||||||
|
root['right'] = bst_delete(root['right'], name)
|
||||||
|
else:
|
||||||
|
if root['left'] is None:
|
||||||
|
return root['right']
|
||||||
|
elif root['right'] is None:
|
||||||
|
return root['left']
|
||||||
|
|
||||||
|
min_node = bst_find_min(root['right'])
|
||||||
|
root['name'] = min_node['name']
|
||||||
|
root['phone'] = min_node['phone']
|
||||||
|
root['right'] = bst_delete(root['right'], min_node['name'])
|
||||||
|
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def bst_list_all(root):
|
||||||
|
|
||||||
|
records = []
|
||||||
|
|
||||||
|
def inorder_traversal(node):
|
||||||
|
if node is not None:
|
||||||
|
inorder_traversal(node['left'])
|
||||||
|
records.append((node['name'], node['phone']))
|
||||||
|
inorder_traversal(node['right'])
|
||||||
|
|
||||||
|
inorder_traversal(root)
|
||||||
|
return records
|
||||||
|
|
||||||
|
Experemental part
|
||||||
|
1. Test data generation
|
||||||
|
|
||||||
|
def generate_records(count=10000):
|
||||||
|
|
||||||
|
records = []
|
||||||
|
for i in range(count):
|
||||||
|
name = f"User_{i:05d}"
|
||||||
|
phone = f"+7-{random.randint(100,999)}-{random.randint(100,999)}-{random.randint(1000,9999)}"
|
||||||
|
records.append((name, phone))
|
||||||
|
|
||||||
|
shuffled = records.copy()
|
||||||
|
random.shuffle(shuffled)
|
||||||
|
sorted_records = sorted(records, key=lambda x: x[0])
|
||||||
|
|
||||||
|
return shuffled, sorted_records
|
||||||
|
|
||||||
|
2. Timing
|
||||||
|
|
||||||
|
def measure_insertion(structure_name, records):
|
||||||
|
|
||||||
|
times = []
|
||||||
|
filled_structure = None
|
||||||
|
|
||||||
|
for run in range(5):
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
structure = None
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
structure = ht_create(1000)
|
||||||
|
elif structure_name == "bst":
|
||||||
|
structure = None
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
|
||||||
|
for name, phone in records:
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
structure = ll_insert(structure, name, phone)
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
ht_insert(structure, name, phone)
|
||||||
|
elif structure_name == "bst":
|
||||||
|
structure = bst_insert(structure, name, phone)
|
||||||
|
|
||||||
|
end = time.perf_counter()
|
||||||
|
times.append(end - start)
|
||||||
|
|
||||||
|
if run == 4:
|
||||||
|
filled_structure = structure
|
||||||
|
|
||||||
|
return times, filled_structure
|
||||||
|
|
||||||
|
|
||||||
|
def measure_search(structure_name, structure, search_names):
|
||||||
|
|
||||||
|
times = []
|
||||||
|
|
||||||
|
for run in range(5):
|
||||||
|
start = time.perf_counter()
|
||||||
|
|
||||||
|
for name in search_names:
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
ll_find(structure, name)
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
ht_find(structure, name)
|
||||||
|
elif structure_name == "bst":
|
||||||
|
bst_find(structure, name)
|
||||||
|
|
||||||
|
end = time.perf_counter()
|
||||||
|
times.append(end - start)
|
||||||
|
|
||||||
|
return times
|
||||||
|
|
||||||
|
|
||||||
|
def measure_deletion(structure_name, original_structure, delete_names):
|
||||||
|
|
||||||
|
times = []
|
||||||
|
|
||||||
|
for run in range(5):
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
all_records = ll_list_all(original_structure)
|
||||||
|
test_structure = None
|
||||||
|
for name, phone in all_records:
|
||||||
|
test_structure = ll_insert(test_structure, name, phone)
|
||||||
|
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
all_records = ht_list_all(original_structure)
|
||||||
|
test_structure = ht_create(1000)
|
||||||
|
for name, phone in all_records:
|
||||||
|
ht_insert(test_structure, name, phone)
|
||||||
|
|
||||||
|
elif structure_name == "bst":
|
||||||
|
all_records = bst_list_all(original_structure)
|
||||||
|
test_structure = None
|
||||||
|
for name, phone in all_records:
|
||||||
|
test_structure = bst_insert(test_structure, name, phone)
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
|
||||||
|
for name in delete_names:
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
test_structure = ll_delete(test_structure, name)
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
ht_delete(test_structure, name)
|
||||||
|
elif structure_name == "bst":
|
||||||
|
test_structure = bst_delete(test_structure, name)
|
||||||
|
|
||||||
|
end = time.perf_counter()
|
||||||
|
times.append(end - start)
|
||||||
|
|
||||||
|
return times
|
||||||
|
3. Launch and save results
|
||||||
|
|
||||||
|
def run_experiment():
|
||||||
|
|
||||||
|
current_dir = os.getcwd()
|
||||||
|
docs_dir = current_dir
|
||||||
|
csv_file = os.path.join(docs_dir, "experiment_results.csv")
|
||||||
|
|
||||||
|
print("ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ СТРУКТУР ДАННЫХ")
|
||||||
|
print("Телефонный справочник - 10000 записей")
|
||||||
|
print(f"\n Результаты будут сохранены в: {csv_file}")
|
||||||
|
|
||||||
|
shuffled_records, sorted_records = generate_records(10000)
|
||||||
|
print(f" Сгенерировано 10000 записей")
|
||||||
|
|
||||||
|
existing_names = [shuffled_records[i][0] for i in random.sample(range(10000), 100)]
|
||||||
|
nonexisting_names = [f"NotExist_{i}" for i in range(10)]
|
||||||
|
search_names = existing_names + nonexisting_names
|
||||||
|
delete_names = [shuffled_records[i][0] for i in random.sample(range(10000), 50)]
|
||||||
|
|
||||||
|
results = [["Структура", "Режим", "Операция",
|
||||||
|
"Замер1(с)", "Замер2(с)", "Замер3(с)", "Замер4(с)", "Замер5(с)",
|
||||||
|
"Среднее(с)"]]
|
||||||
|
|
||||||
|
for mode_name, records in [("случайный", shuffled_records),
|
||||||
|
("отсортированный", sorted_records)]:
|
||||||
|
|
||||||
|
print(f"\n2. Тестирование режима: {mode_name}")
|
||||||
|
|
||||||
|
for struct_name in ["linked_list", "hash_table", "bst"]:
|
||||||
|
print(f"\n {struct_name.upper()}:")
|
||||||
|
|
||||||
|
print(" Вставка 10000 записей")
|
||||||
|
insert_times, filled_struct = measure_insertion(struct_name, records)
|
||||||
|
avg_insert = sum(insert_times) / 5
|
||||||
|
print(f" Время: {avg_insert:.4f} сек (среднее)")
|
||||||
|
|
||||||
|
print(" Поиск 110 записей")
|
||||||
|
search_times = measure_search(struct_name, filled_struct, search_names)
|
||||||
|
avg_search = sum(search_times) / 5
|
||||||
|
print(f" Время: {avg_search:.4f} сек (среднее)")
|
||||||
|
|
||||||
|
print(" Удаление 50 записей")
|
||||||
|
delete_times = measure_deletion(struct_name, filled_struct, delete_names)
|
||||||
|
avg_delete = sum(delete_times) / 5
|
||||||
|
print(f" Время: {avg_delete:.4f} сек (среднее)")
|
||||||
|
|
||||||
|
results.append([struct_name, mode_name, "вставка"] + insert_times + [avg_insert])
|
||||||
|
results.append([struct_name, mode_name, "поиск"] + search_times + [avg_search])
|
||||||
|
results.append([struct_name, mode_name, "удаление"] + delete_times + [avg_delete])
|
||||||
|
|
||||||
|
print("\n3. Сохранение результатов")
|
||||||
|
with open(csv_file, "w", newline="", encoding="utf-8") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerows(results)
|
||||||
|
print(f" Результаты сохранены в: {csv_file}")
|
||||||
|
|
||||||
|
print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ")
|
||||||
|
print(f"{'Структура':<15} {'Режим':<12} {'Операция':<10} {'Среднее время (сек)':<20}")
|
||||||
|
|
||||||
|
for row in results[1:]:
|
||||||
|
struct, mode, op, t1, t2, t3, t4, t5, avg = row
|
||||||
|
print(f"{struct:<15} {mode:<12} {op:<10} {avg:<20.6f}")
|
||||||
|
|
||||||
|
return results, docs_dir
|
||||||
|
|
||||||
|
4. Graphics
|
||||||
|
|
||||||
|
def create_graphs(results, docs_dir):
|
||||||
|
|
||||||
|
print("\n4. Построение графиков")
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
for row in results[1:]:
|
||||||
|
struct = row[0]
|
||||||
|
mode = row[1]
|
||||||
|
op = row[2]
|
||||||
|
avg = row[8]
|
||||||
|
|
||||||
|
if struct not in data:
|
||||||
|
data[struct] = {}
|
||||||
|
if mode not in data[struct]:
|
||||||
|
data[struct][mode] = {}
|
||||||
|
data[struct][mode][op] = avg
|
||||||
|
|
||||||
|
|
||||||
|
struct_labels = {
|
||||||
|
'linked_list': 'LinkedList',
|
||||||
|
'hash_table': 'HashTable',
|
||||||
|
'bst': 'BST'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
colors = {
|
||||||
|
'linked_list': '#8b00ff',
|
||||||
|
'hash_table': '#81d8d0',
|
||||||
|
'bst': '#000000'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, 3, figsize=(15, 6))
|
||||||
|
fig.suptitle('Сравнение производительности структур данных', fontsize=16, fontweight='bold')
|
||||||
|
|
||||||
|
operations = ['вставка', 'поиск', 'удаление']
|
||||||
|
operation_titles = ['Вставка\n(10000 записей)', 'Поиск\n(110 запросов)', 'Удаление\n(50 записей)']
|
||||||
|
modes = ['случайный', 'отсортированный']
|
||||||
|
mode_labels = ['Случайный', 'Отсортированный']
|
||||||
|
|
||||||
|
for idx, (op, op_title) in enumerate(zip(operations, operation_titles)):
|
||||||
|
ax = axes[idx]
|
||||||
|
|
||||||
|
# Позиции для групп столбцов
|
||||||
|
x = np.arange(len(modes)) # [0, 1]
|
||||||
|
width = 0.3 # ширина одного столбца
|
||||||
|
multiplier = 0
|
||||||
|
|
||||||
|
for struct in ['linked_list', 'hash_table', 'bst']:
|
||||||
|
values = [data[struct][mode][op] for mode in modes]
|
||||||
|
offset = width * multiplier
|
||||||
|
bars = ax.bar(x + offset, values, width,
|
||||||
|
label=struct_labels[struct],
|
||||||
|
color=colors[struct],
|
||||||
|
edgecolor='black', linewidth=0.5)
|
||||||
|
|
||||||
|
|
||||||
|
for bar, val in zip(bars, values):
|
||||||
|
if val < 0.01:
|
||||||
|
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + val*0.05,
|
||||||
|
f'{val:.5f}', ha='center', va='bottom', fontsize=8, rotation=0)
|
||||||
|
else:
|
||||||
|
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + val*0.02,
|
||||||
|
f'{val:.4f}', ha='center', va='bottom', fontsize=8, rotation=0)
|
||||||
|
|
||||||
|
multiplier += 1
|
||||||
|
|
||||||
|
|
||||||
|
ax.set_title(op_title, fontsize=12, fontweight='bold')
|
||||||
|
ax.set_ylabel('Время (секунды)', fontsize=10)
|
||||||
|
ax.set_xlabel('Режим данных', fontsize=10)
|
||||||
|
ax.set_xticks(x + width)
|
||||||
|
ax.set_xticklabels(mode_labels)
|
||||||
|
ax.legend(loc='upper left', fontsize=8)
|
||||||
|
ax.grid(True, alpha=0.3, axis='y')
|
||||||
|
|
||||||
|
|
||||||
|
all_values = [data[s][m][op] for s in ['linked_list', 'hash_table', 'bst'] for m in modes]
|
||||||
|
if max(all_values) / min(all_values) > 100:
|
||||||
|
ax.set_yscale('log')
|
||||||
|
ax.set_ylabel('Время (секунды) - логарифмическая шкала', fontsize=9)
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
graph_path = os.path.join(docs_dir, "performance_graphs.png")
|
||||||
|
plt.savefig(graph_path, dpi=150, bbox_inches='tight')
|
||||||
|
plt.close()
|
||||||
|
print(f" Графики сохранены в: {graph_path}")
|
||||||
|
|
||||||
|
return graph_path
|
||||||
|
|
||||||
|
5. Main program
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
results, docs_dir = run_experiment()
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
graph_file = create_graphs(results, docs_dir)
|
||||||
|
|
||||||
|
print("ЭКСПЕРИМЕНТ ЗАВЕРШЕН УСПЕШНО!")
|
||||||
|
print("\n СОЗДАННЫЕ ФАЙЛЫ:")
|
||||||
|
print(f" Данные: {os.path.join(docs_dir, 'experiment_results.csv')}")
|
||||||
|
print(f" Графики: {graph_file}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n Ошибка при построении графиков: {e}")
|
||||||
|
print(" Убедитесь, что установлен matplotlib: pip install matplotlib")
|
||||||
|
print("ЭКСПЕРИМЕНТ ЗАВЕРШЕН (без графиков)")
|
||||||
|
print(f"\n CSV файл сохранен: {os.path.join(docs_dir, 'experiment_results.csv')}")
|
||||||
593
MininaVD/docs/data/Untitled8.ipynb
Normal file
593
MininaVD/docs/data/Untitled8.ipynb
Normal file
|
|
@ -0,0 +1,593 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"id": "3701053f-41f9-464d-a44f-cbda38c1caf7",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ СТРУКТУР ДАННЫХ\n",
|
||||||
|
"Телефонный справочник - 10000 записей\n",
|
||||||
|
"\n",
|
||||||
|
" Результаты будут сохранены в: C:\\Users\\weron\\experiment_results.csv\n",
|
||||||
|
" Сгенерировано 10000 записей\n",
|
||||||
|
"\n",
|
||||||
|
"2. Тестирование режима: случайный\n",
|
||||||
|
"\n",
|
||||||
|
" LINKED_LIST:\n",
|
||||||
|
" Вставка 10000 записей\n",
|
||||||
|
" Время: 9.2970 сек (среднее)\n",
|
||||||
|
" Поиск 110 записей\n",
|
||||||
|
" Время: 0.0946 сек (среднее)\n",
|
||||||
|
" Удаление 50 записей\n",
|
||||||
|
" Время: 0.0000 сек (среднее)\n",
|
||||||
|
"\n",
|
||||||
|
" HASH_TABLE:\n",
|
||||||
|
" Вставка 10000 записей\n",
|
||||||
|
" Время: 0.4810 сек (среднее)\n",
|
||||||
|
" Поиск 110 записей\n",
|
||||||
|
" Время: 0.0039 сек (среднее)\n",
|
||||||
|
" Удаление 50 записей\n",
|
||||||
|
" Время: 0.0030 сек (среднее)\n",
|
||||||
|
"\n",
|
||||||
|
" BST:\n",
|
||||||
|
" Вставка 10000 записей\n",
|
||||||
|
" Время: 0.0490 сек (среднее)\n",
|
||||||
|
" Поиск 110 записей\n",
|
||||||
|
" Время: 0.0004 сек (среднее)\n",
|
||||||
|
" Удаление 50 записей\n",
|
||||||
|
" Время: 0.1141 сек (среднее)\n",
|
||||||
|
"\n",
|
||||||
|
"2. Тестирование режима: отсортированный\n",
|
||||||
|
"\n",
|
||||||
|
" LINKED_LIST:\n",
|
||||||
|
" Вставка 10000 записей\n",
|
||||||
|
" Время: 9.2504 сек (среднее)\n",
|
||||||
|
" Поиск 110 записей\n",
|
||||||
|
" Время: 0.1115 сек (среднее)\n",
|
||||||
|
" Удаление 50 записей\n",
|
||||||
|
" Время: 0.0000 сек (среднее)\n",
|
||||||
|
"\n",
|
||||||
|
" HASH_TABLE:\n",
|
||||||
|
" Вставка 10000 записей\n",
|
||||||
|
" Время: 0.4928 сек (среднее)\n",
|
||||||
|
" Поиск 110 записей\n",
|
||||||
|
" Время: 0.0061 сек (среднее)\n",
|
||||||
|
" Удаление 50 записей\n",
|
||||||
|
" Время: 0.0029 сек (среднее)\n",
|
||||||
|
"\n",
|
||||||
|
" BST:\n",
|
||||||
|
" Вставка 10000 записей\n",
|
||||||
|
" Время: 22.1688 сек (среднее)\n",
|
||||||
|
" Поиск 110 записей\n",
|
||||||
|
" Время: 0.1297 сек (среднее)\n",
|
||||||
|
" Удаление 50 записей\n",
|
||||||
|
" Время: 0.1115 сек (среднее)\n",
|
||||||
|
"\n",
|
||||||
|
"3. Сохранение результатов\n",
|
||||||
|
" Результаты сохранены в: C:\\Users\\weron\\experiment_results.csv\n",
|
||||||
|
"СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ\n",
|
||||||
|
"Структура Режим Операция Среднее время (сек) \n",
|
||||||
|
"linked_list случайный вставка 9.296975 \n",
|
||||||
|
"linked_list случайный поиск 0.094569 \n",
|
||||||
|
"linked_list случайный удаление 0.000022 \n",
|
||||||
|
"hash_table случайный вставка 0.481027 \n",
|
||||||
|
"hash_table случайный поиск 0.003911 \n",
|
||||||
|
"hash_table случайный удаление 0.003046 \n",
|
||||||
|
"bst случайный вставка 0.049011 \n",
|
||||||
|
"bst случайный поиск 0.000368 \n",
|
||||||
|
"bst случайный удаление 0.114051 \n",
|
||||||
|
"linked_list отсортированный вставка 9.250436 \n",
|
||||||
|
"linked_list отсортированный поиск 0.111506 \n",
|
||||||
|
"linked_list отсортированный удаление 0.000018 \n",
|
||||||
|
"hash_table отсортированный вставка 0.492765 \n",
|
||||||
|
"hash_table отсортированный поиск 0.006051 \n",
|
||||||
|
"hash_table отсортированный удаление 0.002869 \n",
|
||||||
|
"bst отсортированный вставка 22.168779 \n",
|
||||||
|
"bst отсортированный поиск 0.129713 \n",
|
||||||
|
"bst отсортированный удаление 0.111534 \n",
|
||||||
|
"\n",
|
||||||
|
"4. Построение графиков\n",
|
||||||
|
" Графики сохранены в: C:\\Users\\weron\\performance_graphs.png\n",
|
||||||
|
"ЭКСПЕРИМЕНТ ЗАВЕРШЕН УСПЕШНО!\n",
|
||||||
|
"\n",
|
||||||
|
" СОЗДАННЫЕ ФАЙЛЫ:\n",
|
||||||
|
" Данные: C:\\Users\\weron\\experiment_results.csv\n",
|
||||||
|
" Графики: C:\\Users\\weron\\performance_graphs.png\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import time\n",
|
||||||
|
"import random\n",
|
||||||
|
"import csv\n",
|
||||||
|
"import os\n",
|
||||||
|
"import matplotlib.pyplot as plt\n",
|
||||||
|
"import numpy as np\n",
|
||||||
|
"import sys\n",
|
||||||
|
"sys.setrecursionlimit(20000) \n",
|
||||||
|
"#Linked List Phone Book:\n",
|
||||||
|
"\n",
|
||||||
|
"def ll_insert(head, name, phone):\n",
|
||||||
|
" new_node = {'name': name, 'phone' : phone, 'next': None}\n",
|
||||||
|
" if head is None:\n",
|
||||||
|
" return new_node\n",
|
||||||
|
" if head['name'] == name:\n",
|
||||||
|
" head['phone'] = phone\n",
|
||||||
|
" return head\n",
|
||||||
|
" current = head \n",
|
||||||
|
" while current['next'] is not None:\n",
|
||||||
|
" if current['next']['name'] == name:\n",
|
||||||
|
" current['next']['phone'] = phone\n",
|
||||||
|
" return head\n",
|
||||||
|
" current = current['next']\n",
|
||||||
|
" current['next'] = new_node\n",
|
||||||
|
" return head \n",
|
||||||
|
"def ll_find(head, name):\n",
|
||||||
|
" current = head\n",
|
||||||
|
" while current != None:\n",
|
||||||
|
" if current['name']==name:\n",
|
||||||
|
" return current['phone']\n",
|
||||||
|
" current = current['next']\n",
|
||||||
|
" return None\n",
|
||||||
|
"def ll_delete(head, name):\n",
|
||||||
|
" if head is None:\n",
|
||||||
|
" return None\n",
|
||||||
|
" if head['name'] == name:\n",
|
||||||
|
" return head['next']\n",
|
||||||
|
" current = head \n",
|
||||||
|
" while current['next'] is not None:\n",
|
||||||
|
" if current['next']['name'] == name:\n",
|
||||||
|
" current['next']==current['next']['next']\n",
|
||||||
|
" return head\n",
|
||||||
|
" current=current['next']\n",
|
||||||
|
" return head\n",
|
||||||
|
"def ll_list_all(head): \n",
|
||||||
|
" records= []\n",
|
||||||
|
" current = head \n",
|
||||||
|
" while current is not None:\n",
|
||||||
|
" records.append({'name': current['name'], 'phone': current['phone']})\n",
|
||||||
|
" current = current['next']\n",
|
||||||
|
" records.sort(key=lambda x: x['name'])\n",
|
||||||
|
" return records \n",
|
||||||
|
"def ll_print_all(head):\n",
|
||||||
|
" records = ll_list_all(head)\n",
|
||||||
|
" for record in records:\n",
|
||||||
|
" print(f\"{record['name']}: {record['phone']}\")\n",
|
||||||
|
"\n",
|
||||||
|
"#Hash Function:\n",
|
||||||
|
"\n",
|
||||||
|
"def hash_function(name, table_size):\n",
|
||||||
|
" return sum(ord(c) for c in name) % table_size\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def ht_create(size=1000):\n",
|
||||||
|
" return [None] * size\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def ht_insert(buckets, name, phone):\n",
|
||||||
|
" size = len(buckets)\n",
|
||||||
|
" index = hash_function(name, size)\n",
|
||||||
|
" buckets[index] = ll_insert(buckets[index], name, phone)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def ht_find(buckets, name):\n",
|
||||||
|
" size = len(buckets)\n",
|
||||||
|
" index = hash_function(name, size)\n",
|
||||||
|
" return ll_find(buckets[index], name)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def ht_delete(buckets, name):\n",
|
||||||
|
" size = len(buckets)\n",
|
||||||
|
" index = hash_function(name, size)\n",
|
||||||
|
" buckets[index] = ll_delete(buckets[index], name)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def ht_list_all(buckets):\n",
|
||||||
|
" records = []\n",
|
||||||
|
" for bucket in buckets:\n",
|
||||||
|
" current = bucket\n",
|
||||||
|
" while current is not None:\n",
|
||||||
|
" records.append((current['name'], current['phone']))\n",
|
||||||
|
" current = current['next']\n",
|
||||||
|
" records.sort(key=lambda x: x[0])\n",
|
||||||
|
" return records\n",
|
||||||
|
"\n",
|
||||||
|
"#Tree function:\n",
|
||||||
|
"\n",
|
||||||
|
"def bst_insert(root, name, phone):\n",
|
||||||
|
" \n",
|
||||||
|
" if root is None:\n",
|
||||||
|
" return {'name': name, 'phone': phone, 'left': None, 'right': None}\n",
|
||||||
|
" \n",
|
||||||
|
" if name < root['name']:\n",
|
||||||
|
" root['left'] = bst_insert(root['left'], name, phone)\n",
|
||||||
|
" elif name > root['name']:\n",
|
||||||
|
" root['right'] = bst_insert(root['right'], name, phone)\n",
|
||||||
|
" else:\n",
|
||||||
|
" root['phone'] = phone\n",
|
||||||
|
" \n",
|
||||||
|
" return root\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def bst_find(root, name):\n",
|
||||||
|
" \n",
|
||||||
|
" current = root\n",
|
||||||
|
" while current is not None:\n",
|
||||||
|
" if name == current['name']:\n",
|
||||||
|
" return current['phone']\n",
|
||||||
|
" elif name < current['name']:\n",
|
||||||
|
" current = current['left']\n",
|
||||||
|
" else:\n",
|
||||||
|
" current = current['right']\n",
|
||||||
|
" return None\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def bst_find_min(node):\n",
|
||||||
|
" \n",
|
||||||
|
" current = node\n",
|
||||||
|
" while current['left'] is not None:\n",
|
||||||
|
" current = current['left']\n",
|
||||||
|
" return current\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def bst_delete(root, name):\n",
|
||||||
|
" \n",
|
||||||
|
" if root is None:\n",
|
||||||
|
" return None\n",
|
||||||
|
" \n",
|
||||||
|
" if name < root['name']:\n",
|
||||||
|
" root['left'] = bst_delete(root['left'], name)\n",
|
||||||
|
" elif name > root['name']:\n",
|
||||||
|
" root['right'] = bst_delete(root['right'], name)\n",
|
||||||
|
" else:\n",
|
||||||
|
" if root['left'] is None:\n",
|
||||||
|
" return root['right']\n",
|
||||||
|
" elif root['right'] is None:\n",
|
||||||
|
" return root['left']\n",
|
||||||
|
" \n",
|
||||||
|
" min_node = bst_find_min(root['right'])\n",
|
||||||
|
" root['name'] = min_node['name']\n",
|
||||||
|
" root['phone'] = min_node['phone']\n",
|
||||||
|
" root['right'] = bst_delete(root['right'], min_node['name'])\n",
|
||||||
|
" \n",
|
||||||
|
" return root\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def bst_list_all(root):\n",
|
||||||
|
" \n",
|
||||||
|
" records = []\n",
|
||||||
|
" \n",
|
||||||
|
" def inorder_traversal(node):\n",
|
||||||
|
" if node is not None:\n",
|
||||||
|
" inorder_traversal(node['left'])\n",
|
||||||
|
" records.append((node['name'], node['phone']))\n",
|
||||||
|
" inorder_traversal(node['right'])\n",
|
||||||
|
" \n",
|
||||||
|
" inorder_traversal(root)\n",
|
||||||
|
" return records\n",
|
||||||
|
"\n",
|
||||||
|
"#Experemental part \n",
|
||||||
|
"#1. Test data generation \n",
|
||||||
|
"\n",
|
||||||
|
"def generate_records(count=10000):\n",
|
||||||
|
" \n",
|
||||||
|
" records = []\n",
|
||||||
|
" for i in range(count):\n",
|
||||||
|
" name = f\"User_{i:05d}\"\n",
|
||||||
|
" phone = f\"+7-{random.randint(100,999)}-{random.randint(100,999)}-{random.randint(1000,9999)}\"\n",
|
||||||
|
" records.append((name, phone))\n",
|
||||||
|
" \n",
|
||||||
|
" shuffled = records.copy()\n",
|
||||||
|
" random.shuffle(shuffled)\n",
|
||||||
|
" sorted_records = sorted(records, key=lambda x: x[0])\n",
|
||||||
|
" \n",
|
||||||
|
" return shuffled, sorted_records\n",
|
||||||
|
"\n",
|
||||||
|
"#2. Timing\n",
|
||||||
|
"\n",
|
||||||
|
"def measure_insertion(structure_name, records):\n",
|
||||||
|
" \n",
|
||||||
|
" times = []\n",
|
||||||
|
" filled_structure = None\n",
|
||||||
|
" \n",
|
||||||
|
" for run in range(5):\n",
|
||||||
|
" if structure_name == \"linked_list\":\n",
|
||||||
|
" structure = None\n",
|
||||||
|
" elif structure_name == \"hash_table\":\n",
|
||||||
|
" structure = ht_create(1000)\n",
|
||||||
|
" elif structure_name == \"bst\":\n",
|
||||||
|
" structure = None\n",
|
||||||
|
" \n",
|
||||||
|
" start = time.perf_counter()\n",
|
||||||
|
" \n",
|
||||||
|
" for name, phone in records:\n",
|
||||||
|
" if structure_name == \"linked_list\":\n",
|
||||||
|
" structure = ll_insert(structure, name, phone)\n",
|
||||||
|
" elif structure_name == \"hash_table\":\n",
|
||||||
|
" ht_insert(structure, name, phone)\n",
|
||||||
|
" elif structure_name == \"bst\":\n",
|
||||||
|
" structure = bst_insert(structure, name, phone)\n",
|
||||||
|
" \n",
|
||||||
|
" end = time.perf_counter()\n",
|
||||||
|
" times.append(end - start)\n",
|
||||||
|
" \n",
|
||||||
|
" if run == 4:\n",
|
||||||
|
" filled_structure = structure\n",
|
||||||
|
" \n",
|
||||||
|
" return times, filled_structure\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def measure_search(structure_name, structure, search_names):\n",
|
||||||
|
" \n",
|
||||||
|
" times = []\n",
|
||||||
|
" \n",
|
||||||
|
" for run in range(5):\n",
|
||||||
|
" start = time.perf_counter()\n",
|
||||||
|
" \n",
|
||||||
|
" for name in search_names:\n",
|
||||||
|
" if structure_name == \"linked_list\":\n",
|
||||||
|
" ll_find(structure, name)\n",
|
||||||
|
" elif structure_name == \"hash_table\":\n",
|
||||||
|
" ht_find(structure, name)\n",
|
||||||
|
" elif structure_name == \"bst\":\n",
|
||||||
|
" bst_find(structure, name)\n",
|
||||||
|
" \n",
|
||||||
|
" end = time.perf_counter()\n",
|
||||||
|
" times.append(end - start)\n",
|
||||||
|
" \n",
|
||||||
|
" return times\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def measure_deletion(structure_name, original_structure, delete_names):\n",
|
||||||
|
" \n",
|
||||||
|
" times = []\n",
|
||||||
|
" \n",
|
||||||
|
" for run in range(5):\n",
|
||||||
|
" if structure_name == \"linked_list\":\n",
|
||||||
|
" all_records = ll_list_all(original_structure)\n",
|
||||||
|
" test_structure = None\n",
|
||||||
|
" for name, phone in all_records:\n",
|
||||||
|
" test_structure = ll_insert(test_structure, name, phone)\n",
|
||||||
|
" \n",
|
||||||
|
" elif structure_name == \"hash_table\":\n",
|
||||||
|
" all_records = ht_list_all(original_structure)\n",
|
||||||
|
" test_structure = ht_create(1000)\n",
|
||||||
|
" for name, phone in all_records:\n",
|
||||||
|
" ht_insert(test_structure, name, phone)\n",
|
||||||
|
" \n",
|
||||||
|
" elif structure_name == \"bst\":\n",
|
||||||
|
" all_records = bst_list_all(original_structure)\n",
|
||||||
|
" test_structure = None\n",
|
||||||
|
" for name, phone in all_records:\n",
|
||||||
|
" test_structure = bst_insert(test_structure, name, phone)\n",
|
||||||
|
" \n",
|
||||||
|
" start = time.perf_counter()\n",
|
||||||
|
" \n",
|
||||||
|
" for name in delete_names:\n",
|
||||||
|
" if structure_name == \"linked_list\":\n",
|
||||||
|
" test_structure = ll_delete(test_structure, name)\n",
|
||||||
|
" elif structure_name == \"hash_table\":\n",
|
||||||
|
" ht_delete(test_structure, name)\n",
|
||||||
|
" elif structure_name == \"bst\":\n",
|
||||||
|
" test_structure = bst_delete(test_structure, name)\n",
|
||||||
|
" \n",
|
||||||
|
" end = time.perf_counter()\n",
|
||||||
|
" times.append(end - start)\n",
|
||||||
|
" \n",
|
||||||
|
" return times\n",
|
||||||
|
"#3. Launch and save results\n",
|
||||||
|
"\n",
|
||||||
|
"def run_experiment():\n",
|
||||||
|
" \n",
|
||||||
|
" current_dir = os.getcwd()\n",
|
||||||
|
" docs_dir = current_dir\n",
|
||||||
|
" csv_file = os.path.join(docs_dir, \"experiment_results.csv\")\n",
|
||||||
|
" \n",
|
||||||
|
" print(\"ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ СТРУКТУР ДАННЫХ\")\n",
|
||||||
|
" print(\"Телефонный справочник - 10000 записей\")\n",
|
||||||
|
" print(f\"\\n Результаты будут сохранены в: {csv_file}\")\n",
|
||||||
|
" \n",
|
||||||
|
" shuffled_records, sorted_records = generate_records(10000)\n",
|
||||||
|
" print(f\" Сгенерировано 10000 записей\")\n",
|
||||||
|
" \n",
|
||||||
|
" existing_names = [shuffled_records[i][0] for i in random.sample(range(10000), 100)]\n",
|
||||||
|
" nonexisting_names = [f\"NotExist_{i}\" for i in range(10)]\n",
|
||||||
|
" search_names = existing_names + nonexisting_names\n",
|
||||||
|
" delete_names = [shuffled_records[i][0] for i in random.sample(range(10000), 50)]\n",
|
||||||
|
" \n",
|
||||||
|
" results = [[\"Структура\", \"Режим\", \"Операция\", \n",
|
||||||
|
" \"Замер1(с)\", \"Замер2(с)\", \"Замер3(с)\", \"Замер4(с)\", \"Замер5(с)\", \n",
|
||||||
|
" \"Среднее(с)\"]]\n",
|
||||||
|
" \n",
|
||||||
|
" for mode_name, records in [(\"случайный\", shuffled_records), \n",
|
||||||
|
" (\"отсортированный\", sorted_records)]:\n",
|
||||||
|
" \n",
|
||||||
|
" print(f\"\\n2. Тестирование режима: {mode_name}\")\n",
|
||||||
|
" \n",
|
||||||
|
" for struct_name in [\"linked_list\", \"hash_table\", \"bst\"]:\n",
|
||||||
|
" print(f\"\\n {struct_name.upper()}:\")\n",
|
||||||
|
" \n",
|
||||||
|
" print(\" Вставка 10000 записей\")\n",
|
||||||
|
" insert_times, filled_struct = measure_insertion(struct_name, records)\n",
|
||||||
|
" avg_insert = sum(insert_times) / 5\n",
|
||||||
|
" print(f\" Время: {avg_insert:.4f} сек (среднее)\")\n",
|
||||||
|
" \n",
|
||||||
|
" print(\" Поиск 110 записей\")\n",
|
||||||
|
" search_times = measure_search(struct_name, filled_struct, search_names)\n",
|
||||||
|
" avg_search = sum(search_times) / 5\n",
|
||||||
|
" print(f\" Время: {avg_search:.4f} сек (среднее)\")\n",
|
||||||
|
" \n",
|
||||||
|
" print(\" Удаление 50 записей\")\n",
|
||||||
|
" delete_times = measure_deletion(struct_name, filled_struct, delete_names)\n",
|
||||||
|
" avg_delete = sum(delete_times) / 5\n",
|
||||||
|
" print(f\" Время: {avg_delete:.4f} сек (среднее)\")\n",
|
||||||
|
" \n",
|
||||||
|
" results.append([struct_name, mode_name, \"вставка\"] + insert_times + [avg_insert])\n",
|
||||||
|
" results.append([struct_name, mode_name, \"поиск\"] + search_times + [avg_search])\n",
|
||||||
|
" results.append([struct_name, mode_name, \"удаление\"] + delete_times + [avg_delete])\n",
|
||||||
|
" \n",
|
||||||
|
" print(\"\\n3. Сохранение результатов\")\n",
|
||||||
|
" with open(csv_file, \"w\", newline=\"\", encoding=\"utf-8\") as f:\n",
|
||||||
|
" writer = csv.writer(f)\n",
|
||||||
|
" writer.writerows(results)\n",
|
||||||
|
" print(f\" Результаты сохранены в: {csv_file}\")\n",
|
||||||
|
" \n",
|
||||||
|
" print(\"СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ\")\n",
|
||||||
|
" print(f\"{'Структура':<15} {'Режим':<12} {'Операция':<10} {'Среднее время (сек)':<20}\")\n",
|
||||||
|
" \n",
|
||||||
|
" for row in results[1:]:\n",
|
||||||
|
" struct, mode, op, t1, t2, t3, t4, t5, avg = row\n",
|
||||||
|
" print(f\"{struct:<15} {mode:<12} {op:<10} {avg:<20.6f}\")\n",
|
||||||
|
" \n",
|
||||||
|
" return results, docs_dir\n",
|
||||||
|
"\n",
|
||||||
|
"#4. Graphics\n",
|
||||||
|
"\n",
|
||||||
|
"def create_graphs(results, docs_dir):\n",
|
||||||
|
" \n",
|
||||||
|
" print(\"\\n4. Построение графиков\")\n",
|
||||||
|
" \n",
|
||||||
|
" data = {}\n",
|
||||||
|
" for row in results[1:]:\n",
|
||||||
|
" struct = row[0]\n",
|
||||||
|
" mode = row[1]\n",
|
||||||
|
" op = row[2]\n",
|
||||||
|
" avg = row[8]\n",
|
||||||
|
" \n",
|
||||||
|
" if struct not in data:\n",
|
||||||
|
" data[struct] = {}\n",
|
||||||
|
" if mode not in data[struct]:\n",
|
||||||
|
" data[struct][mode] = {}\n",
|
||||||
|
" data[struct][mode][op] = avg\n",
|
||||||
|
" \n",
|
||||||
|
" \n",
|
||||||
|
" struct_labels = {\n",
|
||||||
|
" 'linked_list': 'LinkedList',\n",
|
||||||
|
" 'hash_table': 'HashTable',\n",
|
||||||
|
" 'bst': 'BST'\n",
|
||||||
|
" }\n",
|
||||||
|
" \n",
|
||||||
|
" \n",
|
||||||
|
" colors = {\n",
|
||||||
|
" 'linked_list': '#8b00ff', \n",
|
||||||
|
" 'hash_table': '#81d8d0', \n",
|
||||||
|
" 'bst': '#000000' \n",
|
||||||
|
" }\n",
|
||||||
|
" \n",
|
||||||
|
" \n",
|
||||||
|
" fig, axes = plt.subplots(1, 3, figsize=(15, 6))\n",
|
||||||
|
" fig.suptitle('Сравнение производительности структур данных', fontsize=16, fontweight='bold')\n",
|
||||||
|
" \n",
|
||||||
|
" operations = ['вставка', 'поиск', 'удаление']\n",
|
||||||
|
" operation_titles = ['Вставка\\n(10000 записей)', 'Поиск\\n(110 запросов)', 'Удаление\\n(50 записей)']\n",
|
||||||
|
" modes = ['случайный', 'отсортированный']\n",
|
||||||
|
" mode_labels = ['Случайный', 'Отсортированный']\n",
|
||||||
|
" \n",
|
||||||
|
" for idx, (op, op_title) in enumerate(zip(operations, operation_titles)):\n",
|
||||||
|
" ax = axes[idx]\n",
|
||||||
|
" \n",
|
||||||
|
" # Позиции для групп столбцов\n",
|
||||||
|
" x = np.arange(len(modes)) # [0, 1]\n",
|
||||||
|
" width = 0.3 # ширина одного столбца\n",
|
||||||
|
" multiplier = 0\n",
|
||||||
|
" \n",
|
||||||
|
" for struct in ['linked_list', 'hash_table', 'bst']:\n",
|
||||||
|
" values = [data[struct][mode][op] for mode in modes]\n",
|
||||||
|
" offset = width * multiplier\n",
|
||||||
|
" bars = ax.bar(x + offset, values, width, \n",
|
||||||
|
" label=struct_labels[struct], \n",
|
||||||
|
" color=colors[struct],\n",
|
||||||
|
" edgecolor='black', linewidth=0.5)\n",
|
||||||
|
" \n",
|
||||||
|
" \n",
|
||||||
|
" for bar, val in zip(bars, values):\n",
|
||||||
|
" if val < 0.01:\n",
|
||||||
|
" ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + val*0.05, \n",
|
||||||
|
" f'{val:.5f}', ha='center', va='bottom', fontsize=8, rotation=0)\n",
|
||||||
|
" else:\n",
|
||||||
|
" ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + val*0.02, \n",
|
||||||
|
" f'{val:.4f}', ha='center', va='bottom', fontsize=8, rotation=0)\n",
|
||||||
|
" \n",
|
||||||
|
" multiplier += 1\n",
|
||||||
|
" \n",
|
||||||
|
" \n",
|
||||||
|
" ax.set_title(op_title, fontsize=12, fontweight='bold')\n",
|
||||||
|
" ax.set_ylabel('Время (секунды)', fontsize=10)\n",
|
||||||
|
" ax.set_xlabel('Режим данных', fontsize=10)\n",
|
||||||
|
" ax.set_xticks(x + width)\n",
|
||||||
|
" ax.set_xticklabels(mode_labels)\n",
|
||||||
|
" ax.legend(loc='upper left', fontsize=8)\n",
|
||||||
|
" ax.grid(True, alpha=0.3, axis='y')\n",
|
||||||
|
" \n",
|
||||||
|
" \n",
|
||||||
|
" all_values = [data[s][m][op] for s in ['linked_list', 'hash_table', 'bst'] for m in modes]\n",
|
||||||
|
" if max(all_values) / min(all_values) > 100:\n",
|
||||||
|
" ax.set_yscale('log')\n",
|
||||||
|
" ax.set_ylabel('Время (секунды) - логарифмическая шкала', fontsize=9)\n",
|
||||||
|
" \n",
|
||||||
|
" plt.tight_layout()\n",
|
||||||
|
" graph_path = os.path.join(docs_dir, \"performance_graphs.png\")\n",
|
||||||
|
" plt.savefig(graph_path, dpi=150, bbox_inches='tight')\n",
|
||||||
|
" plt.close()\n",
|
||||||
|
" print(f\" Графики сохранены в: {graph_path}\")\n",
|
||||||
|
" \n",
|
||||||
|
" return graph_path\n",
|
||||||
|
"\n",
|
||||||
|
"#5. Main program\n",
|
||||||
|
"\n",
|
||||||
|
"if __name__ == \"__main__\":\n",
|
||||||
|
" \n",
|
||||||
|
" results, docs_dir = run_experiment()\n",
|
||||||
|
" \n",
|
||||||
|
" \n",
|
||||||
|
" try:\n",
|
||||||
|
" graph_file = create_graphs(results, docs_dir)\n",
|
||||||
|
" \n",
|
||||||
|
" print(\"ЭКСПЕРИМЕНТ ЗАВЕРШЕН УСПЕШНО!\")\n",
|
||||||
|
" print(\"\\n СОЗДАННЫЕ ФАЙЛЫ:\")\n",
|
||||||
|
" print(f\" Данные: {os.path.join(docs_dir, 'experiment_results.csv')}\")\n",
|
||||||
|
" print(f\" Графики: {graph_file}\")\n",
|
||||||
|
" \n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(f\"\\n Ошибка при построении графиков: {e}\")\n",
|
||||||
|
" print(\" Убедитесь, что установлен matplotlib: pip install matplotlib\")\n",
|
||||||
|
" print(\"ЭКСПЕРИМЕНТ ЗАВЕРШЕН (без графиков)\")\n",
|
||||||
|
" print(f\"\\n CSV файл сохранен: {os.path.join(docs_dir, 'experiment_results.csv')}\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "e02735f2-61dc-484b-b74c-1456f7399863",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python [conda env:base] *",
|
||||||
|
"language": "python",
|
||||||
|
"name": "conda-base-py"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.13.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
BIN
MininaVD/docs/performance_graphs.png
Normal file
BIN
MininaVD/docs/performance_graphs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
Loading…
Reference in New Issue
Block a user