{ "cells": [ { "cell_type": "code", "execution_count": 3, "id": "3701053f-41f9-464d-a44f-cbda38c1caf7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ СТРУКТУР ДАННЫХ\n", "Телефонный справочник - 10000 записей\n", "\n", " Результаты будут сохранены в: C:\\Users\\weron\\experiment_results.csv\n", " Сгенерировано 10000 записей\n", "\n", "2. Тестирование режима: случайный\n", "\n", " LINKED_LIST:\n", " Вставка 10000 записей\n", " Время: 9.2970 сек (среднее)\n", " Поиск 110 записей\n", " Время: 0.0946 сек (среднее)\n", " Удаление 50 записей\n", " Время: 0.0000 сек (среднее)\n", "\n", " HASH_TABLE:\n", " Вставка 10000 записей\n", " Время: 0.4810 сек (среднее)\n", " Поиск 110 записей\n", " Время: 0.0039 сек (среднее)\n", " Удаление 50 записей\n", " Время: 0.0030 сек (среднее)\n", "\n", " BST:\n", " Вставка 10000 записей\n", " Время: 0.0490 сек (среднее)\n", " Поиск 110 записей\n", " Время: 0.0004 сек (среднее)\n", " Удаление 50 записей\n", " Время: 0.1141 сек (среднее)\n", "\n", "2. Тестирование режима: отсортированный\n", "\n", " LINKED_LIST:\n", " Вставка 10000 записей\n", " Время: 9.2504 сек (среднее)\n", " Поиск 110 записей\n", " Время: 0.1115 сек (среднее)\n", " Удаление 50 записей\n", " Время: 0.0000 сек (среднее)\n", "\n", " HASH_TABLE:\n", " Вставка 10000 записей\n", " Время: 0.4928 сек (среднее)\n", " Поиск 110 записей\n", " Время: 0.0061 сек (среднее)\n", " Удаление 50 записей\n", " Время: 0.0029 сек (среднее)\n", "\n", " BST:\n", " Вставка 10000 записей\n", " Время: 22.1688 сек (среднее)\n", " Поиск 110 записей\n", " Время: 0.1297 сек (среднее)\n", " Удаление 50 записей\n", " Время: 0.1115 сек (среднее)\n", "\n", "3. Сохранение результатов\n", " Результаты сохранены в: C:\\Users\\weron\\experiment_results.csv\n", "СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ\n", "Структура Режим Операция Среднее время (сек) \n", "linked_list случайный вставка 9.296975 \n", "linked_list случайный поиск 0.094569 \n", "linked_list случайный удаление 0.000022 \n", "hash_table случайный вставка 0.481027 \n", "hash_table случайный поиск 0.003911 \n", "hash_table случайный удаление 0.003046 \n", "bst случайный вставка 0.049011 \n", "bst случайный поиск 0.000368 \n", "bst случайный удаление 0.114051 \n", "linked_list отсортированный вставка 9.250436 \n", "linked_list отсортированный поиск 0.111506 \n", "linked_list отсортированный удаление 0.000018 \n", "hash_table отсортированный вставка 0.492765 \n", "hash_table отсортированный поиск 0.006051 \n", "hash_table отсортированный удаление 0.002869 \n", "bst отсортированный вставка 22.168779 \n", "bst отсортированный поиск 0.129713 \n", "bst отсортированный удаление 0.111534 \n", "\n", "4. Построение графиков\n", " Графики сохранены в: C:\\Users\\weron\\performance_graphs.png\n", "ЭКСПЕРИМЕНТ ЗАВЕРШЕН УСПЕШНО!\n", "\n", " СОЗДАННЫЕ ФАЙЛЫ:\n", " Данные: C:\\Users\\weron\\experiment_results.csv\n", " Графики: C:\\Users\\weron\\performance_graphs.png\n" ] } ], "source": [ "import time\n", "import random\n", "import csv\n", "import os\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import sys\n", "sys.setrecursionlimit(20000) \n", "#Linked List Phone Book:\n", "\n", "def ll_insert(head, name, phone):\n", " new_node = {'name': name, 'phone' : phone, 'next': None}\n", " if head is None:\n", " return new_node\n", " if head['name'] == name:\n", " head['phone'] = phone\n", " return head\n", " current = head \n", " while current['next'] is not None:\n", " if current['next']['name'] == name:\n", " current['next']['phone'] = phone\n", " return head\n", " current = current['next']\n", " current['next'] = new_node\n", " return head \n", "def ll_find(head, name):\n", " current = head\n", " while current != None:\n", " if current['name']==name:\n", " return current['phone']\n", " current = current['next']\n", " return None\n", "def ll_delete(head, name):\n", " if head is None:\n", " return None\n", " if head['name'] == name:\n", " return head['next']\n", " current = head \n", " while current['next'] is not None:\n", " if current['next']['name'] == name:\n", " current['next']==current['next']['next']\n", " return head\n", " current=current['next']\n", " return head\n", "def ll_list_all(head): \n", " records= []\n", " current = head \n", " while current is not None:\n", " records.append({'name': current['name'], 'phone': current['phone']})\n", " current = current['next']\n", " records.sort(key=lambda x: x['name'])\n", " return records \n", "def ll_print_all(head):\n", " records = ll_list_all(head)\n", " for record in records:\n", " print(f\"{record['name']}: {record['phone']}\")\n", "\n", "#Hash Function:\n", "\n", "def hash_function(name, table_size):\n", " return sum(ord(c) for c in name) % table_size\n", "\n", "\n", "def ht_create(size=1000):\n", " return [None] * size\n", "\n", "\n", "def ht_insert(buckets, name, phone):\n", " size = len(buckets)\n", " index = hash_function(name, size)\n", " buckets[index] = ll_insert(buckets[index], name, phone)\n", "\n", "\n", "def ht_find(buckets, name):\n", " size = len(buckets)\n", " index = hash_function(name, size)\n", " return ll_find(buckets[index], name)\n", "\n", "\n", "def ht_delete(buckets, name):\n", " size = len(buckets)\n", " index = hash_function(name, size)\n", " buckets[index] = ll_delete(buckets[index], name)\n", "\n", "\n", "def ht_list_all(buckets):\n", " records = []\n", " for bucket in buckets:\n", " current = bucket\n", " while current is not None:\n", " records.append((current['name'], current['phone']))\n", " current = current['next']\n", " records.sort(key=lambda x: x[0])\n", " return records\n", "\n", "#Tree function:\n", "\n", "def bst_insert(root, name, phone):\n", " \n", " if root is None:\n", " return {'name': name, 'phone': phone, 'left': None, 'right': None}\n", " \n", " if name < root['name']:\n", " root['left'] = bst_insert(root['left'], name, phone)\n", " elif name > root['name']:\n", " root['right'] = bst_insert(root['right'], name, phone)\n", " else:\n", " root['phone'] = phone\n", " \n", " return root\n", "\n", "\n", "def bst_find(root, name):\n", " \n", " current = root\n", " while current is not None:\n", " if name == current['name']:\n", " return current['phone']\n", " elif name < current['name']:\n", " current = current['left']\n", " else:\n", " current = current['right']\n", " return None\n", "\n", "\n", "def bst_find_min(node):\n", " \n", " current = node\n", " while current['left'] is not None:\n", " current = current['left']\n", " return current\n", "\n", "\n", "def bst_delete(root, name):\n", " \n", " if root is None:\n", " return None\n", " \n", " if name < root['name']:\n", " root['left'] = bst_delete(root['left'], name)\n", " elif name > root['name']:\n", " root['right'] = bst_delete(root['right'], name)\n", " else:\n", " if root['left'] is None:\n", " return root['right']\n", " elif root['right'] is None:\n", " return root['left']\n", " \n", " min_node = bst_find_min(root['right'])\n", " root['name'] = min_node['name']\n", " root['phone'] = min_node['phone']\n", " root['right'] = bst_delete(root['right'], min_node['name'])\n", " \n", " return root\n", "\n", "\n", "def bst_list_all(root):\n", " \n", " records = []\n", " \n", " def inorder_traversal(node):\n", " if node is not None:\n", " inorder_traversal(node['left'])\n", " records.append((node['name'], node['phone']))\n", " inorder_traversal(node['right'])\n", " \n", " inorder_traversal(root)\n", " return records\n", "\n", "#Experemental part \n", "#1. Test data generation \n", "\n", "def generate_records(count=10000):\n", " \n", " records = []\n", " for i in range(count):\n", " name = f\"User_{i:05d}\"\n", " phone = f\"+7-{random.randint(100,999)}-{random.randint(100,999)}-{random.randint(1000,9999)}\"\n", " records.append((name, phone))\n", " \n", " shuffled = records.copy()\n", " random.shuffle(shuffled)\n", " sorted_records = sorted(records, key=lambda x: x[0])\n", " \n", " return shuffled, sorted_records\n", "\n", "#2. Timing\n", "\n", "def measure_insertion(structure_name, records):\n", " \n", " times = []\n", " filled_structure = None\n", " \n", " for run in range(5):\n", " if structure_name == \"linked_list\":\n", " structure = None\n", " elif structure_name == \"hash_table\":\n", " structure = ht_create(1000)\n", " elif structure_name == \"bst\":\n", " structure = None\n", " \n", " start = time.perf_counter()\n", " \n", " for name, phone in records:\n", " if structure_name == \"linked_list\":\n", " structure = ll_insert(structure, name, phone)\n", " elif structure_name == \"hash_table\":\n", " ht_insert(structure, name, phone)\n", " elif structure_name == \"bst\":\n", " structure = bst_insert(structure, name, phone)\n", " \n", " end = time.perf_counter()\n", " times.append(end - start)\n", " \n", " if run == 4:\n", " filled_structure = structure\n", " \n", " return times, filled_structure\n", "\n", "\n", "def measure_search(structure_name, structure, search_names):\n", " \n", " times = []\n", " \n", " for run in range(5):\n", " start = time.perf_counter()\n", " \n", " for name in search_names:\n", " if structure_name == \"linked_list\":\n", " ll_find(structure, name)\n", " elif structure_name == \"hash_table\":\n", " ht_find(structure, name)\n", " elif structure_name == \"bst\":\n", " bst_find(structure, name)\n", " \n", " end = time.perf_counter()\n", " times.append(end - start)\n", " \n", " return times\n", "\n", "\n", "def measure_deletion(structure_name, original_structure, delete_names):\n", " \n", " times = []\n", " \n", " for run in range(5):\n", " if structure_name == \"linked_list\":\n", " all_records = ll_list_all(original_structure)\n", " test_structure = None\n", " for name, phone in all_records:\n", " test_structure = ll_insert(test_structure, name, phone)\n", " \n", " elif structure_name == \"hash_table\":\n", " all_records = ht_list_all(original_structure)\n", " test_structure = ht_create(1000)\n", " for name, phone in all_records:\n", " ht_insert(test_structure, name, phone)\n", " \n", " elif structure_name == \"bst\":\n", " all_records = bst_list_all(original_structure)\n", " test_structure = None\n", " for name, phone in all_records:\n", " test_structure = bst_insert(test_structure, name, phone)\n", " \n", " start = time.perf_counter()\n", " \n", " for name in delete_names:\n", " if structure_name == \"linked_list\":\n", " test_structure = ll_delete(test_structure, name)\n", " elif structure_name == \"hash_table\":\n", " ht_delete(test_structure, name)\n", " elif structure_name == \"bst\":\n", " test_structure = bst_delete(test_structure, name)\n", " \n", " end = time.perf_counter()\n", " times.append(end - start)\n", " \n", " return times\n", "#3. Launch and save results\n", "\n", "def run_experiment():\n", " \n", " current_dir = os.getcwd()\n", " docs_dir = current_dir\n", " csv_file = os.path.join(docs_dir, \"experiment_results.csv\")\n", " \n", " print(\"ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ СТРУКТУР ДАННЫХ\")\n", " print(\"Телефонный справочник - 10000 записей\")\n", " print(f\"\\n Результаты будут сохранены в: {csv_file}\")\n", " \n", " shuffled_records, sorted_records = generate_records(10000)\n", " print(f\" Сгенерировано 10000 записей\")\n", " \n", " existing_names = [shuffled_records[i][0] for i in random.sample(range(10000), 100)]\n", " nonexisting_names = [f\"NotExist_{i}\" for i in range(10)]\n", " search_names = existing_names + nonexisting_names\n", " delete_names = [shuffled_records[i][0] for i in random.sample(range(10000), 50)]\n", " \n", " results = [[\"Структура\", \"Режим\", \"Операция\", \n", " \"Замер1(с)\", \"Замер2(с)\", \"Замер3(с)\", \"Замер4(с)\", \"Замер5(с)\", \n", " \"Среднее(с)\"]]\n", " \n", " for mode_name, records in [(\"случайный\", shuffled_records), \n", " (\"отсортированный\", sorted_records)]:\n", " \n", " print(f\"\\n2. Тестирование режима: {mode_name}\")\n", " \n", " for struct_name in [\"linked_list\", \"hash_table\", \"bst\"]:\n", " print(f\"\\n {struct_name.upper()}:\")\n", " \n", " print(\" Вставка 10000 записей\")\n", " insert_times, filled_struct = measure_insertion(struct_name, records)\n", " avg_insert = sum(insert_times) / 5\n", " print(f\" Время: {avg_insert:.4f} сек (среднее)\")\n", " \n", " print(\" Поиск 110 записей\")\n", " search_times = measure_search(struct_name, filled_struct, search_names)\n", " avg_search = sum(search_times) / 5\n", " print(f\" Время: {avg_search:.4f} сек (среднее)\")\n", " \n", " print(\" Удаление 50 записей\")\n", " delete_times = measure_deletion(struct_name, filled_struct, delete_names)\n", " avg_delete = sum(delete_times) / 5\n", " print(f\" Время: {avg_delete:.4f} сек (среднее)\")\n", " \n", " results.append([struct_name, mode_name, \"вставка\"] + insert_times + [avg_insert])\n", " results.append([struct_name, mode_name, \"поиск\"] + search_times + [avg_search])\n", " results.append([struct_name, mode_name, \"удаление\"] + delete_times + [avg_delete])\n", " \n", " print(\"\\n3. Сохранение результатов\")\n", " with open(csv_file, \"w\", newline=\"\", encoding=\"utf-8\") as f:\n", " writer = csv.writer(f)\n", " writer.writerows(results)\n", " print(f\" Результаты сохранены в: {csv_file}\")\n", " \n", " print(\"СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ\")\n", " print(f\"{'Структура':<15} {'Режим':<12} {'Операция':<10} {'Среднее время (сек)':<20}\")\n", " \n", " for row in results[1:]:\n", " struct, mode, op, t1, t2, t3, t4, t5, avg = row\n", " print(f\"{struct:<15} {mode:<12} {op:<10} {avg:<20.6f}\")\n", " \n", " return results, docs_dir\n", "\n", "#4. Graphics\n", "\n", "def create_graphs(results, docs_dir):\n", " \n", " print(\"\\n4. Построение графиков\")\n", " \n", " data = {}\n", " for row in results[1:]:\n", " struct = row[0]\n", " mode = row[1]\n", " op = row[2]\n", " avg = row[8]\n", " \n", " if struct not in data:\n", " data[struct] = {}\n", " if mode not in data[struct]:\n", " data[struct][mode] = {}\n", " data[struct][mode][op] = avg\n", " \n", " \n", " struct_labels = {\n", " 'linked_list': 'LinkedList',\n", " 'hash_table': 'HashTable',\n", " 'bst': 'BST'\n", " }\n", " \n", " \n", " colors = {\n", " 'linked_list': '#8b00ff', \n", " 'hash_table': '#81d8d0', \n", " 'bst': '#000000' \n", " }\n", " \n", " \n", " fig, axes = plt.subplots(1, 3, figsize=(15, 6))\n", " fig.suptitle('Сравнение производительности структур данных', fontsize=16, fontweight='bold')\n", " \n", " operations = ['вставка', 'поиск', 'удаление']\n", " operation_titles = ['Вставка\\n(10000 записей)', 'Поиск\\n(110 запросов)', 'Удаление\\n(50 записей)']\n", " modes = ['случайный', 'отсортированный']\n", " mode_labels = ['Случайный', 'Отсортированный']\n", " \n", " for idx, (op, op_title) in enumerate(zip(operations, operation_titles)):\n", " ax = axes[idx]\n", " \n", " # Позиции для групп столбцов\n", " x = np.arange(len(modes)) # [0, 1]\n", " width = 0.3 # ширина одного столбца\n", " multiplier = 0\n", " \n", " for struct in ['linked_list', 'hash_table', 'bst']:\n", " values = [data[struct][mode][op] for mode in modes]\n", " offset = width * multiplier\n", " bars = ax.bar(x + offset, values, width, \n", " label=struct_labels[struct], \n", " color=colors[struct],\n", " edgecolor='black', linewidth=0.5)\n", " \n", " \n", " for bar, val in zip(bars, values):\n", " if val < 0.01:\n", " ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + val*0.05, \n", " f'{val:.5f}', ha='center', va='bottom', fontsize=8, rotation=0)\n", " else:\n", " ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + val*0.02, \n", " f'{val:.4f}', ha='center', va='bottom', fontsize=8, rotation=0)\n", " \n", " multiplier += 1\n", " \n", " \n", " ax.set_title(op_title, fontsize=12, fontweight='bold')\n", " ax.set_ylabel('Время (секунды)', fontsize=10)\n", " ax.set_xlabel('Режим данных', fontsize=10)\n", " ax.set_xticks(x + width)\n", " ax.set_xticklabels(mode_labels)\n", " ax.legend(loc='upper left', fontsize=8)\n", " ax.grid(True, alpha=0.3, axis='y')\n", " \n", " \n", " all_values = [data[s][m][op] for s in ['linked_list', 'hash_table', 'bst'] for m in modes]\n", " if max(all_values) / min(all_values) > 100:\n", " ax.set_yscale('log')\n", " ax.set_ylabel('Время (секунды) - логарифмическая шкала', fontsize=9)\n", " \n", " plt.tight_layout()\n", " graph_path = os.path.join(docs_dir, \"performance_graphs.png\")\n", " plt.savefig(graph_path, dpi=150, bbox_inches='tight')\n", " plt.close()\n", " print(f\" Графики сохранены в: {graph_path}\")\n", " \n", " return graph_path\n", "\n", "#5. Main program\n", "\n", "if __name__ == \"__main__\":\n", " \n", " results, docs_dir = run_experiment()\n", " \n", " \n", " try:\n", " graph_file = create_graphs(results, docs_dir)\n", " \n", " print(\"ЭКСПЕРИМЕНТ ЗАВЕРШЕН УСПЕШНО!\")\n", " print(\"\\n СОЗДАННЫЕ ФАЙЛЫ:\")\n", " print(f\" Данные: {os.path.join(docs_dir, 'experiment_results.csv')}\")\n", " print(f\" Графики: {graph_file}\")\n", " \n", " except Exception as e:\n", " print(f\"\\n Ошибка при построении графиков: {e}\")\n", " print(\" Убедитесь, что установлен matplotlib: pip install matplotlib\")\n", " print(\"ЭКСПЕРИМЕНТ ЗАВЕРШЕН (без графиков)\")\n", " print(f\"\\n CSV файл сохранен: {os.path.join(docs_dir, 'experiment_results.csv')}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "e02735f2-61dc-484b-b74c-1456f7399863", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:base] *", "language": "python", "name": "conda-base-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.9" } }, "nbformat": 4, "nbformat_minor": 5 }