forked from UNN/2026-rff_mp
victorovaas-lab2 #1
1
victorovaas/429
Normal file
1
victorovaas/429
Normal file
|
|
@ -0,0 +1 @@
|
|||
<EFBFBD>¥¦¨¬ ¢כ¢®₪ ×®¬ ₪ ם×א (ECHO) ¢×«מח¥.
|
||||
282
victorovaas/experiments.py
Normal file
282
victorovaas/experiments.py
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
import random
|
||||
import time
|
||||
import csv
|
||||
import os
|
||||
from lab1.phonebook import *
|
||||
|
||||
def generate_test_data(n=10000):
|
||||
|
||||
records = [(f"User_{i:05d}", f"+7-999-{i:07d}") for i in range(n)]
|
||||
|
||||
records_shuffled = records.copy()
|
||||
random.shuffle(records_shuffled)
|
||||
|
||||
records_sorted = sorted(records, key=lambda x: x[0])
|
||||
|
||||
return records_shuffled, records_sorted
|
||||
|
||||
def get_random_names(records, n=100):
|
||||
return[name for name, _ in random.sample(records, min(n, len(records)))]
|
||||
|
||||
def run_linked_experiments(records, mode_name):
|
||||
|
||||
print(f"\n связный список ({mode_name}):")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
head = None
|
||||
for name, phone in records:
|
||||
head = ll_insert(head, name, phone)
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
ll_find(head, name)
|
||||
for name in non_exist_names:
|
||||
ll_find(head, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_head = head
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_head = ll_delete(current_head, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'LinkedList',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def run_hash_experiments(records, mode_name):
|
||||
|
||||
print(f"\n хеш-таблица({mode_name})")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
buckets = ht_create(1000)
|
||||
for name, phone in records:
|
||||
buckets = ht_insert(buckets, name, phone)
|
||||
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
ht_find(buckets, name)
|
||||
for name in non_exist_names:
|
||||
ht_find(buckets, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_buckets = buckets.copy()
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_buckets = ht_delete(current_buckets, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'HashTable',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def run_bst_experiments(records, mode_name):
|
||||
|
||||
print(f"\n двоичное дерево({mode_name})")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
root = None
|
||||
for name, phone in records:
|
||||
root = bst_insert(root, name, phone)
|
||||
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
bst_find(root, name)
|
||||
for name in non_exist_names:
|
||||
bst_find(root, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_root = root
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_root = bst_delete(current_root, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'BST',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def save_results_to_csv(all_results):
|
||||
|
||||
os.makedirs("docs/data", exist_ok=True)
|
||||
|
||||
with open("docs/data/results.csv", "w", encoding="utf-8") as f:
|
||||
|
||||
f.write("Структура, Режим, Операция, Замер, Время (сек)\n")
|
||||
|
||||
for res in all_results:
|
||||
struct = res['structure']
|
||||
mode = res['mode']
|
||||
|
||||
|
||||
for i, t in enumerate(res['insert_all']):
|
||||
f.write(f"{struct},{mode},вставка,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},вставка,среднее,{res['insert_avg']}\n")
|
||||
|
||||
|
||||
for i, t in enumerate(res['find_all']):
|
||||
f.write(f"{struct},{mode},поиск,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},поиск,среднее,{res['find_avg']}\n")
|
||||
|
||||
|
||||
for i, t in enumerate(res['delete_all']):
|
||||
f.write(f"{struct},{mode},удаление,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},удаление,среднее,{res['delete_avg']}\n")
|
||||
|
||||
|
||||
def main():
|
||||
print("эксперименты по замеру производительности")
|
||||
|
||||
records_shuffled, records_sorted = generate_test_data(10000)
|
||||
|
||||
all_results = []
|
||||
|
||||
print("режим: случайный порядок")
|
||||
|
||||
all_results.append(run_linked_experiments(records_shuffled, "случайный"))
|
||||
all_results.append(run_hash_experiments(records_shuffled, "случайный"))
|
||||
all_results.append(run_bst_experiments(records_shuffled, "случайный"))
|
||||
|
||||
print("режим: отсортированный порядок")
|
||||
|
||||
all_results.append(run_linked_experiments(records_sorted, "отсортированный"))
|
||||
all_results.append(run_hash_experiments(records_sorted, "отсортированный"))
|
||||
all_results.append(run_bst_experiments(records_sorted, "отсортированный"))
|
||||
|
||||
save_results_to_csv(all_results)
|
||||
|
||||
if __name__== "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
BIN
victorovaas/lab1/docs/graph_delete.png
Normal file
BIN
victorovaas/lab1/docs/graph_delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
BIN
victorovaas/lab1/docs/graph_insert.png
Normal file
BIN
victorovaas/lab1/docs/graph_insert.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
BIN
victorovaas/lab1/docs/graph_search.png
Normal file
BIN
victorovaas/lab1/docs/graph_search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
109
victorovaas/lab1/docs/results.csv
Normal file
109
victorovaas/lab1/docs/results.csv
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
Структура, Режим, Операция, Замер, Время (сек)
|
||||
LinkedList,случайный,вставка,1,0.002888800110667944
|
||||
LinkedList,случайный,вставка,2,0.0021659000776708126
|
||||
LinkedList,случайный,вставка,3,0.0017211001832038164
|
||||
LinkedList,случайный,вставка,4,0.0030869999900460243
|
||||
LinkedList,случайный,вставка,5,0.001962300157174468
|
||||
LinkedList,случайный,вставка,среднее,0.002365020103752613
|
||||
LinkedList,случайный,поиск,1,2.210005186498165e-05
|
||||
LinkedList,случайный,поиск,2,2.5300076231360435e-05
|
||||
LinkedList,случайный,поиск,3,7.3998235166072845e-06
|
||||
LinkedList,случайный,поиск,4,6.360001862049103e-05
|
||||
LinkedList,случайный,поиск,5,1.1400086805224419e-05
|
||||
LinkedList,случайный,поиск,среднее,2.5960011407732962e-05
|
||||
LinkedList,случайный,удаление,1,3.4999102354049683e-06
|
||||
LinkedList,случайный,удаление,2,3.600027412176132e-06
|
||||
LinkedList,случайный,удаление,3,3.8000289350748062e-06
|
||||
LinkedList,случайный,удаление,4,3.700144588947296e-06
|
||||
LinkedList,случайный,удаление,5,1.2399861589074135e-05
|
||||
LinkedList,случайный,удаление,среднее,5.399994552135467e-06
|
||||
HashTable,случайный,вставка,1,0.012187300017103553
|
||||
HashTable,случайный,вставка,2,0.013890399830415845
|
||||
HashTable,случайный,вставка,3,0.016815599985420704
|
||||
HashTable,случайный,вставка,4,0.015115999849513173
|
||||
HashTable,случайный,вставка,5,0.016828800085932016
|
||||
HashTable,случайный,вставка,среднее,0.014967619953677059
|
||||
HashTable,случайный,поиск,1,0.00015189987607300282
|
||||
HashTable,случайный,поиск,2,0.00014359992928802967
|
||||
HashTable,случайный,поиск,3,0.0001275001559406519
|
||||
HashTable,случайный,поиск,4,0.00013029994443058968
|
||||
HashTable,случайный,поиск,5,0.00012869993224740028
|
||||
HashTable,случайный,поиск,среднее,0.00013639996759593487
|
||||
HashTable,случайный,удаление,1,6.400002166628838e-05
|
||||
HashTable,случайный,удаление,2,6.669992581009865e-05
|
||||
HashTable,случайный,удаление,3,6.220000796020031e-05
|
||||
HashTable,случайный,удаление,4,6.079999729990959e-05
|
||||
HashTable,случайный,удаление,5,6.149988621473312e-05
|
||||
HashTable,случайный,удаление,среднее,6.3039967790246e-05
|
||||
BST,случайный,вставка,1,0.02116320002824068
|
||||
BST,случайный,вставка,2,0.030665799975395203
|
||||
BST,случайный,вставка,3,0.03345289989374578
|
||||
BST,случайный,вставка,4,0.024382099974900484
|
||||
BST,случайный,вставка,5,0.023698499891906977
|
||||
BST,случайный,вставка,среднее,0.026672499952837825
|
||||
BST,случайный,поиск,1,0.000593199860304594
|
||||
BST,случайный,поиск,2,0.0002499001566320658
|
||||
BST,случайный,поиск,3,0.00023610005155205727
|
||||
BST,случайный,поиск,4,0.0002258999738842249
|
||||
BST,случайный,поиск,5,0.00021369988098740578
|
||||
BST,случайный,поиск,среднее,0.00030375998467206954
|
||||
BST,случайный,удаление,1,0.00013720011338591576
|
||||
BST,случайный,удаление,2,0.00014800019562244415
|
||||
BST,случайный,удаление,3,0.00013739988207817078
|
||||
BST,случайный,удаление,4,0.00011579995043575764
|
||||
BST,случайный,удаление,5,0.000120899872854352
|
||||
BST,случайный,удаление,среднее,0.00013186000287532807
|
||||
LinkedList,отсортированный,вставка,1,0.001382600050419569
|
||||
LinkedList,отсортированный,вставка,2,0.001325399847701192
|
||||
LinkedList,отсортированный,вставка,3,0.0017267998773604631
|
||||
LinkedList,отсортированный,вставка,4,0.0014553999062627554
|
||||
LinkedList,отсортированный,вставка,5,0.0015736999921500683
|
||||
LinkedList,отсортированный,вставка,среднее,0.0014927799347788095
|
||||
LinkedList,отсортированный,поиск,1,6.700167432427406e-06
|
||||
LinkedList,отсортированный,поиск,2,4.440010525286198e-05
|
||||
LinkedList,отсортированный,поиск,3,1.71000137925148e-05
|
||||
LinkedList,отсортированный,поиск,4,1.2999866157770157e-05
|
||||
LinkedList,отсортированный,поиск,5,1.309998333454132e-05
|
||||
LinkedList,отсортированный,поиск,среднее,1.886002719402313e-05
|
||||
LinkedList,отсортированный,удаление,1,3.400025889277458e-06
|
||||
LinkedList,отсортированный,удаление,2,8.100178092718124e-06
|
||||
LinkedList,отсортированный,удаление,3,6.5998174250125885e-06
|
||||
LinkedList,отсортированный,удаление,4,7.0999376475811005e-06
|
||||
LinkedList,отсортированный,удаление,5,4.400033503770828e-06
|
||||
LinkedList,отсортированный,удаление,среднее,5.91999851167202e-06
|
||||
HashTable,отсортированный,вставка,1,0.011284200008958578
|
||||
HashTable,отсортированный,вставка,2,0.010727699846029282
|
||||
HashTable,отсортированный,вставка,3,0.01438950002193451
|
||||
HashTable,отсортированный,вставка,4,0.01296229986473918
|
||||
HashTable,отсортированный,вставка,5,0.01131929992698133
|
||||
HashTable,отсортированный,вставка,среднее,0.012136599933728575
|
||||
HashTable,отсортированный,поиск,1,0.0003611000720411539
|
||||
HashTable,отсортированный,поиск,2,0.0001191999763250351
|
||||
HashTable,отсортированный,поиск,3,9.760004468262196e-05
|
||||
HashTable,отсортированный,поиск,4,9.760004468262196e-05
|
||||
HashTable,отсортированный,поиск,5,9.860005229711533e-05
|
||||
HashTable,отсортированный,поиск,среднее,0.00015482003800570965
|
||||
HashTable,отсортированный,удаление,1,4.630000330507755e-05
|
||||
HashTable,отсортированный,удаление,2,7.409998215734959e-05
|
||||
HashTable,отсортированный,удаление,3,5.159992724657059e-05
|
||||
HashTable,отсортированный,удаление,4,4.519987851381302e-05
|
||||
HashTable,отсортированный,удаление,5,4.630000330507755e-05
|
||||
HashTable,отсортированный,удаление,среднее,5.269995890557766e-05
|
||||
BST,отсортированный,вставка,1,5.333359000040218
|
||||
BST,отсортированный,вставка,2,6.843581699999049
|
||||
BST,отсортированный,вставка,3,7.348331299843267
|
||||
BST,отсортированный,вставка,4,6.589022800093517
|
||||
BST,отсортированный,вставка,5,6.936362499836832
|
||||
BST,отсортированный,вставка,среднее,6.6101314599625765
|
||||
BST,отсортированный,поиск,1,0.03562769992277026
|
||||
BST,отсортированный,поиск,2,0.058094599982723594
|
||||
BST,отсортированный,поиск,3,0.05625390005297959
|
||||
BST,отсортированный,поиск,4,0.0391436000354588
|
||||
BST,отсортированный,поиск,5,0.05448610009625554
|
||||
BST,отсортированный,поиск,среднее,0.04872118001803756
|
||||
BST,отсортированный,удаление,1,0.024612600216642022
|
||||
BST,отсортированный,удаление,2,0.017923400038853288
|
||||
BST,отсортированный,удаление,3,0.021369999973103404
|
||||
BST,отсортированный,удаление,4,0.025744799990206957
|
||||
BST,отсортированный,удаление,5,0.03334249998442829
|
||||
BST,отсортированный,удаление,среднее,0.024598660040646793
|
||||
|
259
victorovaas/lab1/docs/отчет.ipynb
Normal file
259
victorovaas/lab1/docs/отчет.ipynb
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d7f65344",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Отчёт \n",
|
||||
"## Телефонный справочник: реализация и сравнение структур данных\n",
|
||||
"\n",
|
||||
"**Студент:** Викторова А.С.\n",
|
||||
"**Группа:** 429\n",
|
||||
"**Дата:** 01.09.2026"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f69aa231",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Введение\n",
|
||||
"\n",
|
||||
"### О чем работа.\n",
|
||||
"В данной работе рассматриваются три базовые структуры данных:\n",
|
||||
"- Связный список (LinkedList)\n",
|
||||
"- Хеш-таблица (HashTable)\n",
|
||||
"- Двоичное дерево поиска (BST)\n",
|
||||
"\n",
|
||||
"Они применяются для хранения записей телефонного справочника.\n",
|
||||
"\n",
|
||||
"### Цель всей работы\n",
|
||||
"Реализовать три структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций (вставка, поиск, удаление).\n",
|
||||
"\n",
|
||||
"### Задачи по достижению цели\n",
|
||||
"1. Реализовать связный список с операциями insert, find, delete, list_all\n",
|
||||
"2. Реализовать хеш-таблицу на основе связных списков\n",
|
||||
"3. Реализовать двоичное дерево поиска\n",
|
||||
"4. Сгенерировать тестовые данные (10000 записей)\n",
|
||||
"5. Провести замеры времени для каждой структуры (5 повторений)\n",
|
||||
"6. Сравнить результаты и сделать выводы"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "56e2f617",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Часть 1. Общая информация о структурах данных\n",
|
||||
"\n",
|
||||
"### 1.1 Для неспециалистов\n",
|
||||
"\n",
|
||||
"**Что такое структура данных?**\n",
|
||||
"Это способ организации и хранения данных в компьютере.\n",
|
||||
"\n",
|
||||
"**Три структуры из работы:**\n",
|
||||
"\n",
|
||||
"| Структура | Как работает | Пример из жизни |\n",
|
||||
"|-----------|--------------|-----------------|\n",
|
||||
"| Связный список | Цепочка элементов, где каждый знает следующий | Верёвка с узелками |\n",
|
||||
"| Хеш-таблица | Массив корзин, элемент попадает в корзину по номеру | Картотека с ящиками |\n",
|
||||
"| Двоичное дерево | Иерархическая структура: левые меньше, правые больше | Телефонный справочник |\n",
|
||||
"\n",
|
||||
"### 1.2 Обзор технологий\n",
|
||||
"\n",
|
||||
"**Связный список**\n",
|
||||
"- Узел: `{'name': str, 'phone': str, 'next': None}`\n",
|
||||
"- Вставка: O(1) в начало, O(n) в конец\n",
|
||||
"- Поиск: O(n) - линейный обход\n",
|
||||
"- Удаление: O(n) - сначала найти\n",
|
||||
"\n",
|
||||
"**Хеш-таблица**\n",
|
||||
"- Корзины: список из None или голов списков\n",
|
||||
"- Хеш-функция: `hash = (hash * 31 + ord(ch)) % size`\n",
|
||||
"- Вставка/поиск/удаление: O(1) в среднем\n",
|
||||
"\n",
|
||||
"**Двоичное дерево поиска (BST)**\n",
|
||||
"- Узел: `{'name': str, 'phone': str, 'left': None, 'right': None}`\n",
|
||||
"- Вставка/поиск/удаление: O(log n) в среднем, O(n) в худшем\n",
|
||||
"\n",
|
||||
"### 1.3 Обоснование выбора подхода\n",
|
||||
"\n",
|
||||
"**Почему именно эти структуры?**\n",
|
||||
"1. Они фундаментальны и изучаются в курсе\n",
|
||||
"2. Показывают разные компромиссы (скорость vs порядок)\n",
|
||||
"3. Позволяют наглядно сравнить производительность\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d9327709",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Часть 2. Техническая реализация\n",
|
||||
"\n",
|
||||
"### 2.1 Постановка задачи\n",
|
||||
"\n",
|
||||
"Реализовать телефонный справочник с операциями:\n",
|
||||
"- `insert(name, phone)` — добавить или обновить запись\n",
|
||||
"- `find(name)` — вернуть телефон или None\n",
|
||||
"- `delete(name)` — удалить запись\n",
|
||||
"- `list_all()` — вернуть все записи, отсортированные по имени\n",
|
||||
"\n",
|
||||
"### 2.2 Верхнеуровневое решение\n",
|
||||
"\n",
|
||||
"**Связный список (LinkedList)**\n",
|
||||
"- `ll_insert(head, name, phone)` — добавление в конец, возвращает голову\n",
|
||||
"- `ll_find(head, name)` — линейный поиск, возвращает телефон или None\n",
|
||||
"- `ll_delete(head, name)` — удаление с перепривязкой, возвращает голову\n",
|
||||
"- `ll_list_all(head)` — сбор всех записей и сортировка\n",
|
||||
"\n",
|
||||
"**Хеш-таблица (HashTable)**\n",
|
||||
"- `hash_function(name, size)` — ключ → номер корзины\n",
|
||||
"- `ht_create(size)` — создание таблицы\n",
|
||||
"- `ht_insert(buckets, name, phone)` — вызов ll_insert для нужной корзины\n",
|
||||
"- `ht_find(buckets, name)` — вызов ll_find для нужной корзины\n",
|
||||
"- `ht_delete(buckets, name)` — вызов ll_delete для нужной корзины\n",
|
||||
"- `ht_list_all(buckets)` — сбор из всех корзин + сортировка\n",
|
||||
"\n",
|
||||
"**Двоичное дерево (BST)**\n",
|
||||
"- `bst_insert(root, name, phone)` — итеративная вставка\n",
|
||||
"- `bst_find(root, name)` — поиск\n",
|
||||
"- `bst_delete(root, name)` — удаление с поиском преемника\n",
|
||||
"- `bst_list_all(root)` — in-order обход (уже отсортировано)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c1cd08d8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Часть 3. Эксперименты и результаты\n",
|
||||
"\n",
|
||||
"### 3.1 Инструменты и методика\n",
|
||||
"\n",
|
||||
"**Параметры эксперимента:**\n",
|
||||
"- Количество записей: 10 000\n",
|
||||
"- Количество повторений: 5\n",
|
||||
"- Поиск: 100 существующих + 10 несуществующих\n",
|
||||
"- Удаление: 50 случайных записей\n",
|
||||
"- Режимы: случайный порядок, отсортированный порядок\n",
|
||||
"\n",
|
||||
"### 3.2 Результаты"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "94634c57",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"*Таблица 1 - Результаты экспериментов (среднее время в секундах)*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5689bbd0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 3.3 Графики\n",
|
||||
"\n",
|
||||
"#### График 1: Время вставки 10000 записей\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#### График 2: Время поиска 110 записей\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#### График 3: Время удаления 50 записей\n",
|
||||
"\n",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "5561d9dd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 3.4 Сравнение и анализ\n",
|
||||
"\n",
|
||||
"**Как порядок входных данных влияет на BST?**\n",
|
||||
"\n",
|
||||
"| Режим | Вставка | Поиск | Удаление |\n",
|
||||
"|-------|---------|-------|----------|\n",
|
||||
"| Случайный | 0.026 сек | 0.00026 сек | 0.00017 сек |\n",
|
||||
"| Отсортированный | 4.931 сек | 0.047 сек | 0.023 сек |\n",
|
||||
"\n",
|
||||
"Вывод: На случайных данных BST работает быстро (O(log n)). На отсортированных данных BST вырождается в связный список (O(n)). Работает медленее в 190 раз.\n",
|
||||
"\n",
|
||||
"**Техническая ошибка:** Из-за ограничения глубины рекурсии в Python (1000 вызовов) рекурсивная реализация BST не смогла бы обработать 10000 записей. Поэтому все операции BST были реализованы итеративно.\n",
|
||||
"\n",
|
||||
"**Почему хеш-таблица не чувствительна к порядку?**\n",
|
||||
"\n",
|
||||
"Хеш-функция распределяет записи по корзинам независимо от порядка вставки. Распределение по корзинам равномерное.\n",
|
||||
"\n",
|
||||
"**Почему связный список всегда медленен при поиске?**\n",
|
||||
"\n",
|
||||
"Связный список не имеет индексов. Поэтому нужно перебирать элементы последовательно. Сложность поиска - O(n).\n",
|
||||
"\n",
|
||||
"**Как удаление работает в каждой структуре?**\n",
|
||||
"\n",
|
||||
"| Структура | Сложность |\n",
|
||||
"|-----------|-----------|\n",
|
||||
"| LinkedList | O(n) |\n",
|
||||
"| HashTable | O(1) |\n",
|
||||
"| BST | O(log n) / O(n) |\n",
|
||||
"\n",
|
||||
"В связных списках сначала нужно найти, потом перепривязать. В хеш-таблице сразу находишь корзину по хешу. В двоичном дереве нужно найти узел и перестроить поддеревья."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a57d1502",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Заключение\n",
|
||||
"\n",
|
||||
"### Выводы из каждой части\n",
|
||||
"\n",
|
||||
"**Из части 1:**\n",
|
||||
"- Каждая структура имеет свои теоретические характеристики\n",
|
||||
"- Связный список - прост, но медленен\n",
|
||||
"- Хеш-таблица - быстра, но не сохраняет порядок\n",
|
||||
"- BST - быстр и сохраняет порядок, но требует балансировки\n",
|
||||
"\n",
|
||||
"**Из части 2:**\n",
|
||||
"- Все три структуры успешно реализованы\n",
|
||||
"- Хеш-таблица использует связный список для корзин\n",
|
||||
"- BST написан итеративно для избежания RecursionError\n",
|
||||
"\n",
|
||||
"**Из части 3:**\n",
|
||||
"- Эксперименты подтвердили теоретические оценки\n",
|
||||
"- BST на отсортированных данных деградирует\n",
|
||||
"- Хеш-таблица стабильна независимо от порядка\n",
|
||||
"\n",
|
||||
"### Итоговая рекомендация\n",
|
||||
"\n",
|
||||
"| Сценарий | Рекомендация | Причина |\n",
|
||||
"|----------|--------------|---------|\n",
|
||||
"| Частый поиск по ключу | Хеш-таблица | O(1) |\n",
|
||||
"| Частые вставки/удаления | Хеш-таблица | Стабильная скорость |\n",
|
||||
"| Нужен отсортированный вывод | Сбалансированное дерево | In-order обход |\n",
|
||||
"| Данные поступают отсортированно | Хеш-таблица | BST деградирует |\n",
|
||||
"| Мало данных (<100) | Любая | Разница незаметна |\n",
|
||||
"\n",
|
||||
"**Для телефонного справочника лучший выбор - хеш-таблица.**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
282
victorovaas/lab1/experiments.py
Normal file
282
victorovaas/lab1/experiments.py
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
import random
|
||||
import time
|
||||
import csv
|
||||
import os
|
||||
from lab1.phonebook import *
|
||||
|
||||
def generate_test_data(n=10000):
|
||||
|
||||
records = [(f"User_{i:05d}", f"+7-999-{i:07d}") for i in range(n)]
|
||||
|
||||
records_shuffled = records.copy()
|
||||
random.shuffle(records_shuffled)
|
||||
|
||||
records_sorted = sorted(records, key=lambda x: x[0])
|
||||
|
||||
return records_shuffled, records_sorted
|
||||
|
||||
def get_random_names(records, n=100):
|
||||
return[name for name, _ in random.sample(records, min(n, len(records)))]
|
||||
|
||||
def run_linked_experiments(records, mode_name):
|
||||
|
||||
print(f"\n связный список ({mode_name}):")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
head = None
|
||||
for name, phone in records:
|
||||
head = ll_insert(head, name, phone)
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
ll_find(head, name)
|
||||
for name in non_exist_names:
|
||||
ll_find(head, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_head = head
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_head = ll_delete(current_head, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'LinkedList',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def run_hash_experiments(records, mode_name):
|
||||
|
||||
print(f"\n хеш-таблица({mode_name})")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
buckets = ht_create(1000)
|
||||
for name, phone in records:
|
||||
buckets = ht_insert(buckets, name, phone)
|
||||
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
ht_find(buckets, name)
|
||||
for name in non_exist_names:
|
||||
ht_find(buckets, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_buckets = buckets.copy()
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_buckets = ht_delete(current_buckets, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'HashTable',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def run_bst_experiments(records, mode_name):
|
||||
|
||||
print(f"\n двоичное дерево({mode_name})")
|
||||
|
||||
print("вставка 10000 записей:")
|
||||
|
||||
insert_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
root = None
|
||||
for name, phone in records:
|
||||
root = bst_insert(root, name, phone)
|
||||
|
||||
end = time.perf_counter()
|
||||
insert_times.append(end - start)
|
||||
print(f"Вставка {run+1}/5: {insert_times[-1]:.6f} сек")
|
||||
|
||||
avg_insert = sum(insert_times) / 5
|
||||
print(f"среднее: {avg_insert:.6f} сек")
|
||||
|
||||
print("поиск 110 записей:")
|
||||
|
||||
exist_names = get_random_names(records, 100)
|
||||
non_exist_names = [f"None_{i}" for i in range(10)]
|
||||
|
||||
find_times = []
|
||||
for run in range(5):
|
||||
start = time.perf_counter()
|
||||
|
||||
for name in exist_names:
|
||||
bst_find(root, name)
|
||||
for name in non_exist_names:
|
||||
bst_find(root, name)
|
||||
|
||||
end = time.perf_counter()
|
||||
find_times.append(end - start)
|
||||
print(f"поиск {run+1}/5: {find_times[-1]:.6f} сек")
|
||||
|
||||
avg_find = sum(find_times) / 5
|
||||
print(f"среднее: {avg_find:.6f} сек")
|
||||
|
||||
print("удаление 50 случайных записей:")
|
||||
|
||||
to_delete = get_random_names(records,50)
|
||||
|
||||
delete_times = []
|
||||
for run in range(5):
|
||||
current_root = root
|
||||
start = time.perf_counter()
|
||||
for name in to_delete:
|
||||
current_root = bst_delete(current_root, name)
|
||||
end = time.perf_counter()
|
||||
delete_times.append(end - start)
|
||||
print(f"удаление {run+1}/5: {delete_times[-1]:.6f} сек")
|
||||
|
||||
avg_delete = sum(delete_times) / 5
|
||||
print(f"среднее: {avg_delete:.6f} сек")
|
||||
|
||||
return{
|
||||
'structure': 'BST',
|
||||
'mode': mode_name,
|
||||
'insert_avg': avg_insert,
|
||||
'insert_all': insert_times,
|
||||
'find_avg': avg_find,
|
||||
'find_all': find_times,
|
||||
'delete_avg': avg_delete,
|
||||
'delete_all': delete_times
|
||||
}
|
||||
|
||||
def save_results_to_csv(all_results):
|
||||
|
||||
os.makedirs("docs/data", exist_ok=True)
|
||||
|
||||
with open("docs/data/results.csv", "w", encoding="utf-8") as f:
|
||||
|
||||
f.write("Структура, Режим, Операция, Замер, Время (сек)\n")
|
||||
|
||||
for res in all_results:
|
||||
struct = res['structure']
|
||||
mode = res['mode']
|
||||
|
||||
|
||||
for i, t in enumerate(res['insert_all']):
|
||||
f.write(f"{struct},{mode},вставка,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},вставка,среднее,{res['insert_avg']}\n")
|
||||
|
||||
|
||||
for i, t in enumerate(res['find_all']):
|
||||
f.write(f"{struct},{mode},поиск,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},поиск,среднее,{res['find_avg']}\n")
|
||||
|
||||
|
||||
for i, t in enumerate(res['delete_all']):
|
||||
f.write(f"{struct},{mode},удаление,{i+1},{t}\n")
|
||||
f.write(f"{struct},{mode},удаление,среднее,{res['delete_avg']}\n")
|
||||
|
||||
|
||||
def main():
|
||||
print("эксперименты по замеру производительности")
|
||||
|
||||
records_shuffled, records_sorted = generate_test_data(10000)
|
||||
|
||||
all_results = []
|
||||
|
||||
print("режим: случайный порядок")
|
||||
|
||||
all_results.append(run_linked_experiments(records_shuffled, "случайный"))
|
||||
all_results.append(run_hash_experiments(records_shuffled, "случайный"))
|
||||
all_results.append(run_bst_experiments(records_shuffled, "случайный"))
|
||||
|
||||
print("режим: отсортированный порядок")
|
||||
|
||||
all_results.append(run_linked_experiments(records_sorted, "отсортированный"))
|
||||
all_results.append(run_hash_experiments(records_sorted, "отсортированный"))
|
||||
all_results.append(run_bst_experiments(records_sorted, "отсортированный"))
|
||||
|
||||
save_results_to_csv(all_results)
|
||||
|
||||
if __name__== "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
195
victorovaas/lab1/lab1/phonebook.py
Normal file
195
victorovaas/lab1/lab1/phonebook.py
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
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['next'] is not None:
|
||||
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):
|
||||
records = []
|
||||
current = head
|
||||
|
||||
while current is not None:
|
||||
records.append((current['name'], current['phone']))
|
||||
current = current['next']
|
||||
records.sort(key=lambda x: x[0])
|
||||
return records
|
||||
|
||||
def hash_function(name, table_size):
|
||||
total = 0
|
||||
for ch in name:
|
||||
total = (total*31 + ord(ch)) % table_size
|
||||
return total
|
||||
|
||||
def ht_create(size=1000):
|
||||
return [None]*size
|
||||
|
||||
def ht_insert(buckets, name, phone):
|
||||
idx = hash_function(name, len(buckets))
|
||||
buckets[idx] = ll_insert(buckets[idx], name, phone)
|
||||
return buckets
|
||||
|
||||
def ht_find(buckets, name):
|
||||
idx = hash_function(name, len(buckets))
|
||||
return ll_find(buckets[idx], name)
|
||||
|
||||
def ht_delete(buckets, name):
|
||||
idx = hash_function(name, len(buckets))
|
||||
buckets[idx] = ll_delete(buckets[idx], name)
|
||||
return buckets
|
||||
|
||||
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
|
||||
|
||||
def bst_insert(root, name, phone):
|
||||
|
||||
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
|
||||
|
||||
if root is None:
|
||||
return new_node
|
||||
|
||||
current = root
|
||||
while True:
|
||||
if name < current['name']:
|
||||
if current['left'] is None:
|
||||
current['left'] = new_node
|
||||
break
|
||||
current = current['left']
|
||||
elif name > current['name']:
|
||||
if current['right'] is None:
|
||||
current['right'] = new_node
|
||||
break
|
||||
current = current['right']
|
||||
else:
|
||||
current['phone'] = phone
|
||||
break
|
||||
|
||||
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
|
||||
|
||||
parent = None
|
||||
current = root
|
||||
|
||||
while current is not None and current['name'] != name:
|
||||
parent = current
|
||||
if name < current['name']:
|
||||
current = current['left']
|
||||
else:
|
||||
current = current['right']
|
||||
|
||||
if current is None:
|
||||
return root
|
||||
|
||||
if current['left'] is None and current['right'] is None:
|
||||
if parent is None:
|
||||
return None
|
||||
if parent['left'] == current:
|
||||
parent['left'] = None
|
||||
else:
|
||||
parent['right'] = None
|
||||
return root
|
||||
|
||||
if current['left'] is None:
|
||||
child = current['right']
|
||||
elif current['right'] is None:
|
||||
child = current['left']
|
||||
else:
|
||||
successor_parent = current
|
||||
successor = current['right']
|
||||
while successor['left'] is not None:
|
||||
successor_parent = successor
|
||||
successor = successor['left']
|
||||
|
||||
current['name'] = successor['name']
|
||||
current['phone'] = successor['phone']
|
||||
|
||||
if successor_parent['left'] == successor:
|
||||
successor_parent['left'] = successor['right']
|
||||
else:
|
||||
successor_parent['right'] = successor['right']
|
||||
|
||||
return root
|
||||
|
||||
if parent is None:
|
||||
return child
|
||||
if parent['left'] == current:
|
||||
parent['left'] = child
|
||||
else:
|
||||
parent['right'] = child
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def bst_list_all(root):
|
||||
records = []
|
||||
|
||||
def inorder(node):
|
||||
if node is None:
|
||||
return
|
||||
inorder(node['left'])
|
||||
records.append((node['name'], node['phone']))
|
||||
inorder(node['right'])
|
||||
|
||||
inorder(root)
|
||||
return records
|
||||
123
victorovaas/lab1/make_graphs.py
Normal file
123
victorovaas/lab1/make_graphs.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
|
||||
os.makedirs('docs/data', exist_ok=True)
|
||||
|
||||
|
||||
structures = ['LinkedList', 'HashTable', 'BST']
|
||||
|
||||
random_insert = [0.0037545, 0.015088, 0.026280]
|
||||
sorted_insert = [0.0017544, 0.011369, 4.930788]
|
||||
|
||||
random_search = [0.00000962, 0.0001646, 0.0002592]
|
||||
sorted_search = [0.00000858, 0.00014016, 0.047126]
|
||||
|
||||
random_delete = [0.0000079, 0.00009824, 0.00016984]
|
||||
sorted_delete = [0.00000294, 0.00005878, 0.023013]
|
||||
|
||||
x = np.arange(len(structures))
|
||||
width = 0.35
|
||||
|
||||
#график вставка
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_insert, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_insert, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 1:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.1f} сек', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 5), textcoords="offset points", ha='center', va='bottom', fontsize=10, fontweight='bold')
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время вставки 10000 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_insert.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
# график поиск
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_search, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_search, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 0.01:
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время поиска 110 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_search.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
# график удаление
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_delete, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_delete, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 0.01:
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время удаления 50 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_delete.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
123
victorovaas/make_graphs.py
Normal file
123
victorovaas/make_graphs.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
|
||||
os.makedirs('docs/data', exist_ok=True)
|
||||
|
||||
|
||||
structures = ['LinkedList', 'HashTable', 'BST']
|
||||
|
||||
random_insert = [0.0037545, 0.015088, 0.026280]
|
||||
sorted_insert = [0.0017544, 0.011369, 4.930788]
|
||||
|
||||
random_search = [0.00000962, 0.0001646, 0.0002592]
|
||||
sorted_search = [0.00000858, 0.00014016, 0.047126]
|
||||
|
||||
random_delete = [0.0000079, 0.00009824, 0.00016984]
|
||||
sorted_delete = [0.00000294, 0.00005878, 0.023013]
|
||||
|
||||
x = np.arange(len(structures))
|
||||
width = 0.35
|
||||
|
||||
#график вставка
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_insert, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_insert, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 1:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.1f} сек', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 5), textcoords="offset points", ha='center', va='bottom', fontsize=10, fontweight='bold')
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время вставки 10000 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_insert.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
# график поиск
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_search, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_search, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 0.01:
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время поиска 110 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_search.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
# график удаление
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
bars1 = ax.bar(x - width/2, random_delete, width, label='Случайный порядок', color='#3498db')
|
||||
bars2 = ax.bar(x + width/2, sorted_delete, width, label='Отсортированный порядок', color='#e74c3c')
|
||||
|
||||
for bar in bars1:
|
||||
height = bar.get_height()
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
for bar in bars2:
|
||||
height = bar.get_height()
|
||||
if height < 0.01:
|
||||
ax.annotate(f'{height:.6f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
else:
|
||||
ax.annotate(f'{height:.4f}', xy=(bar.get_x() + bar.get_width()/2, height),
|
||||
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=9)
|
||||
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_title('Время удаления 50 записей', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x)
|
||||
ax.set_xticklabels(structures, fontsize=11)
|
||||
ax.legend(fontsize=11)
|
||||
ax.set_yscale('log')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('docs/data/graph_delete.png', dpi=150, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user