diff --git a/ivankinad/task1/benchmark_output.csv b/ivankinad/task1/benchmark_output.csv new file mode 100644 index 0000000..637b602 --- /dev/null +++ b/ivankinad/task1/benchmark_output.csv @@ -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 diff --git a/ivankinad/task1/laba1.py b/ivankinad/task1/laba1.py new file mode 100644 index 0000000..2710db3 --- /dev/null +++ b/ivankinad/task1/laba1.py @@ -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'") \ No newline at end of file diff --git a/ivankinad/task1/performance_charts.png b/ivankinad/task1/performance_charts.png new file mode 100644 index 0000000..d65e22b Binary files /dev/null and b/ivankinad/task1/performance_charts.png differ