forked from UNN/2026-rff_mp
Merge pull request '[1]task1' (#384) from ivankinad/2026-rff_mp:task1 into develop
Reviewed-on: UNN/2026-rff_mp#384
This commit is contained in:
commit
714ea579dd
0
ivankinad/427.md.txt
Normal file
0
ivankinad/427.md.txt
Normal file
109
ivankinad/task1/benchmark_output.csv
Normal file
109
ivankinad/task1/benchmark_output.csv
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
Structure,Mode,Operation,Time (sec)
|
||||
LinkedList,random,insert (trial 1),3.415125
|
||||
LinkedList,random,find (trial 1),0.035397
|
||||
LinkedList,random,delete (trial 1),0.017692
|
||||
LinkedList,random,insert (trial 2),3.534952
|
||||
LinkedList,random,find (trial 2),0.038819
|
||||
LinkedList,random,delete (trial 2),0.021675
|
||||
LinkedList,random,insert (trial 3),3.493529
|
||||
LinkedList,random,find (trial 3),0.036592
|
||||
LinkedList,random,delete (trial 3),0.018221
|
||||
LinkedList,random,insert (trial 4),3.340745
|
||||
LinkedList,random,find (trial 4),0.036098
|
||||
LinkedList,random,delete (trial 4),0.017576
|
||||
LinkedList,random,insert (trial 5),3.369741
|
||||
LinkedList,random,find (trial 5),0.035302
|
||||
LinkedList,random,delete (trial 5),0.017674
|
||||
LinkedList,random,Insert (avg),3.430818
|
||||
LinkedList,random,Find (avg),0.036442
|
||||
LinkedList,random,Delete (avg),0.018568
|
||||
LinkedList,sorted,insert (trial 1),3.118200
|
||||
LinkedList,sorted,find (trial 1),0.037116
|
||||
LinkedList,sorted,delete (trial 1),0.019387
|
||||
LinkedList,sorted,insert (trial 2),3.338498
|
||||
LinkedList,sorted,find (trial 2),0.036500
|
||||
LinkedList,sorted,delete (trial 2),0.019001
|
||||
LinkedList,sorted,insert (trial 3),3.136906
|
||||
LinkedList,sorted,find (trial 3),0.037498
|
||||
LinkedList,sorted,delete (trial 3),0.019219
|
||||
LinkedList,sorted,insert (trial 4),3.253578
|
||||
LinkedList,sorted,find (trial 4),0.036434
|
||||
LinkedList,sorted,delete (trial 4),0.019333
|
||||
LinkedList,sorted,insert (trial 5),3.207896
|
||||
LinkedList,sorted,find (trial 5),0.038585
|
||||
LinkedList,sorted,delete (trial 5),0.020473
|
||||
LinkedList,sorted,Insert (avg),3.211016
|
||||
LinkedList,sorted,Find (avg),0.037227
|
||||
LinkedList,sorted,Delete (avg),0.019482
|
||||
HashTable,random,insert (trial 1),0.010124
|
||||
HashTable,random,find (trial 1),0.000093
|
||||
HashTable,random,delete (trial 1),0.000052
|
||||
HashTable,random,insert (trial 2),0.010397
|
||||
HashTable,random,find (trial 2),0.000086
|
||||
HashTable,random,delete (trial 2),0.000048
|
||||
HashTable,random,insert (trial 3),0.009352
|
||||
HashTable,random,find (trial 3),0.000081
|
||||
HashTable,random,delete (trial 3),0.000046
|
||||
HashTable,random,insert (trial 4),0.009326
|
||||
HashTable,random,find (trial 4),0.000080
|
||||
HashTable,random,delete (trial 4),0.000046
|
||||
HashTable,random,insert (trial 5),0.010205
|
||||
HashTable,random,find (trial 5),0.000081
|
||||
HashTable,random,delete (trial 5),0.000046
|
||||
HashTable,random,Insert (avg),0.009881
|
||||
HashTable,random,Find (avg),0.000084
|
||||
HashTable,random,Delete (avg),0.000048
|
||||
HashTable,sorted,insert (trial 1),0.008975
|
||||
HashTable,sorted,find (trial 1),0.000087
|
||||
HashTable,sorted,delete (trial 1),0.000050
|
||||
HashTable,sorted,insert (trial 2),0.009137
|
||||
HashTable,sorted,find (trial 2),0.000086
|
||||
HashTable,sorted,delete (trial 2),0.000050
|
||||
HashTable,sorted,insert (trial 3),0.009460
|
||||
HashTable,sorted,find (trial 3),0.000086
|
||||
HashTable,sorted,delete (trial 3),0.000049
|
||||
HashTable,sorted,insert (trial 4),0.008977
|
||||
HashTable,sorted,find (trial 4),0.000085
|
||||
HashTable,sorted,delete (trial 4),0.000049
|
||||
HashTable,sorted,insert (trial 5),0.009416
|
||||
HashTable,sorted,find (trial 5),0.000094
|
||||
HashTable,sorted,delete (trial 5),0.000053
|
||||
HashTable,sorted,Insert (avg),0.009193
|
||||
HashTable,sorted,Find (avg),0.000087
|
||||
HashTable,sorted,Delete (avg),0.000050
|
||||
BST,random,insert (trial 1),0.031499
|
||||
BST,random,find (trial 1),0.000274
|
||||
BST,random,delete (trial 1),0.000153
|
||||
BST,random,insert (trial 2),0.031806
|
||||
BST,random,find (trial 2),0.000286
|
||||
BST,random,delete (trial 2),0.000157
|
||||
BST,random,insert (trial 3),0.031514
|
||||
BST,random,find (trial 3),0.000267
|
||||
BST,random,delete (trial 3),0.000150
|
||||
BST,random,insert (trial 4),0.031758
|
||||
BST,random,find (trial 4),0.000260
|
||||
BST,random,delete (trial 4),0.000144
|
||||
BST,random,insert (trial 5),0.031923
|
||||
BST,random,find (trial 5),0.000262
|
||||
BST,random,delete (trial 5),0.000145
|
||||
BST,random,Insert (avg),0.031700
|
||||
BST,random,Find (avg),0.000270
|
||||
BST,random,Delete (avg),0.000150
|
||||
BST,sorted,insert (trial 1),14.135551
|
||||
BST,sorted,find (trial 1),0.123919
|
||||
BST,sorted,delete (trial 1),0.054104
|
||||
BST,sorted,insert (trial 2),13.781539
|
||||
BST,sorted,find (trial 2),0.126835
|
||||
BST,sorted,delete (trial 2),0.055120
|
||||
BST,sorted,insert (trial 3),15.010563
|
||||
BST,sorted,find (trial 3),0.117841
|
||||
BST,sorted,delete (trial 3),0.056871
|
||||
BST,sorted,insert (trial 4),14.378650
|
||||
BST,sorted,find (trial 4),0.113316
|
||||
BST,sorted,delete (trial 4),0.054129
|
||||
BST,sorted,insert (trial 5),14.285981
|
||||
BST,sorted,find (trial 5),0.112919
|
||||
BST,sorted,delete (trial 5),0.057369
|
||||
BST,sorted,Insert (avg),14.318457
|
||||
BST,sorted,Find (avg),0.118966
|
||||
BST,sorted,Delete (avg),0.055519
|
||||
|
312
ivankinad/task1/laba1.py
Normal file
312
ivankinad/task1/laba1.py
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
import csv
|
||||
import random
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
sys.setrecursionlimit(30000)
|
||||
threading.stack_size(64*1024*1024)
|
||||
|
||||
|
||||
def ll_insert(head, name, phone):
|
||||
"""Добавляет запись или обновляет телефон, если имя уже существует. Возвращает новую голову списка."""
|
||||
current = head
|
||||
while current is not None:
|
||||
if current["name"] == name:
|
||||
current["phone"] = phone
|
||||
return head
|
||||
current = current["next"]
|
||||
|
||||
fresh_node = {"name": name, "phone": phone, "next": head}
|
||||
return fresh_node
|
||||
|
||||
def ll_find(head, name):
|
||||
"""Ищет узел по имени. Возвращает телефон или None."""
|
||||
current = head
|
||||
while current is not None:
|
||||
if current["name"] == name:
|
||||
return current["phone"]
|
||||
current = current["next"]
|
||||
return None
|
||||
|
||||
def ll_delete(head, name):
|
||||
"""Удаляет узел по имени. Возвращает новую голову списка."""
|
||||
current = head
|
||||
previous = None
|
||||
|
||||
while current is not None:
|
||||
if current["name"] == name:
|
||||
if previous is None:
|
||||
return current["next"]
|
||||
else:
|
||||
previous["next"] = current["next"]
|
||||
return head
|
||||
previous = current
|
||||
current = current["next"]
|
||||
|
||||
return head
|
||||
|
||||
def ll_list_all(head):
|
||||
"""Собирает все записи в список и сортирует их по имени."""
|
||||
entries = []
|
||||
current = head
|
||||
while current is not None:
|
||||
entries.append((current["name"], current["phone"]))
|
||||
current = current["next"]
|
||||
entries.sort(key=lambda item: item[0])
|
||||
return entries
|
||||
|
||||
|
||||
def ht_create(size=1000):
|
||||
"""Создает пустую хеш-таблицу заданного размера."""
|
||||
return [None] * size
|
||||
|
||||
def ht_insert(buckets, name, phone):
|
||||
"""Вычисляет индекс бакета и вызывает ll_insert."""
|
||||
bucket_idx = abs(hash(name)) % len(buckets)
|
||||
buckets[bucket_idx] = ll_insert(buckets[bucket_idx], name, phone)
|
||||
|
||||
def ht_find(buckets, name):
|
||||
"""Вычисляет индекс бакета и вызывает ll_find."""
|
||||
bucket_idx = abs(hash(name)) % len(buckets)
|
||||
return ll_find(buckets[bucket_idx], name)
|
||||
|
||||
def ht_delete(buckets, name):
|
||||
"""Вычисляет индекс бакета и вызывает ll_delete."""
|
||||
bucket_idx = abs(hash(name)) % len(buckets)
|
||||
buckets[bucket_idx] = ll_delete(buckets[bucket_idx], name)
|
||||
|
||||
def ht_list_all(buckets):
|
||||
"""Собирает записи из всех бакетов и сортирует их по имени."""
|
||||
entries = []
|
||||
for head_node in buckets:
|
||||
current = head_node
|
||||
while current is not None:
|
||||
entries.append((current["name"], current["phone"]))
|
||||
current = current["next"]
|
||||
entries.sort(key=lambda item: item[0])
|
||||
return entries
|
||||
|
||||
|
||||
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_delete(root, name):
|
||||
"""Рекурсивное удаление узла из BST."""
|
||||
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"]
|
||||
if root["right"] is None:
|
||||
return root["left"]
|
||||
|
||||
successor = root["right"]
|
||||
while successor["left"] is not None:
|
||||
successor = successor["left"]
|
||||
|
||||
root["name"] = successor["name"]
|
||||
root["phone"] = successor["phone"]
|
||||
root["right"] = bst_delete(root["right"], successor["name"])
|
||||
|
||||
return root
|
||||
|
||||
def bst_list_all(root):
|
||||
"""Центрированный обход дерева для сбора записей."""
|
||||
entries = []
|
||||
def _inorder(node):
|
||||
if node is not None:
|
||||
_inorder(node["left"])
|
||||
entries.append((node["name"], node["phone"]))
|
||||
_inorder(node["right"])
|
||||
|
||||
_inorder(root)
|
||||
return entries
|
||||
|
||||
|
||||
def perform_benchmark():
|
||||
total_records = 10000
|
||||
random.seed(42)
|
||||
|
||||
ordered_records = [(f"User_{i:05d}", f"8-999-123-{i:04d}") for i in range(total_records)]
|
||||
shuffled_records = ordered_records.copy()
|
||||
random.shuffle(shuffled_records)
|
||||
|
||||
existing_searches = [random.choice(ordered_records)[0] for _ in range(100)]
|
||||
non_existing_searches = [f"None_{i}" for i in range(10)]
|
||||
search_queries = existing_searches + non_existing_searches
|
||||
deletion_targets = [random.choice(ordered_records)[0] for _ in range(50)]
|
||||
|
||||
output_rows = [["Structure", "Mode", "Operation", "Time (sec)"]]
|
||||
graph_entries = []
|
||||
|
||||
def execute_trial(structure_kind, data_mode, data_collection):
|
||||
print(f"Starting: {structure_kind} | Mode: {data_mode}...")
|
||||
insertion_measurements, search_measurements, deletion_measurements = [], [], []
|
||||
|
||||
for trial_num in range(1, 6):
|
||||
if structure_kind == "LinkedList": container = None
|
||||
elif structure_kind == "HashTable": container = ht_create(size=1000)
|
||||
elif structure_kind == "BST": container = None
|
||||
|
||||
# А. Вставка
|
||||
timer_start = time.perf_counter()
|
||||
if structure_kind == "LinkedList":
|
||||
for name, phone in data_collection: container = ll_insert(container, name, phone)
|
||||
elif structure_kind == "HashTable":
|
||||
for name, phone in data_collection: ht_insert(container, name, phone)
|
||||
elif structure_kind == "BST":
|
||||
for name, phone in data_collection: container = bst_insert(container, name, phone)
|
||||
insert_elapsed = time.perf_counter() - timer_start
|
||||
insertion_measurements.append(insert_elapsed)
|
||||
output_rows.append([structure_kind, data_mode, f"insert (trial {trial_num})", f"{insert_elapsed:.6f}"])
|
||||
|
||||
# Б. Поиск
|
||||
timer_start = time.perf_counter()
|
||||
if structure_kind == "LinkedList":
|
||||
for name in search_queries: ll_find(container, name)
|
||||
elif structure_kind == "HashTable":
|
||||
for name in search_queries: ht_find(container, name)
|
||||
elif structure_kind == "BST":
|
||||
for name in search_queries: bst_find(container, name)
|
||||
search_elapsed = time.perf_counter() - timer_start
|
||||
search_measurements.append(search_elapsed)
|
||||
output_rows.append([structure_kind, data_mode, f"find (trial {trial_num})", f"{search_elapsed:.6f}"])
|
||||
|
||||
# В. Удаление
|
||||
timer_start = time.perf_counter()
|
||||
if structure_kind == "LinkedList":
|
||||
for name in deletion_targets: container = ll_delete(container, name)
|
||||
elif structure_kind == "HashTable":
|
||||
for name in deletion_targets: ht_delete(container, name)
|
||||
elif structure_kind == "BST":
|
||||
for name in deletion_targets: container = bst_delete(container, name)
|
||||
delete_elapsed = time.perf_counter() - timer_start
|
||||
deletion_measurements.append(delete_elapsed)
|
||||
output_rows.append([structure_kind, data_mode, f"delete (trial {trial_num})", f"{delete_elapsed:.6f}"])
|
||||
|
||||
# Запись средних значений
|
||||
output_rows.append([structure_kind, data_mode, "Insert (avg)", f"{sum(insertion_measurements)/5:.6f}"])
|
||||
output_rows.append([structure_kind, data_mode, "Find (avg)", f"{sum(search_measurements)/5:.6f}"])
|
||||
output_rows.append([structure_kind, data_mode, "Delete (avg)", f"{sum(deletion_measurements)/5:.6f}"])
|
||||
|
||||
avg_insertion = sum(insertion_measurements) / 5
|
||||
avg_search = sum(search_measurements) / 5
|
||||
avg_deletion = sum(deletion_measurements) / 5
|
||||
|
||||
graph_entries.append((structure_kind, data_mode, avg_insertion, avg_search, avg_deletion))
|
||||
|
||||
# Запуск всех тестов
|
||||
execute_trial("LinkedList", "random", shuffled_records)
|
||||
execute_trial("LinkedList", "sorted", ordered_records)
|
||||
execute_trial("HashTable", "random", shuffled_records)
|
||||
execute_trial("HashTable", "sorted", ordered_records)
|
||||
execute_trial("BST", "random", shuffled_records)
|
||||
execute_trial("BST", "sorted", ordered_records)
|
||||
|
||||
# Сохранение в CSV
|
||||
with open("benchmark_output.csv", "w", newline="", encoding="utf-8") as csv_file:
|
||||
csv_writer = csv.writer(csv_file)
|
||||
csv_writer.writerows(output_rows)
|
||||
print("\n[Success] All benchmarks completed! Results saved to 'benchmark_output.csv'.")
|
||||
|
||||
# ВЫЗЫВАЕМ ФУНКЦИЮ ДЛЯ СОЗДАНИЯ ГРАФИКОВ
|
||||
generate_performance_charts(graph_entries)
|
||||
|
||||
|
||||
def generate_performance_charts(plot_data):
|
||||
"""
|
||||
Создает графики производительности структур данных.
|
||||
|
||||
Args:
|
||||
plot_data: список кортежей (structure, mode, avg_insert, avg_find, avg_delete)
|
||||
"""
|
||||
if not plot_data:
|
||||
print("Нет данных для построения графиков")
|
||||
return
|
||||
|
||||
# Подготовка данных
|
||||
structures = ['LinkedList', 'HashTable', 'BST']
|
||||
modes = ['random', 'sorted']
|
||||
operations = ['Insert', 'Find', 'Delete']
|
||||
|
||||
# Создаем фигуру с тремя подграфиками
|
||||
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
|
||||
|
||||
for idx, operation in enumerate(operations):
|
||||
ax = axes[idx]
|
||||
|
||||
# Данные для текущей операции
|
||||
x_positions = []
|
||||
y_values = []
|
||||
labels = []
|
||||
|
||||
for structure in structures:
|
||||
for mode in modes:
|
||||
# Находим данные для этой комбинации
|
||||
for data in plot_data:
|
||||
if data[0] == structure and data[1] == mode:
|
||||
value = data[2 + idx] # 2=insert, 3=find, 4=delete
|
||||
x_positions.append(len(x_positions))
|
||||
y_values.append(value)
|
||||
labels.append(f"{structure}\n{mode}")
|
||||
|
||||
# Создаем столбчатую диаграмму
|
||||
bars = ax.bar(x_positions, y_values, color=['skyblue', 'lightcoral']*3)
|
||||
|
||||
# Настройка графика
|
||||
ax.set_title(f'Операция: {operation}', fontsize=14, fontweight='bold')
|
||||
ax.set_ylabel('Время (сек)', fontsize=12)
|
||||
ax.set_xticks(x_positions)
|
||||
ax.set_xticklabels(labels, rotation=45, ha='right', fontsize=10)
|
||||
ax.grid(axis='y', alpha=0.3)
|
||||
|
||||
# Добавляем значения над столбцами
|
||||
for bar, value in zip(bars, y_values):
|
||||
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height(),
|
||||
f'{value:.4f}', ha='center', va='bottom', fontsize=8)
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('performance_charts.png', dpi=300, bbox_inches='tight')
|
||||
plt.show()
|
||||
print("Графики сохранены в 'performance_charts.png'")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
benchmark_thread = threading.Thread(target=perform_benchmark)
|
||||
benchmark_thread.start()
|
||||
benchmark_thread.join()
|
||||
|
||||
# Дополнительно: если графики не показались автоматически
|
||||
# Можно вызвать функцию напрямую после завершения потока
|
||||
print("\nПрограмма завершена. Проверьте файл 'performance_charts.png'")
|
||||
BIN
ivankinad/task1/performance_charts.png
Normal file
BIN
ivankinad/task1/performance_charts.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
Loading…
Reference in New Issue
Block a user