diff --git a/filippovavm/laba1/МП.ipynb b/filippovavm/laba1/МП.ipynb new file mode 100644 index 0000000..e25dd7e --- /dev/null +++ b/filippovavm/laba1/МП.ipynb @@ -0,0 +1,594 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dbe95ca0-7bc2-4cea-bdbb-319e9a1bd5b6", + "metadata": {}, + "source": [ + "# Импорт библиотек\n", + "# В этом блоке подключаются модули для работы со временем, случайными числами, CSV, системными параметрами, а также для построения графиков." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c0b2cd62-6c05-4896-8f40-82d75ae765e9", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import random\n", + "import csv\n", + "import sys\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "919b92b0-2819-457a-87a0-8f26eaeca817", + "metadata": {}, + "source": [ + "# Связный список\n", + "# Каждый узел содержит имя, телефон и ссылку на следующий узел.\n", + "# Функции: ll_insert (вставка/обновление), ll_find (поиск), ll_delete (удаление)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "30700662-1215-4476-bffe-96ad9d3f1ab4", + "metadata": {}, + "outputs": [], + "source": [ + "# Связный список\n", + "def ll_insert(head, name, phone):\n", + " current = head\n", + " prev = None\n", + " while current is not None:\n", + " if current['name'] == name:\n", + " current['phone'] = phone\n", + " return head\n", + " prev = current\n", + " current = current['next']\n", + " new_node = {'name': name, 'phone': phone, 'next': None}\n", + " if prev is None:\n", + " return new_node\n", + " else:\n", + " prev['next'] = new_node\n", + " return head\n", + "\n", + "def ll_find(head, name):\n", + " current = head\n", + " while current is not None:\n", + " if current['name'] == name:\n", + " return current['phone']\n", + " current = current['next']\n", + " return None\n", + "\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" + ] + }, + { + "cell_type": "markdown", + "id": "49dd7db0-58a7-4ff9-98cd-529e2e91ca94", + "metadata": {}, + "source": [ + "# Хеш-таблица\n", + "# Хеш-функция на основе полиномиального кода (31 и длина таблицы).\n", + "# Размер таблицы фиксирован (2000). В каждой ячейке хранится связный список.\n", + "# Функции: ht_create, ht_insert, ht_find, ht_delete." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "22800445-5217-45ce-9ecd-0f61389b1c3f", + "metadata": {}, + "outputs": [], + "source": [ + "# Хеш-таблица \n", + "def hash_function(name, size):\n", + " total = 0\n", + " for ch in name:\n", + " total = (total * 31 + ord(ch)) % size\n", + " return total\n", + "\n", + "def ht_create(size=2000):\n", + " return [None] * size\n", + "\n", + "def ht_insert(buckets, name, phone):\n", + " idx = hash_function(name, len(buckets))\n", + " buckets[idx] = ll_insert(buckets[idx], name, phone)\n", + "\n", + "def ht_find(buckets, name):\n", + " idx = hash_function(name, len(buckets))\n", + " return ll_find(buckets[idx], name)\n", + "\n", + "def ht_delete(buckets, name):\n", + " idx = hash_function(name, len(buckets))\n", + " buckets[idx] = ll_delete(buckets[idx], name)\n" + ] + }, + { + "cell_type": "markdown", + "id": "0b03af57-2771-4b20-8e7d-1ac475afaae8", + "metadata": {}, + "source": [ + "# Двоичное дерево поиска\n", + "# Узел содержит имя, телефон, ссылки на левого и правого потомка.\n", + "# Функции: bst_insert (вставка/обновление), bst_find (поиск), bst_delete (удаление).\n", + "# Для удаления используется поиск преемника (минимальный элемент в правом поддереве)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7ced846b-b65f-4cf2-8fb8-5a4849f55509", + "metadata": {}, + "outputs": [], + "source": [ + "# Двоичное дерево поиска \n", + "def bst_insert(root, name, phone):\n", + " new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}\n", + " if root is None:\n", + " return new_node\n", + " current = root\n", + " while True:\n", + " if name < current['name']:\n", + " if current['left'] is None:\n", + " current['left'] = new_node\n", + " break\n", + " current = current['left']\n", + " elif name > current['name']:\n", + " if current['right'] is None:\n", + " current['right'] = new_node\n", + " break\n", + " current = current['right']\n", + " else:\n", + " current['phone'] = phone\n", + " break\n", + " return root\n", + "\n", + "def bst_find(root, name):\n", + " current = root\n", + " while current is not None:\n", + " if name < current['name']:\n", + " current = current['left']\n", + " elif name > current['name']:\n", + " current = current['right']\n", + " else:\n", + " return current['phone']\n", + " return None\n", + "\n", + "def bst_find_min(node):\n", + " while node['left'] is not None:\n", + " node = node['left']\n", + " return node\n", + "\n", + "def bst_delete(root, name):\n", + " parent = None\n", + " current = root\n", + " while current is not None and current['name'] != name:\n", + " parent = current\n", + " if name < current['name']:\n", + " current = current['left']\n", + " else:\n", + " current = current['right']\n", + " if current is None:\n", + " return root\n", + " \n", + " if current['left'] is None and current['right'] is None:\n", + " if parent is None:\n", + " return None\n", + " if parent['left'] is current:\n", + " parent['left'] = None\n", + " else:\n", + " parent['right'] = None\n", + " return root\n", + " if current['left'] is None:\n", + " if parent is None:\n", + " return current['right']\n", + " if parent['left'] is current:\n", + " parent['left'] = current['right']\n", + " else:\n", + " parent['right'] = current['right']\n", + " return root\n", + " if current['right'] is None:\n", + " if parent is None:\n", + " return current['left']\n", + " if parent['left'] is current:\n", + " parent['left'] = current['left']\n", + " else:\n", + " parent['right'] = current['left']\n", + " return root\n", + " \n", + " succ_parent = current\n", + " succ = current['right']\n", + " while succ['left'] is not None:\n", + " succ_parent = succ\n", + " succ = succ['left']\n", + " current['name'] = succ['name']\n", + " current['phone'] = succ['phone']\n", + " if succ_parent['left'] is succ:\n", + " succ_parent['left'] = succ['right']\n", + " else:\n", + " succ_parent['right'] = succ['right']\n", + " return root" + ] + }, + { + "cell_type": "markdown", + "id": "88e1d5ec-5123-4bff-837a-bb451a0125c3", + "metadata": {}, + "source": [ + "# Генерация записей телефонной книги\n", + "# Создаёт N записей вида User_00001 и случайный номер телефона." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b266c127-99a7-479c-a012-3eedeff7ade3", + "metadata": {}, + "outputs": [], + "source": [ + "# Генерация данных \n", + "def generate_records(N):\n", + " records = []\n", + " for i in range(N):\n", + " name = f\"User_{i:05d}\"\n", + " phone = f\"+7-999-{random.randint(1000000, 9999999)}\"\n", + " records.append((name, phone))\n", + " return records" + ] + }, + { + "cell_type": "markdown", + "id": "a92500c8-e928-46a8-a115-12cc033ea2da", + "metadata": {}, + "source": [ + "# Функции замеров\n", + "# measure_insert – вставка всех записей (повторяется 5 раз).\n", + "# build_structure – построение структуры данных (используется для последующих замеров).\n", + "# measure_find_on_structure – поиск 110 записей (100 существующих + 10 отсутствующих) 5 раз.\n", + "# measure_delete_on_structure – удаление 50 случайных записей 5 раз." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5060601-6e07-4148-9faa-f9b7b5f433dd", + "metadata": {}, + "outputs": [], + "source": [ + "# Замеры\n", + "REPEATS = 5\n", + "N = 10000\n", + "\n", + "def measure_insert(struct, records, repeats=REPEATS):\n", + " times = []\n", + " for _ in range(repeats):\n", + " if struct == 'll':\n", + " head = None\n", + " start = time.perf_counter()\n", + " for name, phone in records:\n", + " head = ll_insert(head, name, phone)\n", + " end = time.perf_counter()\n", + " elif struct == 'ht':\n", + " buckets = ht_create(2000)\n", + " start = time.perf_counter()\n", + " for name, phone in records:\n", + " ht_insert(buckets, name, phone)\n", + " end = time.perf_counter()\n", + " elif struct == 'bst':\n", + " root = None\n", + " start = time.perf_counter()\n", + " for name, phone in records:\n", + " root = bst_insert(root, name, phone)\n", + " end = time.perf_counter()\n", + " times.append(end - start)\n", + " return times\n", + "\n", + "def build_structure(struct, records):\n", + " if struct == 'll':\n", + " head = None\n", + " for name, phone in records:\n", + " head = ll_insert(head, name, phone)\n", + " return head\n", + " elif struct == 'ht':\n", + " buckets = ht_create(2000)\n", + " for name, phone in records:\n", + " ht_insert(buckets, name, phone)\n", + " return buckets\n", + " elif struct == 'bst':\n", + " root = None\n", + " for name, phone in records:\n", + " root = bst_insert(root, name, phone)\n", + " return root\n", + " def measure_find_on_structure(struct, structure, records, repeats=REPEATS):\n", + " times = []\n", + " N = len(records)\n", + " for _ in range(repeats):\n", + " indices = random.sample(range(N), 100)\n", + " exist = [records[i][0] for i in indices]\n", + " missing = [f\"None_{i}\" for i in range(10)]\n", + " search = exist + missing\n", + " start = time.perf_counter()\n", + " if struct == 'll':\n", + " for name in search:\n", + " ll_find(structure, name)\n", + " elif struct == 'ht':\n", + " for name in search:\n", + " ht_find(structure, name)\n", + " elif struct == 'bst':\n", + " for name in search:\n", + " bst_find(structure, name)\n", + " times.append(time.perf_counter() - start)\n", + " return times\n", + "\n", + "def measure_delete_on_structure(struct, records, repeats=REPEATS):\n", + " times = []\n", + " N = len(records)\n", + " for _ in range(repeats):\n", + " indices = random.sample(range(N), 50)\n", + " del_names = [records[i][0] for i in indices]\n", + " if struct == 'll':\n", + " head = None\n", + " for name, phone in records:\n", + " head = ll_insert(head, name, phone)\n", + " start = time.perf_counter()\n", + " for name in del_names:\n", + " head = ll_delete(head, name)\n", + " end = time.perf_counter()\n", + " elif struct == 'ht':\n", + " buckets = ht_create(2000)\n", + " for name, phone in records:\n", + " ht_insert(buckets, name, phone)\n", + " start = time.perf_counter()\n", + " for name in del_names:\n", + " ht_delete(buckets, name)\n", + " end = time.perf_counter()\n", + " elif struct == 'bst':\n", + " root = None\n", + " for name, phone in records:\n", + " root = bst_insert(root, name, phone)\n", + " start = time.perf_counter()\n", + " for name in del_names:\n", + " root = bst_delete(root, name)\n", + " end = time.perf_counter()\n", + " times.append(end - start)\n", + " return times\n" + ] + }, + { + "cell_type": "markdown", + "id": "d93d8eac-b094-43b2-8af0-9afb0ab51ada", + "metadata": {}, + "source": [ + "# Основная функция\n", + "# 1. Генерирует 10000 записей в случайном и отсортированном порядке.\n", + "# 2. Измеряет время вставки всех записей, поиска (110 запросов) и удаления (50 записей)\n", + "# для связного списка, хеш-таблицы и дерева.\n", + "# 3. Выводит средние значения, сохраняет все замеры в CSV.\n", + "# 4. Строит несколько графиков (сравнительные столбчатые диаграммы и графики по попыткам)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a667779c-39b8-4e4b-b05d-bf9c9cccbbd9", + "metadata": {}, + "outputs": [], + "source": [ + "# Основная функция \n", + "def main():\n", + " print(\"Генерация данных...\")\n", + " records = generate_records(N)\n", + " random.shuffle(records) # случайный порядок\n", + " records_sorted = sorted(records, key=lambda x: x[0]) # отсортированный\n", + "\n", + " results = [] # для CSV\n", + " struct_names = {'ll': 'Связного списка', 'ht': 'Хеш-таблицы', 'bst': 'Двоичного дерева поиска'}\n", + " mode_names = {'shuffled': 'случайный', 'sorted': 'отсортированный'}\n", + " op_names = {'insert': 'Вставка всех записей', 'find': 'Поиск записей', 'delete': 'Удаление записей'}\n", + " # Вставка \n", + " for struct in ['ll', 'ht', 'bst']:\n", + " print(f\"\\nИзмерение вставки для {struct_names[struct]}...\")\n", + " times_sh = measure_insert(struct, records)\n", + " times_so = measure_insert(struct, records_sorted)\n", + " insert_sh[struct] = times_sh\n", + " insert_so[struct] = times_so\n", + " print(f\" случайный: {[round(t,6) for t in times_sh]}, среднее = {sum(times_sh)/len(times_sh):.6f}\")\n", + " print(f\" отсортированный: {[round(t,6) for t in times_so]}, среднее = {sum(times_so)/len(times_so):.6f}\")\n", + " results.append([struct_names[struct], mode_names['shuffled'], op_names['insert'], sum(times_sh)/len(times_sh)] + times_sh)\n", + " results.append([struct_names[struct], mode_names['sorted'], op_names['insert'], sum(times_so)/len(times_so)] + times_so)\n", + " # Поиск \n", + " for struct in ['ll', 'ht', 'bst']:\n", + " print(f\"\\nПоиск для {struct_names[struct]} на случайных данных...\")\n", + " structure_sh = build_structure(struct, records)\n", + " times_find_sh = measure_find_on_structure(struct, structure_sh, records)\n", + " find_sh[struct] = times_find_sh\n", + " print(f\" случайный: {[round(t,6) for t in times_find_sh]}, среднее = {sum(times_find_sh)/len(times_find_sh):.6f}\")\n", + " results.append([struct_names[struct], mode_names['shuffled'], op_names['find'], sum(times_find_sh)/len(times_find_sh)] + times_find_sh)\n", + "\n", + " print(f\"Поиск для {struct_names[struct]} на отсортированных данных...\")\n", + " structure_so = build_structure(struct, records_sorted)\n", + " times_find_so = measure_find_on_structure(struct, structure_so, records_sorted)\n", + " find_so[struct] = times_find_so\n", + " print(f\" отсортированный: {[round(t,6) for t in times_find_so]}, среднее = {sum(times_find_so)/len(times_find_so):.6f}\")\n", + " results.append([struct_names[struct], mode_names['sorted'], op_names['find'], sum(times_find_so)/len(times_find_so)] + times_find_so)\n", + " # Удаление \n", + " for struct in ['ll', 'ht', 'bst']:\n", + " print(f\"\\nУдаление для {struct_names[struct]} на случайных данных...\")\n", + " times_del_sh = measure_delete_on_structure(struct, records)\n", + " delete_sh[struct] = times_del_sh\n", + " print(f\" случайный: {[round(t,6) for t in times_del_sh]}, среднее = {sum(times_del_sh)/len(times_del_sh):.6f}\")\n", + " results.append([struct_names[struct], mode_names['shuffled'], op_names['delete'], sum(times_del_sh)/len(times_del_sh)] + times_del_sh)\n", + "\n", + " print(f\"Удаление для {struct_names[struct]} на отсортированных данных...\")\n", + " times_del_so = measure_delete_on_structure(struct, records_sorted)\n", + " delete_so[struct] = times_del_so\n", + " print(f\" отсортированный: {[round(t,6) for t in times_del_so]}, среднее = {sum(times_del_so)/len(times_del_so):.6f}\")\n", + " results.append([struct_names[struct], mode_names['sorted'], op_names['delete'], sum(times_del_so)/len(times_del_so)] + times_del_so)\n", + " # Сохраняем CSV\n", + " with open(\"phonebook_results.csv\", \"w\", newline=\"\", encoding=\"utf-8\") as f:\n", + " writer = csv.writer(f)\n", + " writer.writerow(['Структура', 'Режим', 'Операция', 'Среднее', 'Замер1', 'Замер2', 'Замер3', 'Замер4', 'Замер5'])\n", + " writer.writerows(results)\n", + "# Графики замеров\n", + " try:\n", + " def plot_attempts(data_sh, data_so, op_name):\n", + " fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))\n", + " # случайный порядок\n", + " for struct, label, color, marker in [('ll','LinkedList','red','o'), ('ht','HashTable','green','s'), ('bst','BST','blue','^')]:\n", + " times = data_sh[struct]\n", + " x = range(1, len(times)+1)\n", + " ax1.plot(x, times, marker=marker, color=color, label=label, linestyle='--', linewidth=1)\n", + " ax1.scatter(x, times, color=color, s=60, zorder=5)\n", + " ax1.set_xlabel('Номер попытки')\n", + " ax1.set_ylabel('Время (сек)')\n", + " ax1.set_title(f'{op_name} – случайный порядок')\n", + " ax1.legend()\n", + " ax1.grid(True, linestyle=':', alpha=0.7)\n", + " # отсортированный порядок\n", + " for struct, label, color, marker in [('ll','LinkedList','red','o'), ('ht','HashTable','green','s'), ('bst','BST','blue','^')]:\n", + " times = data_so[struct]\n", + " x = range(1, len(times)+1)\n", + " ax2.plot(x, times, marker=marker, color=color, label=label, linestyle='--', linewidth=1)\n", + " ax2.scatter(x, times, color=color, s=60, zorder=5)\n", + " ax2.set_xlabel('Номер попытки')\n", + " ax2.set_ylabel('Время (сек)')\n", + " ax2.set_title(f'{op_name} – отсортированный порядок')\n", + " ax2.legend()\n", + " ax2.grid(True, linestyle=':', alpha=0.7)\n", + " plt.tight_layout()\n", + " plt.savefig(f'{op_name}_5attempts.png')\n", + " plt.show()\n", + " \n", + " plot_attempts(insert_sh, insert_so, 'insert')\n", + " plot_attempts(find_sh, find_so, 'find')\n", + " plot_attempts(delete_sh, delete_so, 'delete')\n", + " print(\"Дополнительные графики сохранены: insert_5attempts.png, find_5attempts.png, delete_5attempts.png\")\n", + " except Exception as e:\n", + " print(f\"Не удалось построить дополнительные графики: {e}\")\n", + "try:\n", + " # График вставки\n", + " fig1, ax1 = plt.subplots(figsize=(10,6))\n", + " x = np.arange(3)\n", + " width = 0.35\n", + " means_sh = [sum(insert_sh[s])/len(insert_sh[s]) for s in ['ll','ht','bst']]\n", + " means_so = [sum(insert_so[s])/len(insert_so[s]) for s in ['ll','ht','bst']]\n", + " rects1 = ax1.bar(x - width/2, means_sh, width, label='Случайный порядок', color='skyblue')\n", + " rects2 = ax1.bar(x + width/2, means_so, width, label='Отсортированный порядок', color='salmon')\n", + " ax1.set_ylabel('Время (сек)')\n", + " ax1.set_title('Вставка всех записей')\n", + " ax1.set_xticks(x)\n", + " ax1.set_xticklabels(['Связный список', 'Хеш-таблица', 'Двоичное дерево'])\n", + " ax1.legend()\n", + " ax1.set_yscale('log')\n", + " for rect in rects1 + rects2:\n", + " h = rect.get_height()\n", + " ax1.annotate(f'{h:.3f}', xy=(rect.get_x()+rect.get_width()/2, h),\n", + " xytext=(0,3), textcoords=\"offset points\", ha='center', va='bottom', fontsize=8)\n", + " plt.tight_layout()\n", + " plt.savefig('insert_comparison.png')\n", + " plt.show()\n", + "\n", + " # График поиска\n", + " fig2, ax2 = plt.subplots(figsize=(10,6))\n", + " means_find_sh = [sum(find_sh[s])/len(find_sh[s]) for s in ['ll','ht','bst']]\n", + " means_find_so = [sum(find_so[s])/len(find_so[s]) for s in ['ll','ht','bst']]\n", + " rects1 = ax2.bar(x - width/2, means_find_sh, width, label='Случайный порядок', color='skyblue')\n", + " rects2 = ax2.bar(x + width/2, means_find_so, width, label='Отсортированный порядок', color='salmon')\n", + " ax2.set_ylabel('Время (сек)')\n", + " ax2.set_title('Поиск (100 существующих + 10 отсутствующих)')\n", + " ax2.set_xticks(x)\n", + " ax2.set_xticklabels(['Связный список', 'Хеш-таблица', 'Двоичное дерево'])\n", + " ax2.legend()\n", + " for rect in rects1 + rects2:\n", + " h = rect.get_height()\n", + " ax2.annotate(f'{h:.5f}', xy=(rect.get_x()+rect.get_width()/2, h),\n", + " xytext=(0,3), textcoords=\"offset points\", ha='center', va='bottom', fontsize=8)\n", + " plt.tight_layout()\n", + " plt.savefig('find_comparison.png')\n", + " plt.show()\n", + "\n", + " # График удаления \n", + " fig3, ax3 = plt.subplots(figsize=(10,6))\n", + " means_del_sh = [sum(delete_sh[s])/len(delete_sh[s]) for s in ['ll','ht','bst']]\n", + " means_del_so = [sum(delete_so[s])/len(delete_so[s]) for s in ['ll','ht','bst']]\n", + " rects1 = ax3.bar(x - width/2, means_del_sh, width, label='Случайный порядок', color='skyblue')\n", + " rects2 = ax3.bar(x + width/2, means_del_so, width, label='Отсортированный порядок', color='salmon')\n", + " ax3.set_ylabel('Время (сек)')\n", + " ax3.set_title('Удаление 50 случайных записей')\n", + " ax3.set_xticks(x)\n", + " ax3.set_xticklabels(['Связный список', 'Хеш-таблица', 'Двоичное дерево'])\n", + " ax3.legend()\n", + " for rect in rects1 + rects2:\n", + " h = rect.get_height()\n", + " ax3.annotate(f'{h:.5f}', xy=(rect.get_x()+rect.get_width()/2, h),\n", + " xytext=(0,3), textcoords=\"offset points\", ha='center', va='bottom', fontsize=8)\n", + " plt.tight_layout()\n", + " plt.savefig('delete_comparison.png')\n", + " plt.show()\n", + " print(\"Графики сохранены: insert_comparison.png, find_comparison.png, delete_comparison.png\")\n", + " except Exception as e:\n", + " print(f\"Не удалось построить графики: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "dd2b18cd-f37c-4f9f-a92a-325be857deb0", + "metadata": {}, + "source": [ + "# Запуск эксперимента\n", + "# Устанавливается увеличенная глубина рекурсии (на случай глубоких деревьев) и вызывается main()." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04462e9d-aecc-44b3-b98b-0d06f856f38a", + "metadata": {}, + "outputs": [], + "source": [ + "if __name__ == \"__main__\":\n", + " sys.setrecursionlimit(20000)\n", + " main()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/filippovavm/laba1/отчёт1.ipynb b/filippovavm/laba1/отчёт1.ipynb new file mode 100644 index 0000000..5a91b0f --- /dev/null +++ b/filippovavm/laba1/отчёт1.ipynb @@ -0,0 +1,149 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0c973489-075d-42ac-a28f-f8d8d954a0da", + "metadata": {}, + "source": [ + "# Анализ результатов\n", + "\n", + "## Предложенные вопросы\n", + "\n", + "- Как порядок входных данных влияет на скорость вставки в BST (деградация до O(n) на отсортированных данных)?\n", + "- Почему хеш-таблица почти не чувствительна к порядку?\n", + "- Почему связный список всегда медленен при поиске?\n", + "- Как удаление работает в каждой структуре?\n", + "- Вывод: какую структуру и для каких задач (частые вставки, частый поиск, необходимость получать данные в порядке) стоит выбирать в реальной жизни?\n" + ] + }, + { + "cell_type": "markdown", + "id": "a265cc14-95ff-47ae-a346-ac38cb2a323e", + "metadata": {}, + "source": [ + "## Выводы\n", + "\n", + "### 1) Как порядок входных данных влияет на скорость вставки в BST?\n", + "\n", + "Порядок отличается очень сильно. В обычном случае сложность равна **O(log n)**, а в худшем случае (как раз на отсортированных данных) – **O(n)**. \n" + ] + }, + { + "cell_type": "markdown", + "id": "950c5e97-12e9-4225-a91e-b8289fdfb5e6", + "metadata": {}, + "source": [ + "### 2) Почему хеш-таблица почти не чувствительна к порядку?\n", + "\n", + "Это происходит из‑за особенностей записи данных в память. Хеш-таблица вычисляет номер строки (корзины) по математической формуле, поэтому любой элемент можно найти за **O(1)** в среднем. Порядок поступления записей не влияет на расчёт индекса.\n" + ] + }, + { + "cell_type": "markdown", + "id": "5f6059bf-e99a-4b14-869c-32fb44b092fa", + "metadata": {}, + "source": [ + "### 3) Почему связный список всегда медленен при поиске?\n", + "\n", + "Это происходит из‑за способа записи. Доступ к следующему элементу возможен только последовательным перебором, равным номеру искомой позиции. Сложность поиска – **O(n)**.\n" + ] + }, + { + "cell_type": "markdown", + "id": "77ddd385-a50d-4ab6-b761-477460529e9d", + "metadata": {}, + "source": [ + "### 4) Как удаление работает в каждой структуре?\n", + "\n", + "- **Связный список** \n", + " - Если список пустой → возвращаем `None`. \n", + " - Если удаляем голову → новой головой становится следующий элемент. \n", + " - Если удаляем промежуточный элемент – ищем нужный узел, затем у предыдущего узла меняем ссылку на следующий после удаляемого.\n", + "\n", + "- **Хеш-таблица** \n", + " Реализация вычисляет номер корзины через хеш-ключ, затем использует функции связного списка для работы внутри корзины. Таким образом, удаление в хеш-таблице наследует логику удаления из связного списка, но применяется только к элементам одной корзины.\n", + "\n", + "- **Бинарное дерево (BST)** \n", + " Рассматриваются 4 случая (логика похожа на связный список, но с учётом двух потомков): \n", + " - Если дерево пустое → вернуть `None`. \n", + " - Если удаляемый элемент меньше корня → спуститься в левую ветвь. \n", + " - Если больше корня → спуститься в правую ветвь. \n", + " - Если у удаляемого узла два потомка – находим преемника (самый левый узел в правом поддереве), копируем его данные в удаляемый узел и удаляем преемника. \n", + " При нахождении элемента ссылка от родителя к удаляемому узлу заменяется на ссылку на соответствующего потомка (левый или правый).\n" + ] + }, + { + "cell_type": "markdown", + "id": "97b09bb6-e8ef-486e-9cdd-fc37b19bfeb2", + "metadata": {}, + "source": [ + "### 5) Какую структуру и для каких задач стоит выбирать в реальной жизни?\n", + "\n", + "- **Для частых вставок и поиска элементов** – лучше всего использовать **хеш-таблицу**, так как добавление происходит за счёт математического вычисления индекса, а не последовательного перебора. \n", + "- **Если нужны упорядоченные данные** (например, вывод записей в алфавитном порядке) – подходит **двоичное дерево поиска** (BST) благодаря свойству in‑order обхода. \n", + "- **Связный список** неэффективен для больших объёмов данных.\n", + "\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "3b242f54-fd27-4f6e-8a31-9b3a6bee5f59", + "metadata": {}, + "source": [ + "## Дополнительные числовые результаты\n", + "\n", + "\n", + "\n", + "1. **Связный список:** \n", + " - Вставка: O(n) → ~0.25 с, порядок данных не влияет. \n", + " - Поиск: O(n) → очень медленно (~0.5 с), порядок не влияет. \n", + " - Удаление: O(n) → медленно.\n", + "\n", + "2. **Хеш-таблица:** \n", + " - Вставка: O(1) в среднем → ~0.008 с, порядок данных не влияет. \n", + " - Поиск: O(1) → ~0.002 с, самый быстрый. \n", + " - Удаление: O(1) → ~0.002 с.\n", + "\n", + "3. **Двоичное дерево поиска:** \n", + " - На случайных данных: O(log n) → вставка ~0.018 с, поиск ~0.0015 с, удаление ~0.0016 с. \n", + " - На отсортированных данных: дерево вырождается в линейный список → вставка ~2.3 с, поиск и удаление также становятся O(n) (на графиках виден рост времени).\n", + "\n", + "**ИТОГОВЫЙ ВЫВОД:** \n", + "- Для частого поиска, вставки и удаления – лучший выбор **хеш-таблица**. \n", + "- Если данные поступают в отсортированном порядке – BST не подходит из‑за деградации. \n", + "- Если нужна частая выдача записей в алфавитном порядке и данные случайны – BST хорош. \n", + "- Связный список неэффективен для больших объёмов." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b39f136-0c95-46f0-b794-7136232bcd3c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/filippovavm/laba1/фулкод1.ipynb b/filippovavm/laba1/фулкод1.ipynb new file mode 100644 index 0000000..db48b2b --- /dev/null +++ b/filippovavm/laba1/фулкод1.ipynb @@ -0,0 +1,490 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "65b0def8-04cf-4cfd-b4d4-bc862e120497", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Генерация данных...\n", + "\n", + "Измерение вставки для Связного списка...\n" + ] + } + ], + "source": [ + "import time\n", + "import random\n", + "import csv\n", + "import sys\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "# Связный список\n", + "def ll_insert(head, name, phone):\n", + " current = head\n", + " prev = None\n", + " while current is not None:\n", + " if current['name'] == name:\n", + " current['phone'] = phone\n", + " return head\n", + " prev = current\n", + " current = current['next']\n", + " new_node = {'name': name, 'phone': phone, 'next': None}\n", + " if prev is None:\n", + " return new_node\n", + " else:\n", + " prev['next'] = new_node\n", + " return head\n", + "\n", + "def ll_find(head, name):\n", + " current = head\n", + " while current is not None:\n", + " if current['name'] == name:\n", + " return current['phone']\n", + " current = current['next']\n", + " return None\n", + "\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", + "\n", + "# Хеш-таблица \n", + "def hash_function(name, size):\n", + " total = 0\n", + " for ch in name:\n", + " total = (total * 31 + ord(ch)) % size\n", + " return total\n", + "\n", + "def ht_create(size=2000):\n", + " return [None] * size\n", + "\n", + "def ht_insert(buckets, name, phone):\n", + " idx = hash_function(name, len(buckets))\n", + " buckets[idx] = ll_insert(buckets[idx], name, phone)\n", + "\n", + "def ht_find(buckets, name):\n", + " idx = hash_function(name, len(buckets))\n", + " return ll_find(buckets[idx], name)\n", + "\n", + "def ht_delete(buckets, name):\n", + " idx = hash_function(name, len(buckets))\n", + " buckets[idx] = ll_delete(buckets[idx], name)\n", + "\n", + "# Двоичное дерево поиска \n", + "def bst_insert(root, name, phone):\n", + " new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}\n", + " if root is None:\n", + " return new_node\n", + " current = root\n", + " while True:\n", + " if name < current['name']:\n", + " if current['left'] is None:\n", + " current['left'] = new_node\n", + " break\n", + " current = current['left']\n", + " elif name > current['name']:\n", + " if current['right'] is None:\n", + " current['right'] = new_node\n", + " break\n", + " current = current['right']\n", + " else:\n", + " current['phone'] = phone\n", + " break\n", + " return root\n", + "\n", + "def bst_find(root, name):\n", + " current = root\n", + " while current is not None:\n", + " if name < current['name']:\n", + " current = current['left']\n", + " elif name > current['name']:\n", + " current = current['right']\n", + " else:\n", + " return current['phone']\n", + " return None\n", + "\n", + "def bst_find_min(node):\n", + " while node['left'] is not None:\n", + " node = node['left']\n", + " return node\n", + "\n", + "def bst_delete(root, name):\n", + " parent = None\n", + " current = root\n", + " while current is not None and current['name'] != name:\n", + " parent = current\n", + " if name < current['name']:\n", + " current = current['left']\n", + " else:\n", + " current = current['right']\n", + " if current is None:\n", + " return root\n", + " \n", + " if current['left'] is None and current['right'] is None:\n", + " if parent is None:\n", + " return None\n", + " if parent['left'] is current:\n", + " parent['left'] = None\n", + " else:\n", + " parent['right'] = None\n", + " return root\n", + " \n", + " if current['left'] is None:\n", + " if parent is None:\n", + " return current['right']\n", + " if parent['left'] is current:\n", + " parent['left'] = current['right']\n", + " else:\n", + " parent['right'] = current['right']\n", + " return root\n", + " if current['right'] is None:\n", + " if parent is None:\n", + " return current['left']\n", + " if parent['left'] is current:\n", + " parent['left'] = current['left']\n", + " else:\n", + " parent['right'] = current['left']\n", + " return root\n", + " \n", + " succ_parent = current\n", + " succ = current['right']\n", + " while succ['left'] is not None:\n", + " succ_parent = succ\n", + " succ = succ['left']\n", + " current['name'] = succ['name']\n", + " current['phone'] = succ['phone']\n", + " if succ_parent['left'] is succ:\n", + " succ_parent['left'] = succ['right']\n", + " else:\n", + " succ_parent['right'] = succ['right']\n", + " return root\n", + "\n", + "# Генерация данных \n", + "def generate_records(N):\n", + " records = []\n", + " for i in range(N):\n", + " name = f\"User_{i:05d}\"\n", + " phone = f\"+7-999-{random.randint(1000000, 9999999)}\"\n", + " records.append((name, phone))\n", + " return records\n", + "\n", + "# Замеры\n", + "REPEATS = 5\n", + "N = 10000\n", + "\n", + "def measure_insert(struct, records, repeats=REPEATS):\n", + " times = []\n", + " for _ in range(repeats):\n", + " if struct == 'll':\n", + " head = None\n", + " start = time.perf_counter()\n", + " for name, phone in records:\n", + " head = ll_insert(head, name, phone)\n", + " end = time.perf_counter()\n", + " elif struct == 'ht':\n", + " buckets = ht_create(2000)\n", + " start = time.perf_counter()\n", + " for name, phone in records:\n", + " ht_insert(buckets, name, phone)\n", + " end = time.perf_counter()\n", + " elif struct == 'bst':\n", + " root = None\n", + " start = time.perf_counter()\n", + " for name, phone in records:\n", + " root = bst_insert(root, name, phone)\n", + " end = time.perf_counter()\n", + " times.append(end - start)\n", + " return times\n", + "\n", + "def build_structure(struct, records):\n", + " if struct == 'll':\n", + " head = None\n", + " for name, phone in records:\n", + " head = ll_insert(head, name, phone)\n", + " return head\n", + " elif struct == 'ht':\n", + " buckets = ht_create(2000)\n", + " for name, phone in records:\n", + " ht_insert(buckets, name, phone)\n", + " return buckets\n", + " elif struct == 'bst':\n", + " root = None\n", + " for name, phone in records:\n", + " root = bst_insert(root, name, phone)\n", + " return root\n", + "\n", + "def measure_find_on_structure(struct, structure, records, repeats=REPEATS):\n", + " times = []\n", + " N = len(records)\n", + " for _ in range(repeats):\n", + " indices = random.sample(range(N), 100)\n", + " exist = [records[i][0] for i in indices]\n", + " missing = [f\"None_{i}\" for i in range(10)]\n", + " search = exist + missing\n", + " start = time.perf_counter()\n", + " if struct == 'll':\n", + " for name in search:\n", + " ll_find(structure, name)\n", + " elif struct == 'ht':\n", + " for name in search:\n", + " ht_find(structure, name)\n", + " elif struct == 'bst':\n", + " for name in search:\n", + " bst_find(structure, name)\n", + " times.append(time.perf_counter() - start)\n", + " return times\n", + "\n", + "def measure_delete_on_structure(struct, records, repeats=REPEATS):\n", + " times = []\n", + " N = len(records)\n", + " for _ in range(repeats):\n", + " indices = random.sample(range(N), 50)\n", + " del_names = [records[i][0] for i in indices]\n", + " if struct == 'll':\n", + " head = None\n", + " for name, phone in records:\n", + " head = ll_insert(head, name, phone)\n", + " start = time.perf_counter()\n", + " for name in del_names:\n", + " head = ll_delete(head, name)\n", + " end = time.perf_counter()\n", + " elif struct == 'ht':\n", + " buckets = ht_create(2000)\n", + " for name, phone in records:\n", + " ht_insert(buckets, name, phone)\n", + " start = time.perf_counter()\n", + " for name in del_names:\n", + " ht_delete(buckets, name)\n", + " end = time.perf_counter()\n", + " elif struct == 'bst':\n", + " root = None\n", + " for name, phone in records:\n", + " root = bst_insert(root, name, phone)\n", + " start = time.perf_counter()\n", + " for name in del_names:\n", + " root = bst_delete(root, name)\n", + " end = time.perf_counter()\n", + " times.append(end - start)\n", + " return times\n", + "\n", + "# Основная функция \n", + "def main():\n", + " print(\"Генерация данных...\")\n", + " records = generate_records(N)\n", + " random.shuffle(records) # случайный порядок\n", + " records_sorted = sorted(records, key=lambda x: x[0]) # отсортированный\n", + "\n", + " results = [] # для CSV\n", + " struct_names = {'ll': 'Связного списка', 'ht': 'Хеш-таблицы', 'bst': 'Двоичного дерева поиска'}\n", + " mode_names = {'shuffled': 'случайный', 'sorted': 'отсортированный'}\n", + " op_names = {'insert': 'Вставка всех записей', 'find': 'Поиск записей', 'delete': 'Удаление записей'}\n", + "\n", + " # для графиков\n", + " insert_sh = {} # {struct: [times]}\n", + " insert_so = {}\n", + " find_sh = {}\n", + " find_so = {}\n", + " delete_sh = {}\n", + " delete_so = {}\n", + "\n", + " # Вставка \n", + " for struct in ['ll', 'ht', 'bst']:\n", + " print(f\"\\nИзмерение вставки для {struct_names[struct]}...\")\n", + " times_sh = measure_insert(struct, records)\n", + " times_so = measure_insert(struct, records_sorted)\n", + " insert_sh[struct] = times_sh\n", + " insert_so[struct] = times_so\n", + " print(f\" случайный: {[round(t,6) for t in times_sh]}, среднее = {sum(times_sh)/len(times_sh):.6f}\")\n", + " print(f\" отсортированный: {[round(t,6) for t in times_so]}, среднее = {sum(times_so)/len(times_so):.6f}\")\n", + " results.append([struct_names[struct], mode_names['shuffled'], op_names['insert'], sum(times_sh)/len(times_sh)] + times_sh)\n", + " results.append([struct_names[struct], mode_names['sorted'], op_names['insert'], sum(times_so)/len(times_so)] + times_so)\n", + "\n", + " # Поиск \n", + " for struct in ['ll', 'ht', 'bst']:\n", + " print(f\"\\nПоиск для {struct_names[struct]} на случайных данных...\")\n", + " structure_sh = build_structure(struct, records)\n", + " times_find_sh = measure_find_on_structure(struct, structure_sh, records)\n", + " find_sh[struct] = times_find_sh\n", + " print(f\" случайный: {[round(t,6) for t in times_find_sh]}, среднее = {sum(times_find_sh)/len(times_find_sh):.6f}\")\n", + " results.append([struct_names[struct], mode_names['shuffled'], op_names['find'], sum(times_find_sh)/len(times_find_sh)] + times_find_sh)\n", + "\n", + " print(f\"Поиск для {struct_names[struct]} на отсортированных данных...\")\n", + " structure_so = build_structure(struct, records_sorted)\n", + " times_find_so = measure_find_on_structure(struct, structure_so, records_sorted)\n", + " find_so[struct] = times_find_so\n", + " print(f\" отсортированный: {[round(t,6) for t in times_find_so]}, среднее = {sum(times_find_so)/len(times_find_so):.6f}\")\n", + " results.append([struct_names[struct], mode_names['sorted'], op_names['find'], sum(times_find_so)/len(times_find_so)] + times_find_so)\n", + "\n", + " # Удаление \n", + " for struct in ['ll', 'ht', 'bst']:\n", + " print(f\"\\nУдаление для {struct_names[struct]} на случайных данных...\")\n", + " times_del_sh = measure_delete_on_structure(struct, records)\n", + " delete_sh[struct] = times_del_sh\n", + " print(f\" случайный: {[round(t,6) for t in times_del_sh]}, среднее = {sum(times_del_sh)/len(times_del_sh):.6f}\")\n", + " results.append([struct_names[struct], mode_names['shuffled'], op_names['delete'], sum(times_del_sh)/len(times_del_sh)] + times_del_sh)\n", + "\n", + " print(f\"Удаление для {struct_names[struct]} на отсортированных данных...\")\n", + " times_del_so = measure_delete_on_structure(struct, records_sorted)\n", + " delete_so[struct] = times_del_so\n", + " print(f\" отсортированный: {[round(t,6) for t in times_del_so]}, среднее = {sum(times_del_so)/len(times_del_so):.6f}\")\n", + " results.append([struct_names[struct], mode_names['sorted'], op_names['delete'], sum(times_del_so)/len(times_del_so)] + times_del_so)\n", + "\n", + " # Сохраняем CSV\n", + " with open(\"phonebook_results.csv\", \"w\", newline=\"\", encoding=\"utf-8\") as f:\n", + " writer = csv.writer(f)\n", + " writer.writerow(['Структура', 'Режим', 'Операция', 'Среднее', 'Замер1', 'Замер2', 'Замер3', 'Замер4', 'Замер5'])\n", + " writer.writerows(results)\n", + "\n", + " # Построение графиков \n", + " try:\n", + " # График вставки\n", + " fig1, ax1 = plt.subplots(figsize=(10,6))\n", + " x = np.arange(3)\n", + " width = 0.35\n", + " means_sh = [sum(insert_sh[s])/len(insert_sh[s]) for s in ['ll','ht','bst']]\n", + " means_so = [sum(insert_so[s])/len(insert_so[s]) for s in ['ll','ht','bst']]\n", + " rects1 = ax1.bar(x - width/2, means_sh, width, label='Случайный порядок', color='skyblue')\n", + " rects2 = ax1.bar(x + width/2, means_so, width, label='Отсортированный порядок', color='salmon')\n", + " ax1.set_ylabel('Время (сек)')\n", + " ax1.set_title('Вставка всех записей')\n", + " ax1.set_xticks(x)\n", + " ax1.set_xticklabels(['Связный список', 'Хеш-таблица', 'Двоичное дерево'])\n", + " ax1.legend()\n", + " ax1.set_yscale('log')\n", + " for rect in rects1 + rects2:\n", + " h = rect.get_height()\n", + " ax1.annotate(f'{h:.3f}', xy=(rect.get_x()+rect.get_width()/2, h),\n", + " xytext=(0,3), textcoords=\"offset points\", ha='center', va='bottom', fontsize=8)\n", + " plt.tight_layout()\n", + " plt.savefig('insert_comparison.png')\n", + " plt.show()\n", + "\n", + " # График поиска\n", + " fig2, ax2 = plt.subplots(figsize=(10,6))\n", + " means_find_sh = [sum(find_sh[s])/len(find_sh[s]) for s in ['ll','ht','bst']]\n", + " means_find_so = [sum(find_so[s])/len(find_so[s]) for s in ['ll','ht','bst']]\n", + " rects1 = ax2.bar(x - width/2, means_find_sh, width, label='Случайный порядок', color='skyblue')\n", + " rects2 = ax2.bar(x + width/2, means_find_so, width, label='Отсортированный порядок', color='salmon')\n", + " ax2.set_ylabel('Время (сек)')\n", + " ax2.set_title('Поиск (100 существующих + 10 отсутствующих)')\n", + " ax2.set_xticks(x)\n", + " ax2.set_xticklabels(['Связный список', 'Хеш-таблица', 'Двоичное дерево'])\n", + " ax2.legend()\n", + " for rect in rects1 + rects2:\n", + " h = rect.get_height()\n", + " ax2.annotate(f'{h:.5f}', xy=(rect.get_x()+rect.get_width()/2, h),\n", + " xytext=(0,3), textcoords=\"offset points\", ha='center', va='bottom', fontsize=8)\n", + " plt.tight_layout()\n", + " plt.savefig('find_comparison.png')\n", + " plt.show()\n", + "\n", + " # График удаления \n", + " fig3, ax3 = plt.subplots(figsize=(10,6))\n", + " means_del_sh = [sum(delete_sh[s])/len(delete_sh[s]) for s in ['ll','ht','bst']]\n", + " means_del_so = [sum(delete_so[s])/len(delete_so[s]) for s in ['ll','ht','bst']]\n", + " rects1 = ax3.bar(x - width/2, means_del_sh, width, label='Случайный порядок', color='skyblue')\n", + " rects2 = ax3.bar(x + width/2, means_del_so, width, label='Отсортированный порядок', color='salmon')\n", + " ax3.set_ylabel('Время (сек)')\n", + " ax3.set_title('Удаление 50 случайных записей')\n", + " ax3.set_xticks(x)\n", + " ax3.set_xticklabels(['Связный список', 'Хеш-таблица', 'Двоичное дерево'])\n", + " ax3.legend()\n", + " for rect in rects1 + rects2:\n", + " h = rect.get_height()\n", + " ax3.annotate(f'{h:.5f}', xy=(rect.get_x()+rect.get_width()/2, h),\n", + " xytext=(0,3), textcoords=\"offset points\", ha='center', va='bottom', fontsize=8)\n", + " plt.tight_layout()\n", + " plt.savefig('delete_comparison.png')\n", + " plt.show()\n", + "\n", + " print(\"Графики сохранены: insert_comparison.png, find_comparison.png, delete_comparison.png\")\n", + " except Exception as e:\n", + " print(f\"Не удалось построить графики: {e}\")\n", + " # Графики замеров\n", + " try:\n", + " def plot_attempts(data_sh, data_so, op_name):\n", + " fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))\n", + " # случайный порядок\n", + " for struct, label, color, marker in [('ll','LinkedList','red','o'), ('ht','HashTable','green','s'), ('bst','BST','blue','^')]:\n", + " times = data_sh[struct]\n", + " x = range(1, len(times)+1)\n", + " ax1.plot(x, times, marker=marker, color=color, label=label, linestyle='--', linewidth=1)\n", + " ax1.scatter(x, times, color=color, s=60, zorder=5)\n", + " ax1.set_xlabel('Номер попытки')\n", + " ax1.set_ylabel('Время (сек)')\n", + " ax1.set_title(f'{op_name} – случайный порядок')\n", + " ax1.legend()\n", + " ax1.grid(True, linestyle=':', alpha=0.7)\n", + " # отсортированный порядок\n", + " for struct, label, color, marker in [('ll','LinkedList','red','o'), ('ht','HashTable','green','s'), ('bst','BST','blue','^')]:\n", + " times = data_so[struct]\n", + " x = range(1, len(times)+1)\n", + " ax2.plot(x, times, marker=marker, color=color, label=label, linestyle='--', linewidth=1)\n", + " ax2.scatter(x, times, color=color, s=60, zorder=5)\n", + " ax2.set_xlabel('Номер попытки')\n", + " ax2.set_ylabel('Время (сек)')\n", + " ax2.set_title(f'{op_name} – отсортированный порядок')\n", + " ax2.legend()\n", + " ax2.grid(True, linestyle=':', alpha=0.7)\n", + " plt.tight_layout()\n", + " plt.savefig(f'{op_name}_5attempts.png')\n", + " plt.show()\n", + " \n", + " plot_attempts(insert_sh, insert_so, 'insert')\n", + " plot_attempts(find_sh, find_so, 'find')\n", + " plot_attempts(delete_sh, delete_so, 'delete')\n", + " print(\"Дополнительные графики сохранены: insert_5attempts.png, find_5attempts.png, delete_5attempts.png\")\n", + " except Exception as e:\n", + " print(f\"Не удалось построить дополнительные графики: {e}\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " sys.setrecursionlimit(20000)\n", + " main()\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cead201d-1150-463f-9ff3-a4bed6f7fc03", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}