[1] lab1
This commit is contained in:
parent
d1d1c8cdda
commit
fa44cff7df
17
smirnovad/lab1/docs/LAB_REPORT.md
Normal file
17
smirnovad/lab1/docs/LAB_REPORT.md
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Лабораторная работа №1: Структуры данных
|
||||||
|
|
||||||
|
Выполнен замер производительности на выборке N=10000.
|
||||||
|
## Сводная таблица (Средние значения)
|
||||||
|
| Тип | Режим | Вставка | Поиск | Удаление |
|
||||||
|
| :--- | :--- | :--- | :--- | :--- |
|
||||||
|
| LinkedList | random | 0.00171 | 0.03253 | 0.01889 |
|
||||||
|
| HashTable | random | 0.00315 | 0.00008 | 0.00005 |
|
||||||
|
| BST | random | 0.02405 | 0.00021 | 0.00011 |
|
||||||
|
| LinkedList | sorted | 0.00139 | 0.03529 | 0.01801 |
|
||||||
|
| HashTable | sorted | 0.00289 | 0.00008 | 0.00004 |
|
||||||
|
| BST | sorted | 10.51532 | 0.08273 | 0.05241 |
|
||||||
|
|
||||||
|
## Основные выводы
|
||||||
|
1. **BST** крайне чувствителен к порядку: на отсортированных данных скорость падает из-за превращения дерева в список.
|
||||||
|
2. **HashTable** — самая стабильная структура, время операций почти не зависит от входной последовательности.
|
||||||
|
3. **LinkedList** показывает худшее время на операциях поиска из-за необходимости полного перебора.
|
||||||
BIN
smirnovad/lab1/docs/data/impact_analysis.png
Normal file
BIN
smirnovad/lab1/docs/data/impact_analysis.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
221
smirnovad/lab1/docs/data/lab1.py
Normal file
221
smirnovad/lab1/docs/data/lab1.py
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import csv
|
||||||
|
from pathlib import Path
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Увеличиваем лимит рекурсии для работы с глубокими деревьями (особенно на сортированных данных)
|
||||||
|
sys.setrecursionlimit(15000)
|
||||||
|
|
||||||
|
# Настройка путей (используем pathlib для гибкости)
|
||||||
|
ROOT_DIR = Path(r"C:\Users\andre\2026-rff_mp\smirnovad\lab1")
|
||||||
|
DOCS_DIR = ROOT_DIR / "docs"
|
||||||
|
DATA_DIR = DOCS_DIR / "data"
|
||||||
|
|
||||||
|
# Создание необходимых директорий
|
||||||
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# --- 1. СВЯЗНЫЙ СПИСОК (LinkedList) ---
|
||||||
|
def ll_insert(first_node, name, phone):
|
||||||
|
"""Добавление в начало списка (O(1))"""
|
||||||
|
return {'name': name, 'phone': phone, 'next': first_node}
|
||||||
|
|
||||||
|
def ll_find(first_node, name):
|
||||||
|
"""Линейный поиск по имени"""
|
||||||
|
item = first_node
|
||||||
|
while item:
|
||||||
|
if item['name'] == name:
|
||||||
|
return item['phone']
|
||||||
|
item = item['next']
|
||||||
|
return None
|
||||||
|
|
||||||
|
def ll_delete(first_node, name):
|
||||||
|
"""Удаление узла по имени"""
|
||||||
|
if not first_node:
|
||||||
|
return None
|
||||||
|
if first_node['name'] == name:
|
||||||
|
return first_node['next']
|
||||||
|
|
||||||
|
prev = first_node
|
||||||
|
while prev['next']:
|
||||||
|
if prev['next']['name'] == name:
|
||||||
|
prev['next'] = prev['next']['next']
|
||||||
|
return first_node
|
||||||
|
prev = prev['next']
|
||||||
|
return first_node
|
||||||
|
|
||||||
|
def ll_list_all(first_node):
|
||||||
|
"""Вывод всех записей в алфавитном порядке"""
|
||||||
|
result_list = []
|
||||||
|
item = first_node
|
||||||
|
while item:
|
||||||
|
result_list.append((item['name'], item['phone']))
|
||||||
|
item = item['next']
|
||||||
|
return sorted(result_list)
|
||||||
|
|
||||||
|
# --- 2. ХЕШ-ТАБЛИЦА (Hash Table) ---
|
||||||
|
def ht_insert(hash_table, name, phone):
|
||||||
|
slot = hash(name) % len(hash_table)
|
||||||
|
hash_table[slot] = ll_insert(hash_table[slot], name, phone)
|
||||||
|
|
||||||
|
def ht_find(hash_table, name):
|
||||||
|
slot = hash(name) % len(hash_table)
|
||||||
|
return ll_find(hash_table[slot], name)
|
||||||
|
|
||||||
|
def ht_delete(hash_table, name):
|
||||||
|
slot = hash(name) % len(hash_table)
|
||||||
|
hash_table[slot] = ll_delete(hash_table[slot], name)
|
||||||
|
|
||||||
|
def ht_list_all(hash_table):
|
||||||
|
"""Сбор данных из всех бакетов"""
|
||||||
|
total_data = []
|
||||||
|
for bucket in hash_table:
|
||||||
|
node = bucket
|
||||||
|
while node:
|
||||||
|
total_data.append((node['name'], node['phone']))
|
||||||
|
node = node['next']
|
||||||
|
return sorted(total_data)
|
||||||
|
|
||||||
|
# --- 3. ДВОИЧНОЕ ДЕРЕВО ПОИСКА (BST) ---
|
||||||
|
def bst_insert(root, name, phone):
|
||||||
|
if not root:
|
||||||
|
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):
|
||||||
|
if not root:
|
||||||
|
return None
|
||||||
|
if root['name'] == name:
|
||||||
|
return root['phone']
|
||||||
|
if name < root['name']:
|
||||||
|
return bst_find(root['left'], name)
|
||||||
|
return bst_find(root['right'], name)
|
||||||
|
|
||||||
|
def bst_delete(root, name):
|
||||||
|
"""Удаление узла в BST"""
|
||||||
|
if not root:
|
||||||
|
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 not root['left']: return root['right']
|
||||||
|
if not root['right']: return root['left']
|
||||||
|
# Поиск минимального в правом поддереве
|
||||||
|
min_node = root['right']
|
||||||
|
while min_node['left']:
|
||||||
|
min_node = min_node['left']
|
||||||
|
root['name'], root['phone'] = min_node['name'], min_node['phone']
|
||||||
|
root['right'] = bst_delete(root['right'], min_node['name'])
|
||||||
|
return root
|
||||||
|
|
||||||
|
# --- ЭКСПЕРИМЕНТАЛЬНАЯ ЧАСТЬ ---
|
||||||
|
log_entries = []
|
||||||
|
stats_summary = []
|
||||||
|
|
||||||
|
def run_test(structure_name, data_mode, dataset):
|
||||||
|
print(f"Тестирование: {structure_name} | Режим: {data_mode}")
|
||||||
|
t_ins, t_find, t_del = [], [], []
|
||||||
|
|
||||||
|
for run_idx in range(5): # 5 итераций
|
||||||
|
# Инициализация хранилища
|
||||||
|
storage = [None] * 1024 if structure_name == "HashTable" else None
|
||||||
|
|
||||||
|
# 1. Замер вставки
|
||||||
|
start = time.perf_counter()
|
||||||
|
for n, p in dataset:
|
||||||
|
if structure_name == "LinkedList": storage = ll_insert(storage, n, p)
|
||||||
|
elif structure_name == "HashTable": ht_insert(storage, n, p)
|
||||||
|
elif structure_name == "BST": storage = bst_insert(storage, n, p)
|
||||||
|
t_ins.append(time.perf_counter() - start)
|
||||||
|
|
||||||
|
# 2. Замер поиска (100 существующих + 10 отсутствующих)
|
||||||
|
test_names = [x[0] for x in random.sample(dataset, 100)] + [f"Missing_{j}" for j in range(10)]
|
||||||
|
start = time.perf_counter()
|
||||||
|
for name_to_find in test_names:
|
||||||
|
if structure_name == "LinkedList": ll_find(storage, name_to_find)
|
||||||
|
elif structure_name == "HashTable": ht_find(storage, name_to_find)
|
||||||
|
elif structure_name == "BST": bst_find(storage, name_to_find)
|
||||||
|
t_find.append(time.perf_counter() - start)
|
||||||
|
|
||||||
|
# 3. Замер удаления (50 записей)
|
||||||
|
test_dels = [x[0] for x in random.sample(dataset, 50)]
|
||||||
|
start = time.perf_counter()
|
||||||
|
for name_to_del in test_dels:
|
||||||
|
if structure_name == "LinkedList": storage = ll_delete(storage, name_to_del)
|
||||||
|
elif structure_name == "HashTable": ht_delete(storage, name_to_del)
|
||||||
|
elif structure_name == "BST": bst_delete(storage, name_to_del)
|
||||||
|
t_del.append(time.perf_counter() - start)
|
||||||
|
|
||||||
|
log_entries.append([structure_name, data_mode, f"Run_{run_idx+1}", t_ins[-1], t_find[-1], t_del[-1]])
|
||||||
|
|
||||||
|
# Считаем среднее
|
||||||
|
avg_i, avg_f, avg_d = sum(t_ins)/5, sum(t_find)/5, sum(t_del)/5
|
||||||
|
stats_summary.append({"type": structure_name, "mode": data_mode, "ins": avg_i, "find": avg_f, "del": avg_d})
|
||||||
|
|
||||||
|
# Генерация данных
|
||||||
|
N_COUNT = 10000
|
||||||
|
raw_data = [(f"User_{i:05d}", f"{random.randint(100, 999)}-{random.randint(10, 99)}") for i in range(N_COUNT)]
|
||||||
|
data_shuffled = random.sample(raw_data, len(raw_data))
|
||||||
|
data_sorted = sorted(raw_data)
|
||||||
|
|
||||||
|
# Запуск тестов
|
||||||
|
for mode_label, data_src in [("random", data_shuffled), ("sorted", data_sorted)]:
|
||||||
|
for s_kind in ["LinkedList", "HashTable", "BST"]:
|
||||||
|
run_test(s_kind, mode_label, data_src)
|
||||||
|
|
||||||
|
# Сохранение CSV
|
||||||
|
with open(DATA_DIR / "performance_stats.csv", "w", newline="", encoding="utf-8") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerow(["Structure", "Input_Mode", "Iteration", "Insert_Sec", "Find_Sec", "Delete_Sec"])
|
||||||
|
writer.writerows(log_entries)
|
||||||
|
|
||||||
|
# Построение графиков
|
||||||
|
def generate_visuals():
|
||||||
|
ops = ["Вставка", "Поиск", "Удаление"]
|
||||||
|
structs = ["LinkedList", "HashTable", "BST"]
|
||||||
|
palette = ["#3498db", "#9b59b6", "#2ecc71"] # Другие цвета
|
||||||
|
|
||||||
|
# График влияния порядка
|
||||||
|
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
|
||||||
|
fig.suptitle("Анализ влияния упорядоченности данных", fontsize=14)
|
||||||
|
|
||||||
|
for idx, s_name in enumerate(structs):
|
||||||
|
r_vals = next(s for s in stats_summary if s['type'] == s_name and s['mode'] == "random")
|
||||||
|
s_vals = next(s for s in stats_summary if s['type'] == s_name and s['mode'] == "sorted")
|
||||||
|
|
||||||
|
pos = [0, 1, 2]
|
||||||
|
axes[idx].bar([p - 0.2 for p in pos], [r_vals['ins'], r_vals['find'], r_vals['del']], 0.4, label='Random', color=palette[0])
|
||||||
|
axes[idx].bar([p + 0.2 for p in pos], [s_vals['ins'], s_vals['find'], s_vals['del']], 0.4, label='Sorted', color="#e74c3c")
|
||||||
|
axes[idx].set_title(s_name)
|
||||||
|
axes[idx].set_xticks(pos)
|
||||||
|
axes[idx].set_xticklabels(ops)
|
||||||
|
axes[idx].legend()
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(DATA_DIR / "impact_analysis.png")
|
||||||
|
|
||||||
|
generate_visuals()
|
||||||
|
|
||||||
|
# Генерация отчета
|
||||||
|
with open(DOCS_DIR / "LAB_REPORT.md", "w", encoding="utf-8") as f:
|
||||||
|
f.write("# Лабораторная работа №1: Структуры данных\n\n")
|
||||||
|
f.write(f"Выполнен замер производительности на выборке N={N_COUNT}.\n")
|
||||||
|
f.write("## Сводная таблица (Средние значения)\n")
|
||||||
|
f.write("| Тип | Режим | Вставка | Поиск | Удаление |\n| :--- | :--- | :--- | :--- | :--- |\n")
|
||||||
|
for s in stats_summary:
|
||||||
|
f.write(f"| {s['type']} | {s['mode']} | {s['ins']:.5f} | {s['find']:.5f} | {s['del']:.5f} |\n")
|
||||||
|
f.write("\n## Основные выводы\n")
|
||||||
|
f.write("1. **BST** крайне чувствителен к порядку: на отсортированных данных скорость падает из-за превращения дерева в список.\n")
|
||||||
|
f.write("2. **HashTable** — самая стабильная структура, время операций почти не зависит от входной последовательности.\n")
|
||||||
|
f.write("3. **LinkedList** показывает худшее время на операциях поиска из-за необходимости полного перебора.")
|
||||||
|
|
||||||
|
print(f"Все файлы успешно сохранены в {DOCS_DIR}")
|
||||||
31
smirnovad/lab1/docs/data/performance_stats.csv
Normal file
31
smirnovad/lab1/docs/data/performance_stats.csv
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
Structure,Input_Mode,Iteration,Insert_Sec,Find_Sec,Delete_Sec
|
||||||
|
LinkedList,random,Run_1,0.0023578000254929066,0.031007900135591626,0.01914949994534254
|
||||||
|
LinkedList,random,Run_2,0.0014043999835848808,0.035298199858516455,0.02021360001526773
|
||||||
|
LinkedList,random,Run_3,0.0016908999532461166,0.031882400158792734,0.016559200128540397
|
||||||
|
LinkedList,random,Run_4,0.0016268000472337008,0.033718999940901995,0.01765769999474287
|
||||||
|
LinkedList,random,Run_5,0.00146949989721179,0.030750300036743283,0.020888000028207898
|
||||||
|
HashTable,random,Run_1,0.003547400003299117,7.690000347793102e-05,6.220000796020031e-05
|
||||||
|
HashTable,random,Run_2,0.0028089999686926603,6.849993951618671e-05,3.900006413459778e-05
|
||||||
|
HashTable,random,Run_3,0.002920700004324317,7.139984518289566e-05,4.020007327198982e-05
|
||||||
|
HashTable,random,Run_4,0.003132300218567252,6.630015559494495e-05,3.860006108880043e-05
|
||||||
|
HashTable,random,Run_5,0.003326199948787689,0.00011060014367103577,6.16998877376318e-05
|
||||||
|
BST,random,Run_1,0.021002399967983365,0.00020360015332698822,0.00011419993825256824
|
||||||
|
BST,random,Run_2,0.020290900021791458,0.00019980012439191341,0.00010569998994469643
|
||||||
|
BST,random,Run_3,0.019706800114363432,0.00019660010002553463,0.00010689999908208847
|
||||||
|
BST,random,Run_4,0.019484999822452664,0.00018949992954730988,0.0001066999975591898
|
||||||
|
BST,random,Run_5,0.03975450014695525,0.00024339999072253704,0.00013699987903237343
|
||||||
|
LinkedList,sorted,Run_1,0.0015730001032352448,0.03809090005233884,0.01893949997611344
|
||||||
|
LinkedList,sorted,Run_2,0.001297699986025691,0.033360299887135625,0.01909619988873601
|
||||||
|
LinkedList,sorted,Run_3,0.0015416000969707966,0.03634240012615919,0.016841999953612685
|
||||||
|
LinkedList,sorted,Run_4,0.0012899001594632864,0.03580150008201599,0.0170306998770684
|
||||||
|
LinkedList,sorted,Run_5,0.0012546998914331198,0.03284729993902147,0.018117799889296293
|
||||||
|
HashTable,sorted,Run_1,0.0034030000679194927,8.430005982518196e-05,3.66999302059412e-05
|
||||||
|
HashTable,sorted,Run_2,0.002653100062161684,6.769993342459202e-05,3.760005347430706e-05
|
||||||
|
HashTable,sorted,Run_3,0.0026434999890625477,6.690016016364098e-05,3.8400059565901756e-05
|
||||||
|
HashTable,sorted,Run_4,0.002997299889102578,7.299985736608505e-05,4.179985262453556e-05
|
||||||
|
HashTable,sorted,Run_5,0.002777900081127882,8.819997310638428e-05,3.800005652010441e-05
|
||||||
|
BST,sorted,Run_1,9.951400500023738,0.07922379998490214,0.0600940000731498
|
||||||
|
BST,sorted,Run_2,10.377625699853525,0.08713930007070303,0.05045670014806092
|
||||||
|
BST,sorted,Run_3,12.112230099970475,0.08630810002796352,0.050702399807050824
|
||||||
|
BST,sorted,Run_4,10.117846999783069,0.0832209000363946,0.05910569988191128
|
||||||
|
BST,sorted,Run_5,10.017497000051662,0.07774659991264343,0.041689899982884526
|
||||||
|
|
|
@ -1,249 +0,0 @@
|
||||||
import time
|
|
||||||
import random
|
|
||||||
import csv
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
sys.setrecursionlimit(20000)
|
|
||||||
|
|
||||||
BASE_PATH = r"C:\Users\andre\2026-rff_mp\smirnovad\lab1"
|
|
||||||
DOCS_PATH = os.path.join(BASE_PATH, "docs")
|
|
||||||
DATA_PATH = os.path.join(DOCS_PATH, "data")
|
|
||||||
|
|
||||||
for p in [DOCS_PATH, DATA_PATH]:
|
|
||||||
if not os.path.exists(p):
|
|
||||||
os.makedirs(p)
|
|
||||||
|
|
||||||
def ll_insert(head, name, phone):
|
|
||||||
return {'name': name, 'phone': phone, 'next': head}
|
|
||||||
|
|
||||||
def ll_find(head, name):
|
|
||||||
curr = head
|
|
||||||
while curr:
|
|
||||||
if curr['name'] == name: return curr['phone']
|
|
||||||
curr = curr['next']
|
|
||||||
return None
|
|
||||||
|
|
||||||
def ll_delete(head, name):
|
|
||||||
if not head: return None
|
|
||||||
if head['name'] == name: return head['next']
|
|
||||||
curr = head
|
|
||||||
while curr['next']:
|
|
||||||
if curr['next']['name'] == name:
|
|
||||||
curr['next'] = curr['next']['next']
|
|
||||||
return head
|
|
||||||
curr = curr['next']
|
|
||||||
return head
|
|
||||||
|
|
||||||
def ll_list_all(head):
|
|
||||||
res = []
|
|
||||||
curr = head
|
|
||||||
while curr:
|
|
||||||
res.append((curr['name'], curr['phone']))
|
|
||||||
curr = curr['next']
|
|
||||||
return sorted(res)
|
|
||||||
|
|
||||||
def ht_insert(buckets, name, phone):
|
|
||||||
idx = hash(name) % len(buckets)
|
|
||||||
buckets[idx] = ll_insert(buckets[idx], name, phone)
|
|
||||||
|
|
||||||
def ht_find(buckets, name):
|
|
||||||
idx = hash(name) % len(buckets)
|
|
||||||
return ll_find(buckets[idx], name)
|
|
||||||
|
|
||||||
def ht_delete(buckets, name):
|
|
||||||
idx = hash(name) % len(buckets)
|
|
||||||
buckets[idx] = ll_delete(buckets[idx], name)
|
|
||||||
|
|
||||||
def ht_list_all(buckets):
|
|
||||||
all_recs = []
|
|
||||||
for b in buckets:
|
|
||||||
curr = b
|
|
||||||
while curr:
|
|
||||||
all_recs.append((curr['name'], curr['phone']))
|
|
||||||
curr = curr['next']
|
|
||||||
return sorted(all_recs)
|
|
||||||
|
|
||||||
def bst_insert(root, name, phone):
|
|
||||||
if not root:
|
|
||||||
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):
|
|
||||||
if not root: return None
|
|
||||||
if root['name'] == name: return root['phone']
|
|
||||||
if name < root['name']: return bst_find(root['left'], name)
|
|
||||||
return bst_find(root['right'], name)
|
|
||||||
|
|
||||||
def bst_delete(root, name):
|
|
||||||
if not root: 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 not root['left']: return root['right']
|
|
||||||
if not root['right']: return root['left']
|
|
||||||
temp = root['right']
|
|
||||||
while temp['left']: temp = temp['left']
|
|
||||||
root['name'], root['phone'] = temp['name'], temp['phone']
|
|
||||||
root['right'] = bst_delete(root['right'], temp['name'])
|
|
||||||
return root
|
|
||||||
|
|
||||||
def bst_list_all(root):
|
|
||||||
res = []
|
|
||||||
def _inorder(node):
|
|
||||||
if node:
|
|
||||||
_inorder(node['left'])
|
|
||||||
res.append((node['name'], node['phone']))
|
|
||||||
_inorder(node['right'])
|
|
||||||
_inorder(root)
|
|
||||||
return res
|
|
||||||
|
|
||||||
all_results_csv = []
|
|
||||||
summary_for_report = []
|
|
||||||
|
|
||||||
def run_experiment(struct_type, mode, data):
|
|
||||||
print(f"Processing: {struct_type} ({mode})")
|
|
||||||
ins_times, find_times, del_times = [], [], []
|
|
||||||
|
|
||||||
for i in range(5):
|
|
||||||
container = [None]*1000 if struct_type == "HashTable" else None
|
|
||||||
|
|
||||||
start = time.perf_counter()
|
|
||||||
for n, p in data:
|
|
||||||
if struct_type == "LinkedList": container = ll_insert(container, n, p)
|
|
||||||
elif struct_type == "HashTable": ht_insert(container, n, p)
|
|
||||||
elif struct_type == "BST": container = bst_insert(container, n, p)
|
|
||||||
ins_times.append(time.perf_counter() - start)
|
|
||||||
|
|
||||||
search_list = [d[0] for d in random.sample(data, 100)] + [f"None_{j}" for j in range(10)]
|
|
||||||
start = time.perf_counter()
|
|
||||||
for s_name in search_list:
|
|
||||||
if struct_type == "LinkedList": ll_find(container, s_name)
|
|
||||||
elif struct_type == "HashTable": ht_find(container, s_name)
|
|
||||||
elif struct_type == "BST": bst_find(container, s_name)
|
|
||||||
find_times.append(time.perf_counter() - start)
|
|
||||||
|
|
||||||
del_list = [d[0] for d in random.sample(data, 50)]
|
|
||||||
start = time.perf_counter()
|
|
||||||
for d_name in del_list:
|
|
||||||
if struct_type == "LinkedList": container = ll_delete(container, d_name)
|
|
||||||
elif struct_type == "HashTable": ht_delete(container, d_name)
|
|
||||||
elif struct_type == "BST": container = bst_delete(container, d_name)
|
|
||||||
del_times.append(time.perf_counter() - start)
|
|
||||||
|
|
||||||
all_results_csv.append([struct_type, mode, f"Run {i+1}", ins_times[-1], find_times[-1], del_times[-1]])
|
|
||||||
|
|
||||||
avg_ins = sum(ins_times) / 5
|
|
||||||
avg_find = sum(find_times) / 5
|
|
||||||
avg_del = sum(del_times) / 5
|
|
||||||
|
|
||||||
all_results_csv.append([struct_type, mode, "AVERAGE", avg_ins, avg_find, avg_del])
|
|
||||||
summary_for_report.append({"name": struct_type, "mode": mode, "ins": avg_ins, "find": avg_find, "del": avg_del})
|
|
||||||
|
|
||||||
N = 10000
|
|
||||||
records_raw = [(f"User_{i:05d}", f"8-900-{random.randint(100, 999)}") for i in range(N)]
|
|
||||||
records_shuffled = records_raw[:]
|
|
||||||
random.shuffle(records_shuffled)
|
|
||||||
records_sorted = sorted(records_raw)
|
|
||||||
|
|
||||||
for m_name, d_set in [("случайный", records_shuffled), ("сортированный", records_sorted)]:
|
|
||||||
for s_type in ["LinkedList", "HashTable", "BST"]:
|
|
||||||
run_experiment(s_type, m_name, d_set)
|
|
||||||
|
|
||||||
with open(os.path.join(DATA_PATH, "results.csv"), "w", newline="", encoding="utf-8") as f:
|
|
||||||
writer = csv.writer(f)
|
|
||||||
writer.writerow(["Структура", "Режим", "Итерация", "Вставка", "Поиск", "Удаление"])
|
|
||||||
writer.writerows(all_results_csv)
|
|
||||||
|
|
||||||
def create_plots():
|
|
||||||
labels = ["insert", "find", "delete"]
|
|
||||||
structs = ["LinkedList", "HashTable", "BST"]
|
|
||||||
colors = ['#5dade2', '#e67e22', '#58d68d']
|
|
||||||
|
|
||||||
fig1, axs = plt.subplots(1, 3, figsize=(18, 6))
|
|
||||||
fig1.suptitle("Влияние порядка данных на время операций", fontsize=16, fontweight='bold')
|
|
||||||
|
|
||||||
for i, s_name in enumerate(structs):
|
|
||||||
rand_data = next(r for r in summary_for_report if r['name'] == s_name and r['mode'] == "случайный")
|
|
||||||
sort_data = next(r for r in summary_for_report if r['name'] == s_name and r['mode'] == "сортированный")
|
|
||||||
|
|
||||||
x = [0, 1, 2]
|
|
||||||
width = 0.35
|
|
||||||
axs[i].bar([p - width/2 for p in x], [rand_data['ins'], rand_data['find'], rand_data['del']], width, label='случайный', color=colors[0])
|
|
||||||
axs[i].bar([p + width/2 for p in x], [sort_data['ins'], sort_data['find'], sort_data['del']], width, label='сортированный', color='#e74c3c', alpha=0.8)
|
|
||||||
|
|
||||||
axs[i].set_title(s_name, fontweight='bold')
|
|
||||||
axs[i].set_xticks(x)
|
|
||||||
axs[i].set_xticklabels(labels)
|
|
||||||
axs[i].set_ylabel("Время (с)")
|
|
||||||
axs[i].legend()
|
|
||||||
axs[i].grid(axis='y', linestyle='--', alpha=0.3)
|
|
||||||
|
|
||||||
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
|
|
||||||
plt.savefig(os.path.join(DATA_PATH, "order_impact.png"))
|
|
||||||
|
|
||||||
fig2, axs2 = plt.subplots(1, 3, figsize=(18, 6))
|
|
||||||
fig2.suptitle(f"Сравнение структур данных (N={N})", fontsize=16, fontweight='bold')
|
|
||||||
|
|
||||||
op_keys = ['ins', 'find', 'del']
|
|
||||||
op_names = ['insert', 'find', 'delete']
|
|
||||||
|
|
||||||
for i, op in enumerate(op_keys):
|
|
||||||
plot_labels = []
|
|
||||||
plot_values = []
|
|
||||||
plot_colors = []
|
|
||||||
|
|
||||||
for r in summary_for_report:
|
|
||||||
plot_labels.append(f"{r['name']}\n({r['mode'][:4]})")
|
|
||||||
plot_values.append(r[op])
|
|
||||||
if r['name'] == "LinkedList": plot_colors.append(colors[0])
|
|
||||||
elif r['name'] == "HashTable": plot_colors.append(colors[1])
|
|
||||||
else: plot_colors.append(colors[2])
|
|
||||||
|
|
||||||
bars = axs2[i].bar(plot_labels, plot_values, color=plot_colors)
|
|
||||||
axs2[i].set_title(f"Операция: {op_names[i]}", fontweight='bold')
|
|
||||||
axs2[i].set_ylabel("Время (с)")
|
|
||||||
axs2[i].tick_params(axis='x', rotation=15)
|
|
||||||
|
|
||||||
for bar in bars:
|
|
||||||
height = bar.get_height()
|
|
||||||
axs2[i].text(bar.get_x() + bar.get_width()/2., height, f'{height:.4f}', ha='center', va='bottom', fontsize=8)
|
|
||||||
|
|
||||||
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
|
|
||||||
plt.savefig(os.path.join(DATA_PATH, "struct_comparison.png"))
|
|
||||||
|
|
||||||
create_plots()
|
|
||||||
|
|
||||||
with open(os.path.join(DOCS_PATH, "report.md"), "w", encoding="utf-8") as f:
|
|
||||||
f.write("# Технический отчет: Сравнительный анализ структур данных\n\n")
|
|
||||||
f.write("## 1. Вводные данные\n")
|
|
||||||
f.write(f"Целью теста является оценка производительности LinkedList, HashTable и BST на массиве из {N} элементов. ")
|
|
||||||
f.write("Анализировались сценарии со случайным распределением и предварительной сортировкой ключей.\n\n")
|
|
||||||
|
|
||||||
f.write("## 2. Результаты измерений (AVG)\n")
|
|
||||||
f.write("| Алгоритм | Входные данные | Вставка (с) | Поиск (с) | Удаление (с) |\n")
|
|
||||||
f.write("| :--- | :--- | :--- | :--- | :--- |\n")
|
|
||||||
for r in summary_for_report:
|
|
||||||
f.write(f"| {r['name']} | {r['mode']} | {r['ins']:.6f} | {r['find']:.6f} | {r['del']:.6f} |\n")
|
|
||||||
|
|
||||||
f.write("\n## 3. Визуальный анализ\n")
|
|
||||||
f.write("### Сравнение по типам операций\n\n\n")
|
|
||||||
f.write("### Влияние упорядоченности на производительность\n\n\n")
|
|
||||||
|
|
||||||
f.write("## 4. Экспертные выводы\n")
|
|
||||||
f.write("- **Эффект вырождения BST:** На отсортированных последовательностях BST демонстрирует критический рост времени выполнения (деградация до $O(N)$). ")
|
|
||||||
f.write("Это связано с отсутствием балансировки, превращающим дерево в линейный список.\n")
|
|
||||||
f.write("- **Инвариантность HashTable:** Хеш-таблица показывает наиболее стабильные результаты. Скорость доступа не коррелирует с порядком входных данных.\n")
|
|
||||||
f.write("- **Линейная сложность LinkedList:** Связный список предсказуемо неэффективен при поиске, так как требует итерации по всей глубине структуры.\n")
|
|
||||||
f.write("- **Итоговая оценка:** Для систем с высокой интенсивностью поиска и вставки оптимальным выбором является HashTable.")
|
|
||||||
|
|
||||||
print("Готово.")
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB |
|
|
@ -1,37 +0,0 @@
|
||||||
Структура,Режим,Итерация,Вставка,Поиск,Удаление
|
|
||||||
LinkedList,случайный,Run 1,0.0019714999943971634,0.029020800022408366,0.017652600072324276
|
|
||||||
LinkedList,случайный,Run 2,0.0012699998915195465,0.03224149998277426,0.019751599989831448
|
|
||||||
LinkedList,случайный,Run 3,0.0015686999540776014,0.032726099947467446,0.023450100095942616
|
|
||||||
LinkedList,случайный,Run 4,0.0012663998641073704,0.031491999980062246,0.02140070009045303
|
|
||||||
LinkedList,случайный,Run 5,0.001316299894824624,0.03115670010447502,0.01875569997355342
|
|
||||||
LinkedList,случайный,AVERAGE,0.0014785799197852612,0.03132742000743747,0.020202140044420957
|
|
||||||
HashTable,случайный,Run 1,0.0036369001027196646,7.810001261532307e-05,4.20999713242054e-05
|
|
||||||
HashTable,случайный,Run 2,0.0027166998479515314,6.680004298686981e-05,3.690016455948353e-05
|
|
||||||
HashTable,случайный,Run 3,0.002825999865308404,6.940006278455257e-05,4.0599843487143517e-05
|
|
||||||
HashTable,случайный,Run 4,0.0038201999850571156,9.25001222640276e-05,4.6800123527646065e-05
|
|
||||||
HashTable,случайный,Run 5,0.0028741999994963408,7.349997758865356e-05,4.6900007873773575e-05
|
|
||||||
HashTable,случайный,AVERAGE,0.0031747999601066113,7.606004364788533e-05,4.2660022154450415e-05
|
|
||||||
BST,случайный,Run 1,0.02340110018849373,0.000219699926674366,0.00012899981811642647
|
|
||||||
BST,случайный,Run 2,0.02266499982215464,0.000223799841478467,0.00011649983935058117
|
|
||||||
BST,случайный,Run 3,0.02390270004980266,0.0002661000471562147,0.00011879997327923775
|
|
||||||
BST,случайный,Run 4,0.02376510016620159,0.00023330003023147583,0.00013200007379055023
|
|
||||||
BST,случайный,Run 5,0.039811399998143315,0.00020990008488297462,0.00011849985457956791
|
|
||||||
BST,случайный,AVERAGE,0.02670906004495919,0.00023055998608469963,0.00012295991182327272
|
|
||||||
LinkedList,сортированный,Run 1,0.001537499949336052,0.033511900110170245,0.019870299845933914
|
|
||||||
LinkedList,сортированный,Run 2,0.001307999948039651,0.0319586000405252,0.014161000028252602
|
|
||||||
LinkedList,сортированный,Run 3,0.0014808999840170145,0.0326090999878943,0.018076500156894326
|
|
||||||
LinkedList,сортированный,Run 4,0.0013822999317198992,0.033412199933081865,0.022219100035727024
|
|
||||||
LinkedList,сортированный,Run 5,0.0012627998366951942,0.030183800030499697,0.016802300000563264
|
|
||||||
LinkedList,сортированный,AVERAGE,0.001394299929961562,0.03233512002043426,0.018225840013474225
|
|
||||||
HashTable,сортированный,Run 1,0.0034910000395029783,8.500018157064915e-05,4.449998959898949e-05
|
|
||||||
HashTable,сортированный,Run 2,0.002676100004464388,6.62999227643013e-05,3.50000336766243e-05
|
|
||||||
HashTable,сортированный,Run 3,0.0026501999236643314,6.309989839792252e-05,3.5800039768218994e-05
|
|
||||||
HashTable,сортированный,Run 4,0.003009800100699067,6.360001862049103e-05,3.800005652010441e-05
|
|
||||||
HashTable,сортированный,Run 5,0.002735199872404337,8.360017091035843e-05,3.979983739554882e-05
|
|
||||||
HashTable,сортированный,AVERAGE,0.0029124599881470204,7.232003845274449e-05,3.86199913918972e-05
|
|
||||||
BST,сортированный,Run 1,10.096050000051036,0.08805329981260002,0.06355300010181963
|
|
||||||
BST,сортированный,Run 2,10.27557749999687,0.0852949998807162,0.04538960009813309
|
|
||||||
BST,сортированный,Run 3,9.790069000096992,0.0812387999612838,0.0491947999689728
|
|
||||||
BST,сортированный,Run 4,9.99310930003412,0.08915449981577694,0.04604210006073117
|
|
||||||
BST,сортированный,Run 5,9.97979940008372,0.07943259994499385,0.049180999863892794
|
|
||||||
BST,сортированный,AVERAGE,10.026921040052548,0.08463483988307416,0.0506721000187099
|
|
||||||
|
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
|
|
@ -1,27 +0,0 @@
|
||||||
# Технический отчет: Сравнительный анализ структур данных
|
|
||||||
|
|
||||||
## 1. Вводные данные
|
|
||||||
Целью теста является оценка производительности LinkedList, HashTable и BST на массиве из 10000 элементов. Анализировались сценарии со случайным распределением и предварительной сортировкой ключей.
|
|
||||||
|
|
||||||
## 2. Результаты измерений (AVG)
|
|
||||||
| Алгоритм | Входные данные | Вставка (с) | Поиск (с) | Удаление (с) |
|
|
||||||
| :--- | :--- | :--- | :--- | :--- |
|
|
||||||
| LinkedList | случайный | 0.001479 | 0.031327 | 0.020202 |
|
|
||||||
| HashTable | случайный | 0.003175 | 0.000076 | 0.000043 |
|
|
||||||
| BST | случайный | 0.026709 | 0.000231 | 0.000123 |
|
|
||||||
| LinkedList | сортированный | 0.001394 | 0.032335 | 0.018226 |
|
|
||||||
| HashTable | сортированный | 0.002912 | 0.000072 | 0.000039 |
|
|
||||||
| BST | сортированный | 10.026921 | 0.084635 | 0.050672 |
|
|
||||||
|
|
||||||
## 3. Визуальный анализ
|
|
||||||
### Сравнение по типам операций
|
|
||||||

|
|
||||||
|
|
||||||
### Влияние упорядоченности на производительность
|
|
||||||

|
|
||||||
|
|
||||||
## 4. Экспертные выводы
|
|
||||||
- **Эффект вырождения BST:** На отсортированных последовательностях BST демонстрирует критический рост времени выполнения (деградация до $O(N)$). Это связано с отсутствием балансировки, превращающим дерево в линейный список.
|
|
||||||
- **Инвариантность HashTable:** Хеш-таблица показывает наиболее стабильные результаты. Скорость доступа не коррелирует с порядком входных данных.
|
|
||||||
- **Линейная сложность LinkedList:** Связный список предсказуемо неэффективен при поиске, так как требует итерации по всей глубине структуры.
|
|
||||||
- **Итоговая оценка:** Для систем с высокой интенсивностью поиска и вставки оптимальным выбором является HashTable.
|
|
||||||
Loading…
Reference in New Issue
Block a user