[1] initial commit

This commit is contained in:
Roman 2026-05-23 20:54:44 +03:00
parent 134b330145
commit 396b19bf71
8 changed files with 837 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View File

@ -0,0 +1,31 @@
Структура,Режим,Прогон,Вставка (сек),Поиск (сек),Удаление (сек)
LinkedList,случайный,1,0.9549722000010661,0.01907249999931082,0.011641299999610055
LinkedList,случайный,2,0.9401399000016681,0.01862980000078096,0.011389800001779804
LinkedList,случайный,3,0.9635646999995515,0.019138600000587758,0.01164940000307979
LinkedList,случайный,4,0.9656800999982806,0.01934369999798946,0.011737699998775497
LinkedList,случайный,5,0.9609748999973817,0.019405200000619516,0.011893200000486104
LinkedList,отсортированный,1,0.9114345000016328,0.015180999998847255,0.01074729999891133
LinkedList,отсортированный,2,0.8903370000007271,0.015180900001723785,0.010781699998915428
LinkedList,отсортированный,3,0.8930579000007128,0.015323700001317775,0.010789800002385164
LinkedList,отсортированный,4,0.8930441000011342,0.015232199999445584,0.010813600001711166
LinkedList,отсортированный,5,0.8936487999999372,0.015375900002254639,0.010843100000784034
HashTable,случайный,1,0.008163899998180568,0.0001584999990882352,7.74000000092201e-05
HashTable,случайный,2,0.00817319999987376,0.0001570999993418809,7.639999967068434e-05
HashTable,случайный,3,0.008005100000445964,0.00015559999883407727,7.579999873996712e-05
HashTable,случайный,4,0.008168999996996718,0.00015559999883407727,7.560000085504726e-05
HashTable,случайный,5,0.008011800000531366,0.0001559999982418958,7.579999873996712e-05
HashTable,отсортированный,1,0.00789959999747225,0.00015469999925699085,7.579999873996712e-05
HashTable,отсортированный,2,0.007853000002796762,0.00015440000061062165,7.569999797851779e-05
HashTable,отсортированный,3,0.00799140000162879,0.00015519999942625873,7.699999696342275e-05
HashTable,отсортированный,4,0.008009199998923577,0.00015419999908772297,7.589999950141646e-05
HashTable,отсортированный,5,0.007893400001194095,0.00015449999773409218,7.579999873996712e-05
BST,случайный,1,0.01466690000233939,0.0002459999996062834,0.0001467000001866836
BST,случайный,2,0.014466300002823118,0.00024329999723704532,0.000143599998409627
BST,случайный,3,0.014517399999022018,0.00024330000087502412,0.00014369999917107634
BST,случайный,4,0.014434400000027381,0.00024290000146720558,0.00014279999959398992
BST,случайный,5,0.06353280000257655,0.0002440999996906612,0.00014400000145542435
BST,отсортированный,1,2.599753700000292,0.0408674999998766,0.030090399999608053
BST,отсортированный,2,2.558562300000631,0.040827799999533454,0.030592600000090897
BST,отсортированный,3,2.5695390999972005,0.040459600000758655,0.030263900000136346
BST,отсортированный,4,2.569048000001203,0.040358000002015615,0.03027529999963008
BST,отсортированный,5,2.556947400000354,0.04035379999913857,0.03032600000005914
1 Структура Режим Прогон Вставка (сек) Поиск (сек) Удаление (сек)
2 LinkedList случайный 1 0.9549722000010661 0.01907249999931082 0.011641299999610055
3 LinkedList случайный 2 0.9401399000016681 0.01862980000078096 0.011389800001779804
4 LinkedList случайный 3 0.9635646999995515 0.019138600000587758 0.01164940000307979
5 LinkedList случайный 4 0.9656800999982806 0.01934369999798946 0.011737699998775497
6 LinkedList случайный 5 0.9609748999973817 0.019405200000619516 0.011893200000486104
7 LinkedList отсортированный 1 0.9114345000016328 0.015180999998847255 0.01074729999891133
8 LinkedList отсортированный 2 0.8903370000007271 0.015180900001723785 0.010781699998915428
9 LinkedList отсортированный 3 0.8930579000007128 0.015323700001317775 0.010789800002385164
10 LinkedList отсортированный 4 0.8930441000011342 0.015232199999445584 0.010813600001711166
11 LinkedList отсортированный 5 0.8936487999999372 0.015375900002254639 0.010843100000784034
12 HashTable случайный 1 0.008163899998180568 0.0001584999990882352 7.74000000092201e-05
13 HashTable случайный 2 0.00817319999987376 0.0001570999993418809 7.639999967068434e-05
14 HashTable случайный 3 0.008005100000445964 0.00015559999883407727 7.579999873996712e-05
15 HashTable случайный 4 0.008168999996996718 0.00015559999883407727 7.560000085504726e-05
16 HashTable случайный 5 0.008011800000531366 0.0001559999982418958 7.579999873996712e-05
17 HashTable отсортированный 1 0.00789959999747225 0.00015469999925699085 7.579999873996712e-05
18 HashTable отсортированный 2 0.007853000002796762 0.00015440000061062165 7.569999797851779e-05
19 HashTable отсортированный 3 0.00799140000162879 0.00015519999942625873 7.699999696342275e-05
20 HashTable отсортированный 4 0.008009199998923577 0.00015419999908772297 7.589999950141646e-05
21 HashTable отсортированный 5 0.007893400001194095 0.00015449999773409218 7.579999873996712e-05
22 BST случайный 1 0.01466690000233939 0.0002459999996062834 0.0001467000001866836
23 BST случайный 2 0.014466300002823118 0.00024329999723704532 0.000143599998409627
24 BST случайный 3 0.014517399999022018 0.00024330000087502412 0.00014369999917107634
25 BST случайный 4 0.014434400000027381 0.00024290000146720558 0.00014279999959398992
26 BST случайный 5 0.06353280000257655 0.0002440999996906612 0.00014400000145542435
27 BST отсортированный 1 2.599753700000292 0.0408674999998766 0.030090399999608053
28 BST отсортированный 2 2.558562300000631 0.040827799999533454 0.030592600000090897
29 BST отсортированный 3 2.5695390999972005 0.040459600000758655 0.030263900000136346
30 BST отсортированный 4 2.569048000001203 0.040358000002015615 0.03027529999963008
31 BST отсортированный 5 2.556947400000354 0.04035379999913857 0.03032600000005914

View File

@ -0,0 +1,19 @@
Структура,Режим,Операция,Среднее (сек),Мин (сек),Макс (сек)
LinkedList,случайный,Вставка,0.957066,0.940140,0.965680
LinkedList,случайный,Поиск,0.019118,0.018630,0.019405
LinkedList,случайный,Удаление,0.011662,0.011390,0.011893
LinkedList,отсортированный,Вставка,0.896304,0.890337,0.911435
LinkedList,отсортированный,Поиск,0.015259,0.015181,0.015376
LinkedList,отсортированный,Удаление,0.010795,0.010747,0.010843
HashTable,случайный,Вставка,0.008105,0.008005,0.008173
HashTable,случайный,Поиск,0.000157,0.000156,0.000158
HashTable,случайный,Удаление,0.000076,0.000076,0.000077
HashTable,отсортированный,Вставка,0.007929,0.007853,0.008009
HashTable,отсортированный,Поиск,0.000155,0.000154,0.000155
HashTable,отсортированный,Удаление,0.000076,0.000076,0.000077
BST,случайный,Вставка,0.024324,0.014434,0.063533
BST,случайный,Поиск,0.000244,0.000243,0.000246
BST,случайный,Удаление,0.000144,0.000143,0.000147
BST,отсортированный,Вставка,2.570770,2.556947,2.599754
BST,отсортированный,Поиск,0.040573,0.040354,0.040867
BST,отсортированный,Удаление,0.030310,0.030090,0.030593
1 Структура Режим Операция Среднее (сек) Мин (сек) Макс (сек)
2 LinkedList случайный Вставка 0.957066 0.940140 0.965680
3 LinkedList случайный Поиск 0.019118 0.018630 0.019405
4 LinkedList случайный Удаление 0.011662 0.011390 0.011893
5 LinkedList отсортированный Вставка 0.896304 0.890337 0.911435
6 LinkedList отсортированный Поиск 0.015259 0.015181 0.015376
7 LinkedList отсортированный Удаление 0.010795 0.010747 0.010843
8 HashTable случайный Вставка 0.008105 0.008005 0.008173
9 HashTable случайный Поиск 0.000157 0.000156 0.000158
10 HashTable случайный Удаление 0.000076 0.000076 0.000077
11 HashTable отсортированный Вставка 0.007929 0.007853 0.008009
12 HashTable отсортированный Поиск 0.000155 0.000154 0.000155
13 HashTable отсортированный Удаление 0.000076 0.000076 0.000077
14 BST случайный Вставка 0.024324 0.014434 0.063533
15 BST случайный Поиск 0.000244 0.000243 0.000246
16 BST случайный Удаление 0.000144 0.000143 0.000147
17 BST отсортированный Вставка 2.570770 2.556947 2.599754
18 BST отсортированный Поиск 0.040573 0.040354 0.040867
19 BST отсортированный Удаление 0.030310 0.030090 0.030593

View File

@ -0,0 +1,206 @@
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 создан")

View File

@ -0,0 +1,83 @@
bst_code = ''
def bst_insert(root, name, phone):
if root is None:
return {'name': name, 'phone': phone, 'left': None, 'right': None}
if name == root['name']:
root['phone'] = phone
elif name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
else:
root['right'] = bst_insert(root['right'], name, phone)
return root
def bst_find(root, name):
if root is None:
return None
if name == root['name']:
return root['phone']
elif name < root['name']:
return bst_find(root['left'], name)
else:
return bst_find(root['right'], name)
def bst_find_min(root):
current = root
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']
else:
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):
result = []
def in_order(node):
if node is not None:
in_order(node['left'])
result.append((node['name'], node['phone']))
in_order(node['right'])
in_order(root)
return result
with open('/mnt/agents/output/lab1/src/bst.py', 'w', encoding='utf-8') as f:
f.write(bst_code)
print("✅ bst.py создан")

View File

@ -0,0 +1,386 @@
experiment_code = ''
import time
import csv
import random
import sys
import matplotlib.pyplot as plt
import numpy as np
from linked_list import ll_insert, ll_find, ll_delete, ll_list_all
from hash_table import ht_insert, ht_find, ht_delete, ht_list_all
from bst import bst_insert, bst_find, bst_delete, bst_list_all
sys.setrecursionlimit(20000)
# параметры
N = 5000 # Количество записей
NUM_RUNS = 5 # Количество прогонов для усреднения
BUCKET_SIZE = N // 2 # Размер хеш-таблицы (load factor ~2)
def generate_data(n):
records = []
for i in range(n):
name = f"User_{i:05d}"
phone = f"+7{random.randint(9000000000, 9999999999)}"
records.append((name, phone))
records_shuffled = records.copy()
random.shuffle(records_shuffled)
records_sorted = sorted(records, key=lambda x: x[0])
return records, records_shuffled, records_sorted
def run_linked_list_experiment(records, search_existing, search_nonexistent, names_to_delete):
head = None
# Вставка
start = time.perf_counter()
for name, phone in records:
head = ll_insert(head, name, phone)
end = time.perf_counter()
insert_time = end - start
# Поиск
start = time.perf_counter()
for name in search_existing:
ll_find(head, name)
for name in search_nonexistent:
ll_find(head, name)
end = time.perf_counter()
find_time = end - start
# Удаление
start = time.perf_counter()
for name in names_to_delete:
head = ll_delete(head, name)
end = time.perf_counter()
delete_time = end - start
return insert_time, find_time, delete_time
def run_hash_table_experiment(records, search_existing, search_nonexistent, names_to_delete):
buckets = [None] * BUCKET_SIZE
# Вставка
start = time.perf_counter()
for name, phone in records:
ht_insert(buckets, name, phone)
end = time.perf_counter()
insert_time = end - start
# Поиск
start = time.perf_counter()
for name in search_existing:
ht_find(buckets, name)
for name in search_nonexistent:
ht_find(buckets, name)
end = time.perf_counter()
find_time = end - start
# Удаление
start = time.perf_counter()
for name in names_to_delete:
ht_delete(buckets, name)
end = time.perf_counter()
delete_time = end - start
return insert_time, find_time, delete_time
def run_bst_experiment(records, search_existing, search_nonexistent, names_to_delete):
root = None
# Вставка
start = time.perf_counter()
for name, phone in records:
root = bst_insert(root, name, phone)
end = time.perf_counter()
insert_time = end - start
# Поиск
start = time.perf_counter()
for name in search_existing:
bst_find(root, name)
for name in search_nonexistent:
bst_find(root, name)
end = time.perf_counter()
find_time = end - start
# Удаление
start = time.perf_counter()
for name in names_to_delete:
root = bst_delete(root, name)
end = time.perf_counter()
delete_time = end - start
return insert_time, find_time, delete_time
def run_all_experiments():
print("=" * 60)
print("ЭКСПЕРИМЕНТ: Сравнение структур данных")
print(f"N = {N}, прогонов = {NUM_RUNS}")
print("=" * 60)
# Генерация данных
print("\\n[1/5] Генерация тестовых данных...")
records, records_shuffled, records_sorted = generate_data(N)
# Подготовка данных для поиска и удаления (фиксируем seed для воспроизводимости)
random.seed(42)
existing_names = [r[0] for r in records]
search_existing = random.sample(existing_names, 100)
search_nonexistent = [f"None_{i:05d}" for i in range(10)]
names_to_delete = random.sample(existing_names, 50)
print(f" Записей: {len(records)}")
print(f" Поиск: {len(search_existing)} существующих + {len(search_nonexistent)} несуществующих")
print(f" Удаление: {len(names_to_delete)} записей")
# Хранение результатов
all_results = []
print("\\n[2/5] Linked List...")
for run in range(NUM_RUNS):
t_insert, t_find, t_delete = run_linked_list_experiment(
records_shuffled, search_existing, search_nonexistent, names_to_delete
)
all_results.append({
'Структура': 'LinkedList', 'Режим': 'случайный', 'Прогон': run + 1,
'Вставка': t_insert, 'Поиск': t_find, 'Удаление': t_delete
})
print(f" Случайный прогон {run + 1}: insert={t_insert:.4f}s, find={t_find:.4f}s, delete={t_delete:.4f}s")
for run in range(NUM_RUNS):
t_insert, t_find, t_delete = run_linked_list_experiment(
records_sorted, search_existing, search_nonexistent, names_to_delete
)
all_results.append({
'Структура': 'LinkedList', 'Режим': 'отсортированный', 'Прогон': run + 1,
'Вставка': t_insert, 'Поиск': t_find, 'Удаление': t_delete
})
print(f" Отсортированный прогон {run + 1}: insert={t_insert:.4f}s, find={t_find:.4f}s, delete={t_delete:.4f}s")
print("\\n[3/5] Hash Table...")
for run in range(NUM_RUNS):
t_insert, t_find, t_delete = run_hash_table_experiment(
records_shuffled, search_existing, search_nonexistent, names_to_delete
)
all_results.append({
'Структура': 'HashTable', 'Режим': 'случайный', 'Прогон': run + 1,
'Вставка': t_insert, 'Поиск': t_find, 'Удаление': t_delete
})
print(f" Случайный прогон {run + 1}: insert={t_insert:.4f}s, find={t_find:.4f}s, delete={t_delete:.4f}s")
for run in range(NUM_RUNS):
t_insert, t_find, t_delete = run_hash_table_experiment(
records_sorted, search_existing, search_nonexistent, names_to_delete
)
all_results.append({
'Структура': 'HashTable', 'Режим': 'отсортированный', 'Прогон': run + 1,
'Вставка': t_insert, 'Поиск': t_find, 'Удаление': t_delete
})
print(f" Отсортированный прогон {run + 1}: insert={t_insert:.4f}s, find={t_find:.4f}s, delete={t_delete:.4f}s")
print("\\n[4/5] BST...")
for run in range(NUM_RUNS):
t_insert, t_find, t_delete = run_bst_experiment(
records_shuffled, search_existing, search_nonexistent, names_to_delete
)
all_results.append({
'Структура': 'BST', 'Режим': 'случайный', 'Прогон': run + 1,
'Вставка': t_insert, 'Поиск': t_find, 'Удаление': t_delete
})
print(f" Случайный прогон {run + 1}: insert={t_insert:.4f}s, find={t_find:.4f}s, delete={t_delete:.4f}s")
for run in range(NUM_RUNS):
t_insert, t_find, t_delete = run_bst_experiment(
records_sorted, search_existing, search_nonexistent, names_to_delete
)
all_results.append({
'Структура': 'BST', 'Режим': 'отсортированный', 'Прогон': run + 1,
'Вставка': t_insert, 'Поиск': t_find, 'Удаление': t_delete
})
print(f" Отсортированный прогон {run + 1}: insert={t_insert:.4f}s, find={t_find:.4f}s, delete={t_delete:.4f}s")
print("\\n[5/5] Сохранение результатов...")
# Сырые данные
with open('../docs/data/results_raw.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Структура', 'Режим', 'Прогон', 'Вставка (сек)', 'Поиск (сек)', 'Удаление (сек)'])
for r in all_results:
writer.writerow([r['Структура'], r['Режим'], r['Прогон'],
r['Вставка'], r['Поиск'], r['Удаление']])
print(" Сохранено: ../docs/data/results_raw.csv")
# Сводная таблица
from collections import defaultdict
avg_results = defaultdict(lambda: {'insert': [], 'find': [], 'delete': []})
for r in all_results:
key = (r['Структура'], r['Режим'])
avg_results[key]['insert'].append(r['Вставка'])
avg_results[key]['find'].append(r['Поиск'])
avg_results[key]['delete'].append(r['Удаление'])
with open('../docs/data/results_summary.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Структура', 'Режим', 'Операция', 'Среднее (сек)', 'Мин (сек)', 'Макс (сек)'])
for (struct, mode), times in avg_results.items():
writer.writerow([struct, mode, 'Вставка',
f"{sum(times['insert']) / len(times['insert']):.6f}",
f"{min(times['insert']):.6f}",
f"{max(times['insert']):.6f}"])
writer.writerow([struct, mode, 'Поиск',
f"{sum(times['find']) / len(times['find']):.6f}",
f"{min(times['find']):.6f}",
f"{max(times['find']):.6f}"])
writer.writerow([struct, mode, 'Удаление',
f"{sum(times['delete']) / len(times['delete']):.6f}",
f"{min(times['delete']):.6f}",
f"{max(times['delete']):.6f}"])
print(" Сохранено: ../docs/data/results_summary.csv")
print(" Построение графиков...")
build_charts(avg_results)
print(" Сохранено: ../docs/data/charts.png")
print("\\n" + "=" * 60)
print("ЭКСПЕРИМЕНТ ЗАВЕРШЁН!")
print("=" * 60)
return all_results, avg_results
def build_charts(avg_results):
"""Строит графики сравнения производительности."""
fig, axes = plt.subplots(2, 3, figsize=(18, 12))
fig.suptitle(f'Сравнение производительности структур данных (N={N})', fontsize=16, fontweight='bold')
structures = ['LinkedList', 'HashTable', 'BST']
modes = ['случайный', 'отсортированный']
struct_colors = {'LinkedList': '#FF6B6B', 'HashTable': '#4ECDC4', 'BST': '#45B7D1'}
# Подготовка данных для графиков
def get_value(struct, mode, op):
key = (struct, mode)
if key in avg_results:
return sum(avg_results[key][op]) / len(avg_results[key][op])
return 0
# График 1: Вставка
ax = axes[0, 0]
x = np.arange(len(modes))
width = 0.25
for i, struct in enumerate(structures):
vals = [get_value(struct, mode, 'insert') for mode in modes]
ax.bar(x + i * width, vals, width, label=struct, color=struct_colors[struct])
ax.set_xlabel('Режим данных')
ax.set_ylabel('Время (сек)')
ax.set_title('Вставка')
ax.set_xticks(x + width)
ax.set_xticklabels(modes)
ax.legend()
ax.set_yscale('log')
ax.grid(True, alpha=0.3)
# График 2: Поиск
ax = axes[0, 1]
for i, struct in enumerate(structures):
vals = [get_value(struct, mode, 'find') for mode in modes]
ax.bar(x + i * width, vals, width, label=struct, color=struct_colors[struct])
ax.set_xlabel('Режим данных')
ax.set_ylabel('Время (сек)')
ax.set_title('Поиск (110 операций)')
ax.set_xticks(x + width)
ax.set_xticklabels(modes)
ax.legend()
ax.set_yscale('log')
ax.grid(True, alpha=0.3)
# График 3: Удаление
ax = axes[0, 2]
for i, struct in enumerate(structures):
vals = [get_value(struct, mode, 'delete') for mode in modes]
ax.bar(x + i * width, vals, width, label=struct, color=struct_colors[struct])
ax.set_xlabel('Режим данных')
ax.set_ylabel('Время (сек)')
ax.set_title('Удаление (50 операций)')
ax.set_xticks(x + width)
ax.set_xticklabels(modes)
ax.legend()
ax.set_yscale('log')
ax.grid(True, alpha=0.3)
# График 4: BST деградация
ax = axes[1, 0]
bst_random = get_value('BST', 'случайный', 'insert')
bst_sorted = get_value('BST', 'отсортированный', 'insert')
ax.bar(['Случайный', 'Отсортированный'], [bst_random, bst_sorted],
color=['#45B7D1', '#E74C3C'])
ax.set_ylabel('Время (сек)')
ax.set_title('BST: влияние порядка данных на вставку')
for i, v in enumerate([bst_random, bst_sorted]):
ax.text(i, v + max(v * 0.05, 0.01), f'{v:.3f}s', ha='center', fontweight='bold')
ax.grid(True, alpha=0.3)
# График 5: Случайный режим — все операции
ax = axes[1, 1]
x = np.arange(len(structures))
width = 0.25
insert_vals = [get_value(s, 'случайный', 'insert') for s in structures]
find_vals = [get_value(s, 'случайный', 'find') for s in structures]
delete_vals = [get_value(s, 'случайный', 'delete') for s in structures]
ax.bar(x - width, insert_vals, width, label='Вставка', color='#FF6B6B')
ax.bar(x, find_vals, width, label='Поиск', color='#4ECDC4')
ax.bar(x + width, delete_vals, width, label='Удаление', color='#45B7D1')
ax.set_xlabel('Структура данных')
ax.set_ylabel('Время (сек)')
ax.set_title('Случайный режим: все операции')
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.legend()
ax.set_yscale('log')
ax.grid(True, alpha=0.3)
# График 6: Отсортированный режим — поиск и удаление
ax = axes[1, 2]
find_vals = [get_value(s, 'отсортированный', 'find') for s in structures]
delete_vals = [get_value(s, 'отсортированный', 'delete') for s in structures]
ax.bar(x - width / 2, find_vals, width, label='Поиск', color='#4ECDC4')
ax.bar(x + width / 2, delete_vals, width, label='Удаление', color='#45B7D1')
ax.set_xlabel('Структура данных')
ax.set_ylabel('Время (сек)')
ax.set_title('Отсортированный режим: поиск и удаление')
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.legend()
ax.set_yscale('log')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('../docs/data/charts.png', dpi=150, bbox_inches='tight')
plt.close()
if __name__ == '__main__':
run_all_experiments()
with open('/mnt/agents/output/lab1/src/experiment.py', 'w', encoding='utf-8') as f:
f.write(experiment_code)
print("✅ experiment.py создан")

View File

@ -0,0 +1,54 @@
hash_table_code = ''
from linked_list import ll_insert, ll_find, ll_delete, ll_list_all
def ht_hash(name, size):
h = 0
for char in name:
h = (h * 31 + ord(char)) % size
return h
def ht_insert(buckets, name, phone):
size = len(buckets)
index = ht_hash(name, size)
buckets[index] = ll_insert(buckets[index], name, phone)
return buckets
def ht_find(buckets, name):
size = len(buckets)
index = ht_hash(name, size)
return ll_find(buckets[index], name)
def ht_delete(buckets, name):
size = len(buckets)
index = ht_hash(name, size)
buckets[index] = ll_delete(buckets[index], name)
return buckets
def ht_list_all(buckets):
result = []
for bucket in buckets:
current = bucket
while current is not None:
result.append((current['name'], current['phone']))
current = current['next']
result.sort(key=lambda x: x[0])
return result
with open('/mnt/agents/output/lab1/src/hash_table.py', 'w', encoding='utf-8') as f:
f.write(hash_table_code)
print("✅ hash_table.py создан")

View File

@ -0,0 +1,58 @@
linked_list = ''
def ll_insert(head, name, phone):
new_node = {'name': name, 'phone': phone, 'next': None}
if head is None:
return new_node
current = head
while current is not None:
if current['name'] == name:
current['phone'] = phone
return head
if current['next'] is None:
break
current = current['next']
current['next'] = new_node
return head
def ll_find(head, name):
current = head
while current is not 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):
result = []
current = head
while current is not None:
result.append((current['name'], current['phone']))
current = current['next']
result.sort(key=lambda x: x[0])
return result
with open('/mnt/agents/output/lab1/src/linked_list.py', 'w', encoding='utf-8') as f:
f.write(linked_list)
print(linked_list)