From 5959566f59955d369b0bbe4f9212893a24bca5d0 Mon Sep 17 00:00:00 2001 From: HUAWEI Date: Fri, 22 May 2026 13:05:33 +0300 Subject: [PATCH 1/2] add lab1 --- filippovavm/laba1/МП.ipynb | 594 ++++++++++++++++++++++++++++++++ filippovavm/laba1/отчёт1.ipynb | 149 ++++++++ filippovavm/laba1/фулкод1.ipynb | 490 ++++++++++++++++++++++++++ 3 files changed, 1233 insertions(+) create mode 100644 filippovavm/laba1/МП.ipynb create mode 100644 filippovavm/laba1/отчёт1.ipynb create mode 100644 filippovavm/laba1/фулкод1.ipynb 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 +} -- 2.43.0 From 68d1dfa68ed290b08cd736200fee0b7be2d52969 Mon Sep 17 00:00:00 2001 From: HUAWEI Date: Sun, 24 May 2026 18:27:35 +0300 Subject: [PATCH 2/2] [1]laba 1 --- filippovavm/docs/data/plot_empty.txt.png | Bin 0 -> 19919 bytes filippovavm/docs/data/plot_large.txt.png | Bin 0 -> 20896 bytes filippovavm/docs/data/plot_medium.txt.png | Bin 0 -> 21332 bytes filippovavm/docs/data/plot_no_exit.txt.png | Bin 0 -> 19940 bytes filippovavm/docs/data/plot_tiny.txt.png | Bin 0 -> 21194 bytes .../{laba1 => docs/data}/фулкод1.ipynb | 0 filippovavm/{ => docs}/laba1/МП.ipynb | 0 filippovavm/{ => docs}/laba1/отчёт1.ipynb | 0 filippovavm/docs/laba1/фулкод1.ipynb | 490 ++++++++++++++ filippovavm/docs/laba2/all_results.csv | 13 + filippovavm/docs/laba2/empty.txt | 99 +++ filippovavm/docs/laba2/large.txt | 50 ++ filippovavm/docs/laba2/medium.txt | 48 ++ filippovavm/docs/laba2/no_exit.txt | 20 + filippovavm/docs/laba2/plot_empty.txt.png | Bin 0 -> 19381 bytes filippovavm/docs/laba2/plot_large.txt.png | Bin 0 -> 18762 bytes filippovavm/docs/laba2/plot_no_exit.txt.png | Bin 0 -> 20517 bytes filippovavm/docs/laba2/plot_tiny.txt.png | Bin 0 -> 23118 bytes filippovavm/docs/laba2/summary_comparison.png | Bin 0 -> 54400 bytes filippovavm/docs/laba2/tiny.txt | 11 + filippovavm/docs/laba2/мп2_1.ipynb | 631 ++++++++++++++++++ filippovavm/docs/laba2/отчет2.ipynb | 375 +++++++++++ filippovavm/docs/laba2/фулкод2.ipynb | 352 ++++++++++ filippovavm/docs/МП.ipynb | 594 +++++++++++++++++ filippovavm/docs/отчёт1.ipynb | 149 +++++ 25 files changed, 2832 insertions(+) create mode 100644 filippovavm/docs/data/plot_empty.txt.png create mode 100644 filippovavm/docs/data/plot_large.txt.png create mode 100644 filippovavm/docs/data/plot_medium.txt.png create mode 100644 filippovavm/docs/data/plot_no_exit.txt.png create mode 100644 filippovavm/docs/data/plot_tiny.txt.png rename filippovavm/{laba1 => docs/data}/фулкод1.ipynb (100%) rename filippovavm/{ => docs}/laba1/МП.ipynb (100%) rename filippovavm/{ => docs}/laba1/отчёт1.ipynb (100%) create mode 100644 filippovavm/docs/laba1/фулкод1.ipynb create mode 100644 filippovavm/docs/laba2/all_results.csv create mode 100644 filippovavm/docs/laba2/empty.txt create mode 100644 filippovavm/docs/laba2/large.txt create mode 100644 filippovavm/docs/laba2/medium.txt create mode 100644 filippovavm/docs/laba2/no_exit.txt create mode 100644 filippovavm/docs/laba2/plot_empty.txt.png create mode 100644 filippovavm/docs/laba2/plot_large.txt.png create mode 100644 filippovavm/docs/laba2/plot_no_exit.txt.png create mode 100644 filippovavm/docs/laba2/plot_tiny.txt.png create mode 100644 filippovavm/docs/laba2/summary_comparison.png create mode 100644 filippovavm/docs/laba2/tiny.txt create mode 100644 filippovavm/docs/laba2/мп2_1.ipynb create mode 100644 filippovavm/docs/laba2/отчет2.ipynb create mode 100644 filippovavm/docs/laba2/фулкод2.ipynb create mode 100644 filippovavm/docs/МП.ipynb create mode 100644 filippovavm/docs/отчёт1.ipynb diff --git a/filippovavm/docs/data/plot_empty.txt.png b/filippovavm/docs/data/plot_empty.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..a263190fa9c1fef5ef4d0457a4dad37aff5a6024 GIT binary patch literal 19919 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 NI(zBN+f!F=|1XfuI*|YX literal 0 HcmV?d00001 diff --git a/filippovavm/docs/data/plot_large.txt.png b/filippovavm/docs/data/plot_large.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..09b7103f557806b136b5ef16f47fb1d6fe52cb12 GIT binary patch literal 20896 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 Coo!VB literal 0 HcmV?d00001 diff --git a/filippovavm/docs/data/plot_medium.txt.png b/filippovavm/docs/data/plot_medium.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..6c08710790457f267a4408143cd07fff58ece2ee GIT binary patch literal 21332 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ξ)P_K5%-O(=S`WcCa5X2LB!?J8uncG>I31^&s!?5^N5SlpZc=x_f7u@aW_uKbDB#`II*LENwqe^FrK|?T=pYeo`_jV2rK{%s? z6-mM)Jsh8cmIy*;5sb653)25Y-U`M5fG^Rj?{n}JmMZY1o%qj+IbfoI?wu@E{rtwOWKoqht%9=<=Svzt#NR z#!~2>7lF7uAmbWd28jyf zCKlP+)mJ-a$2y6pjXa@>Jhnm81+o-5*qH4rpKLyo_hi;(abP#)!#HtnA-ManY1PDx z2*?lvh)kTM9DQgX$RsO_Mpl#GFTs&^Buv^P_c@FU{csb-Xk?}W#ZOh9=jnr)XC77i z08%ze?;&_1unAR&Gcr-`61bAPV5U=RM;lv07OtTGrN3q+Dh+x17~rk~UYf+yh6Vtc zBo6hFw54;$6&9a>R@czZ_xOs7B4rn($pEZS{z%XqoG{=M%P4fa6jc2W?8EDF*l8K{ o#AI`gw|NcBME|)S#keSWwX8L{*xz+02tta?Y1vb8C$HW9FEU~UZ~y=R literal 0 HcmV?d00001 diff --git a/filippovavm/docs/data/plot_no_exit.txt.png b/filippovavm/docs/data/plot_no_exit.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..37e1f9cca3afe77c224962549db72de7ed272142 GIT binary patch literal 19940 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 literal 0 HcmV?d00001 diff --git a/filippovavm/docs/data/plot_tiny.txt.png b/filippovavm/docs/data/plot_tiny.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..b8b5788cec7cfabc6a5ab6a26804d0c679fe2694 GIT binary patch literal 21194 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 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 +} diff --git a/filippovavm/docs/laba2/all_results.csv b/filippovavm/docs/laba2/all_results.csv new file mode 100644 index 0000000..8378c4c --- /dev/null +++ b/filippovavm/docs/laba2/all_results.csv @@ -0,0 +1,13 @@ +maze,algorithm,avg_time_ms,avg_visited,avg_path_len +tiny.txt,BFSStrategy,0.03461998421698809,6.0,4.0 +tiny.txt,DFSStrategy,0.021119997836649418,5.0,6.0 +tiny.txt,AStarStrategy,0.05429997108876705,6.0,4.0 +medium.txt,BFSStrategy,0.1677999971434474,39.0,16.0 +medium.txt,DFSStrategy,0.18643999937921762,44.0,18.0 +medium.txt,AStarStrategy,0.2677599899470806,39.0,16.0 +large.txt,BFSStrategy,12.41911998949945,2500.0,99.0 +large.txt,DFSStrategy,127.24694001954049,2450.0,2451.0 +large.txt,AStarStrategy,17.408600030466914,2500.0,99.0 +empty.txt,BFSStrategy,0.33364000264555216,100.0,100.0 +empty.txt,DFSStrategy,0.37696000654250383,99.0,100.0 +empty.txt,AStarStrategy,0.4786999896168709,100.0,100.0 diff --git a/filippovavm/docs/laba2/empty.txt b/filippovavm/docs/laba2/empty.txt new file mode 100644 index 0000000..3f96ec2 --- /dev/null +++ b/filippovavm/docs/laba2/empty.txt @@ -0,0 +1,99 @@ +S E + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filippovavm/docs/laba2/large.txt b/filippovavm/docs/laba2/large.txt new file mode 100644 index 0000000..aed9a99 --- /dev/null +++ b/filippovavm/docs/laba2/large.txt @@ -0,0 +1,50 @@ +S E + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filippovavm/docs/laba2/medium.txt b/filippovavm/docs/laba2/medium.txt new file mode 100644 index 0000000..964a07b --- /dev/null +++ b/filippovavm/docs/laba2/medium.txt @@ -0,0 +1,48 @@ +################################################## +#S # +# ############## ####################### # +# # # # # # +# # ####### # # ################ # # # +# # # # # # # # # # # +# # # ### # # # # ########### # # # # +# # # # # # # # # # # # # # # +# # # # # # # # # # ###### # # # # # +# # # # # # # # # # # # # # # # # +# # # # # # # # # # # ## # # # # # # +# # # # # # # # # # # # # # # # # +# # # # # # # # # # ###### # # # # # +# # # # # # # # # # # # # # # +# # # # # # # # # ########### # # # # +# # # # # # # # # # # # # +# # # # # # # # ################ # # # +# # # # # # # # # # +# # # # # # # ####################### # +# # # # # # # # +# # # # # # ################################### +# # # # # # # +# # # # # ####################################### +# # # # # # +# # # # ######################################### +# # # # # +# # # ########################################### +# # # # +# # ############################################# +# # # +# ################################################ +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +################################################## \ No newline at end of file diff --git a/filippovavm/docs/laba2/no_exit.txt b/filippovavm/docs/laba2/no_exit.txt new file mode 100644 index 0000000..84f1a27 --- /dev/null +++ b/filippovavm/docs/laba2/no_exit.txt @@ -0,0 +1,20 @@ +#################### +#S # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +###################E \ No newline at end of file diff --git a/filippovavm/docs/laba2/plot_empty.txt.png b/filippovavm/docs/laba2/plot_empty.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..cfaf69a7a4140037793c0be1a107b6c83c7f12d0 GIT binary patch literal 19381 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 KT*{fNfBZjOUD2HY literal 0 HcmV?d00001 diff --git a/filippovavm/docs/laba2/plot_large.txt.png b/filippovavm/docs/laba2/plot_large.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..adf531bfc989431a460da4b2a4eca2a953e49cdd GIT binary patch literal 18762 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 IapOP!7dvfmCIA2c literal 0 HcmV?d00001 diff --git a/filippovavm/docs/laba2/plot_no_exit.txt.png b/filippovavm/docs/laba2/plot_no_exit.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..f545d59da4d881412dfc2b4bc721e070b50c8084 GIT binary patch literal 20517 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%0A%@EVuauLakOd8HE3N8F;@>0Hb}^I z*(u5yojnm8W-RohmK+;nb|qMLI@>Lmejmfbm?9R0^d`-^XEI{3pj6Z#rxSr3?C)d+ zM`1r|?tG2w@+$;m9Qev?jY9|~wTdZ9e@{;&PM1Um-Zh?RA>ab~F)|V_3TdSCQ$z94 zU$cLeR5oRY?H4h0Dx@1J9GAqkri2K8i|@q$dE)p#bQhJ>#=9*{4fzdE0zUT|2tqBW zC3FdtrE@MWE`+WJ>6;i66Jydxh7){ZG!BEbZ!*E+(fD@H0|zX7AuK@O$N?Dwz8sO* z$7gZ{3LYX5-N!O780uV{eoZK*gB(C79$*bZVMDC(Ei816nt;Nt1&pYKzQn6vO=sGc zb(M%0UGMHSz?GvmK9AcNIqSRrqy#C!81@fIZ2IlDPvE+MN@IgmOTcVvgpvYDx*v2z z@HZh+eF0p$4~X1=IxFE#25e&Hfp|zNTxTPz=!Q?w4^TvJipgBQ+z8rd`KKiWoZ}MB zErf7CWcNbEF)X$O;LK=m`UA>b501WZw(7XlStCD4_zjE1P%;948OicCSU z?MLFc8V8h6gL{zyJL8(saX9$NlNT7U{4(&b$cTu37%>8>x&H%xggXeCs$5n}`BLcW zx|RCGNlcp#;H(?)N1}cBacC)mlU`}$a)iAIk=~D>%D&z)RKv|vqSF#;269!%aXMk* zCIrSpg~N!X=eWxhBo29$#9sxAjzYYmbZx`LYA6KGbrp_~`qLr&gPXaGGF_8FSmozW zo^;mQetW;s-ob&8sCD*`%#mzXb(l~nZ4#`=4v?C4t?84a9pa@GCrbK3#55t6(=p(l zJAc02A{1;Pn@Z~YNK;I)gscVUWtnG&OLE;Oxuz46V`_+Is!kalTQmYAP}nZ100h>$ zwQB`tegFm%-oCb>IFra2=*LGSKn>KrzrV8qwg;Qx76m~0UuK$Z`_V%dgHWyxA37A= z*Gi+!wHO>282G9O(z1%Te6psf-!xHLKwWu9%+B8OE25#HiPf#}jWzEmAS4@ve&U5P zTZK1j3yoZrf;kyaQW2z31G3rv)~&_Fo(QeHry*c}bPeLwGLt}J%x5-kq)^7pbDO{`9tWkh7Od(0Vxx5y^ON!d3x;nYXBIAFe4924HG4t{&;88Rn zViNyR)MzPn8sK-0o01wJ9YAGCK#l*ae5;{}pwh?Vvo=vtH8p9uCPM!*fD%cN)pK1u zmMmULj@7ZM>2VMCawH{b`}Xizve1omQeIVWOh#DC3`tE<#9B`n*&3PaAE8# z07i$Q#LNU%OwUE6ao}BIkQ5T|?L19RPEO0eUhGc#^S$TfDmf%;R;hzUCv-WmolxNh zWpCom0`2(qT{$@`zeq=lOsGgKkfn}6p+(mn$pg5+qC{;&N&*UqFT2K|Qz-pB@#`VT z;>-VPZ}h9_d5wHPhR4B0cN`4_V+ra>?ykLiqfvfTabXm<)>Lb%Yl*7qZ}6-%@@`!P z``L)$XxEM%FM%7t^(+7VvKNdBVD(nuANoZapz~3=fkPq%2;l87e0DgW0aSON=;)qE za%V>*#Bp znI%ZI3F%mU!}BR4rqA-Zwl0ffyu1y8he+CcqMbszO8+ZDbnrBg3;%4MHLHBc$T0|J z&pT;kyHnS_y*#?*yZXYf;RD)K5x!_K=v=pFye}0X{JqsG5M%_DzOZvB-)eyxRg(f8FT ze5kPv*;e{QoB&=2<@|&(_J28pM%QG)B8qM%L)?Dw0%o8!Xcn$4=l(OtW+-5rWzh}G zKDl(gTjzHYn8Z!36uF=8V8f=7Q$HsnI!Uy8U}44QT|a?l7Vj$IR4n>{g&QGfEGKYv!2Lawlpo4^S#wgS)m`F9GU zF{4&Q;Yz+vI^nLaB8pdj{(>HCtcBYrM+;YyU{4|B5=;|+Fd71Eql99dSP69;xoTh# zL(60E0x*$JiPY_D0DGPIxdI$gORz_%rd+#S9VX5pL54{Pm=0=9`)O(8V5BudU!VW> zkYm^G-I0XvM;lW4|Dpg7w+&mUU+}lzIs+^#ck-lLT0S5>t+>mgsfHcopqDRS2G?k7 zYun6@UH78lK2PRm>(HyO&J%}dW;#2mOI65pVY`uf=*hw1`Dj59Dy);5#VghD-er7b8xDvK`oFPhc*3DO%4g*Kh%{!K(d=cvQ#4a}HguHAi zaLy%`U}&XA1X?&u#^}|AvPlr)4N}9d{reMCY~{(+WfQYtS>SybYKJRf_b0Vs#z0|XhlmZ%Nj-hp)9LMz?pI{!!NRj~2S>1W1{H_OWJa(yGK)7VGZB;aA&& zdOFsLU~Jh9y(V%P;Dcznta0S3^U<6}b4SHsVtF4q4D-ky1%z4WG8!Mj5pcQehT%LV zJ^Q z4=F<^5AGfwxgw7-gp;v|%=o~yayUPE&;%qS_b8Ad%7s;YZEyfss*Y^CPsPQ>-^dWw z_OR#IfJhwpB=2DvV8#PL$KAATn<4~|1oj%)f8{qQ7IJ7TFb~FIW+gQjgEV-d!+@Lp zUky~YmFOC(1jeh>$>#G?80tL>8_!1m3 zI6k5mHM**R>l-1tOiXOj(p8^aU~0jqDF4k7@WIlLNy$pgPJj;MV;-?f01Bxo;Yi)~ zr6Eu-B>o!hKspl@m6%96To$D7ZQ5On9(d!Q$bdq)D6dr;XKGCX`UV{kxSQz zgLK`BJzx>!yeC1X55b?-1Ut+dOe}r{c;###+7@QTCyePbv$z zz@bt2Yd|&l`s(!zBAZRhYOM%w{A7W3quW31dEgBEueX4(+>L4mSgvw~1`lE(jgj(b zAOB_a6NPKnuDKh6)^R|AnS?5>`u8iAVbeDPndMuDKuCa(DXen z{388e(EDC0={&yZ4m0t4f~xu{+J1$EPOsIEb(ASx{Q)l(o3 zNO$>#yY8;vDvNj`DEQtVf3($eH6^M=1N@M|9&ZQ#UQU+?#t;R}@J`Oo7_ynA1Gsw3 z3s8~VT&XGwHZqEcsN>4&Fj~EVFPui(x=F(>_T=r=uEzFz+10PWvkjg5C{+Jm_obQ9 zj%37mJl>HQMEKxZBZdrgIl({%gj+{>ZStLG5yT(?=5OVeAQVA#zADQ$S4c`^wF2z) zQ3+VyC`@pa z3DlfGjRdxXJ}DnH;RzQGo@(_4$by!y zGJg*@)g$;8TeGDpsK9x|wubzb%y9_vBY6y+#5;+;z!dh*G}lbCuhwIorwDyO9xlxL zJOBjwleLnHK2(Tc zsNuUmHntJ z30-?ujiD^Jr0PEWMN_qB-PCr5gSWZb5e^a56?r0_K;m!|u0S)$I($o2qbSh=taM~l z6sUpsk)p^3G|hnOoqs`H;@Cspz;gSz9xgQ^2L6Rqi9mYfh+x=JCZ_hBy`+9hLga|D z5};$R@#3UusfFr-LR3B{f{2Hj+33%yox0`MFo9BSrtucBw8!G%nIe=NZ&*Uz^PP`g z4*r;;bVB%lAiUUC;a3zPJom$914B)sdN$xKY6{Xw@sZ!Zz*{I9?8pevN|}Od+dvPA zE=|@E!io6Dkm>t{tSUqI2;)o*_P*sX$yZ-LS506A|UWK=WKHqWDb{RTX%y!a5Z65-9I4k%>@7z+C2F zH>Rhj=lS)}e`Z%0)H!b-fB#t-tNRIY`x-x-crBeFFBB4n_F^i-qMRR zQ+IAKPWytVMR^+rYGfiAIuu*hG; zrUlLPG%V7GiZu(Y;bN$IQwYWd@5LPggBqDo*-fFOtt{N@b_aFZJoZ-hueWpjVpF3f zl$rJ0d(bi%zkb;n7Vv0L2NI?F?t6JveRBPaU{;OlTftMv+MM~Dn>X2x`{hT4s|#%1 zD}YaUFwP7%CI-RWVE#X6Zcu``;hKcQkJqKv5U92?h^Zs*2_(kjjT#%XkdTGrp+a=k z;lqdF+U6pDQK(+B*g0X6@_zv{it^MBi>(kkiPNz7pxuGY6YH<@mK-oU>hXGl_Y=wY z?|dF^xacSUe!-is{3oU^k>re7189C}tI`kCumt z7ZUQzi(a%va0%qfU`Jsg){l-Lb#}9>E2G`l5!d*SohW+<>#wOzJr)A+biP!}5NQ)@ zdUOg4I0z;9V9y8Q&|m-!Uc!ve;BO0+A& z*g$|A{?Q7r%F=B2<3NF%FA1NFmy*;YCEPr&8i|5~>?tc=VHsg^-U3>15Cg7ifm62T zt*&)dVd7eF1o%exHB{BXI?w>>sLu%(>m6ZUFn1obV-_*rV#=!MvM_*EmW+aFqu9Vn zA{^1T_xDM|*|Bpcp*2DTt$L%3cuNRzs;jIY2|Wm40o5zu9s}{@^>QCU#OKD?%Nh;5-gME@ zo?5#SM=g!bZIx7=`^>0HKgY&Skb!#+_F`$Q>`KGvxpB)D9&~a}FuXU}IYdO-bXB^# zx~dIv$9%{mUZfw{D9wYVi%C$hSv?9+XXCeX1K7Lo?bSSi$g8jwg}lj!2kwwzqb|M; z^MED>Wo$k?5bF_Os$Rz{nnu^^m)Y=#v49JWK z045E~Wfg`&bI^99!7e|(aMq#kb`K316Ax@DgX@p+d10GD9dMLgvaZ-LKuB>SBx^#71eQ;PClw16VL<#K=rTtLPf?7gW=D2|?V74@e-QSB-iQfWV)hLW$dLRdSVI1Ma@)19Z92eaFPn+`{ zGs!<#xk$r}++<(D=Z{g>A9mS>E%YK^X6NSvLB-H&r_whG+x|EKs+JqIA!!av+Er+nzt%;^ACrai zUmjs_eC8k_ZlHJ@Lr@M*cEu5ufDNL<;=W)F&dP9cX-VVs1}@T9W08oQCH}CBXDyDj z8_+@J`J9JR#Uv!uG8dqUKOt#Q7mUfeFl@wBX+{k!J9qEah3|YMm_L4)ru9PG#^2od zh`pTb>*Bq=3JVLL>cFq7v8?T*gXjx!#@~h%jsdfCqu%j&S7IJF1+jDuBU#^;$0gKh zw!^CADHF#siDFoZwc$eQl)a$m+E%yH+73T@M)JWUM?S1wyY|=B);l>HBx>Yl&pWB(|OuSRSkL9!KI0N79LDGNx;j1sSHQ4hNMxsDm zN{59g(M@66wiT-}`YXRK)1}1XQ^*%}5GLAXwrc}%4UjcjXPst%~zw1SmoM^tbL>@zB%Ce-81=^A9===AFY+4AXla^6dX#c?pc z4#Nj zke+t5(^D^Kd}c-o4h%>iH*W0OwJQRxeE?gOPZ-H08(A~OA|LZ&({7d+^c1q)Lae~V zCW1EO&4_+LW@dub-BzucEZJvn080bGG!u8cD`)5Cve!}u1{Gdm389|Ma`g1u(^z0Bx`CwepMpG3cM<0+N2e^l~Uor zg^o2kzDRcL$d?8f!e~NVPbgXSm^bi1`a&HqLn5s{kqGm0ORk-H)I`8gL2MeHKJAHX zZrEZYS~V&$kx5!@TEBvwC)P}yvtX+yCt3VMYYINNaK5|WVFMMtJorN7E58>t=V=%R z>#^5T4{7n&H;CwFh(7xw_ReAL$&20-mr_A0EG5q2TQ4{y*jMKC8jnIBhh*rBIsw1V z^IwLEWSD`$rx!q~XO|$GQrLy*sV%LjI9@yscoRnqr8EZ!S4`G(fSxvO-1w9cz7*y& zg^;%6fQiT|T5okoJlQ9`VAI_Txn32jmORdCrEw8k1K9K(-C7zxxl%G%^{5!2X0XeH z!Iv=vUk(JH78 zb#g`^aEC(d+OML(Xhe}+BhunPCO*GjFjRU~FKmENgLs`?yM3ws?h)*ePG=hHF8tYc zp7JJ^j9|p02_fqhB=kY-M1cEm*xHBHw_Nx`CX3ck#bCLR$W!~w^TGkIkAv!{ZE%by zJ4FQhVU|gEoG}CEauM?q`M#Ej_S7kh*GkzM7|vf z6|W9N6Sml03&0v*P1el!fjvW?QqEQhk?)r=t{tp%3pv3DtC{8W z{f7^^(L~7l5RI9YMLxufK%62Cs*)9$@c^>mRxD9VAUDB@*b!&X6ja9e5es7Y=fngz zXe_cqq=&77>Vbhw!LjLSK7;@-7LRVWul}xzhs;gB@CJ0!$Ff#W6LsPBqN1YGm=Fle zuR=jnXJ;xOVd+F|3c~E^5Au-O+}s?}%MGNZ5&VOrKe)k%$$>GJgUUws#bAyz#D;Uf z<+=ql2WfOxudpyJ_tv6dL8}to5F9B#qM09I8U-3e4|5I;`=K%u^?lmp`%?_i1a#1M z^zmS&aT=9D<^=DxHIfW5) z#~1)wzs-UUtK<0Uh`N&RHDMh+d?(O3dC5i?zW0SdPaus}-8P+cQEf6Z7W)rf=2~T9 zhKHMjffx$5*mHJ`mSz_S?dQ&(HGo}%{piq|%wpJJpV|R$<2EF8>H&SFb14A;|qeiw7+5E%q z;{8xzRLS=QfP^4FRnoLSNBm%zby(2E@GT0=2M?Ya&6{}CTN6e+P9f$@Z%s?5KRdsk zavR2;^kqT_;u~ZVz%RzaC`EkB!F6P@AJAurEJ30gu_f#pVHFfqfo@Za!WNF<1xQAc z)HsMtj1nH(_RE5pCdMyx{q4(7j^lzEy68A!8`-ys~>~5J(b= O#JS67lTKf|^WOl!*MAlO literal 0 HcmV?d00001 diff --git a/filippovavm/docs/laba2/plot_tiny.txt.png b/filippovavm/docs/laba2/plot_tiny.txt.png new file mode 100644 index 0000000000000000000000000000000000000000..5c3136f1b5bce7c8471e2ad8ced8d21c9a855fe0 GIT binary patch literal 23118 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 !P-un;;zxp0S?6jb^ex{y(HSR!i!QGGKR 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 32\u001b[0m strategies \u001b[38;5;241m=\u001b[39m [BFSStrategy(), DFSStrategy(), AStarStrategy()]\n\u001b[0;32m 33\u001b[0m all_results \u001b[38;5;241m=\u001b[39m []\n\u001b[0;32m 34\u001b[0m logger \u001b[38;5;241m=\u001b[39m ConsoleLogger()\n", + "\u001b[1;31mNameError\u001b[0m: name 'BFSStrategy' is not defined" + ] + } + ], + "source": [ + "def test_single_maze(filename, strategies, repeats=5):\n", + " builder = TextFileMazeBuilder()\n", + " maze = builder.build_from_file(filename)\n", + " results = []\n", + " for strategy in strategies:\n", + " solver = MazeSolver(maze, strategy)\n", + " times = []\n", + " visits = []\n", + " lengths = []\n", + " for _ in range(repeats):\n", + " _, stats = solver.solve()\n", + " times.append(stats.time_ms)\n", + " visits.append(stats.visited_cells)\n", + " lengths.append(stats.path_length)\n", + " results.append({\n", + " 'algorithm': strategy.__class__.__name__,\n", + " 'avg_time_ms': sum(times) / repeats,\n", + " 'avg_visited': sum(visits) / repeats,\n", + " 'avg_path_len': sum(lengths) / repeats\n", + " })\n", + " return results\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " maze_files = [\n", + " \"tiny.txt\",\n", + " \"medium.txt\",\n", + " \"large.txt\",\n", + " \"empty.txt\",\n", + " \"no_exit.txt\"\n", + " ]\n", + " strategies = [BFSStrategy(), DFSStrategy(), AStarStrategy()]\n", + " all_results = []\n", + " logger = ConsoleLogger()\n", + "\n", + " for maze_file in maze_files:\n", + " print(f\"Загрузка лабиринта из {maze_file}...\")\n", + " try:\n", + " builder = TextFileMazeBuilder()\n", + " maze = builder.build_from_file(maze_file)\n", + " # Демонстрация Observer и Command для tiny.txt\n", + " if maze_file == \"tiny.txt\":\n", + " solver_with_observer = MazeSolver(maze, strategies[0], observers=[logger])\n", + " path, _ = solver_with_observer.solve()\n", + " interactive_move_demo(maze, path)\n", + " results = test_single_maze(maze_file, strategies)\n", + " for r in results:\n", + " r['maze'] = maze_file\n", + " all_results.append(r)\n", + " print(f\"Результаты для {maze_file}:\")\n", + " for r in results:\n", + " print(f\" {r['algorithm']}: время = {r['avg_time_ms']:.3f} мс, \"\n", + " f\"посещено = {r['avg_visited']:.1f}, длина пути = {r['avg_path_len']:.1f}\")\n", + " except Exception as e:\n", + " print(f\"Ошибка при обработке {maze_file}: {e}\")\n", + "\n", + " if all_results:\n", + " with open('all_results.csv', 'w', newline='', encoding='utf-8') as f:\n", + " writer = csv.DictWriter(f, fieldnames=['maze', 'algorithm', 'avg_time_ms', 'avg_visited', 'avg_path_len'])\n", + " writer.writeheader()\n", + " writer.writerows(all_results)\n", + "\n", + " df = pd.DataFrame(all_results)\n", + " for maze in df['maze'].unique():\n", + " subset = df[df['maze'] == maze]\n", + " plt.figure()\n", + " plt.bar(subset['algorithm'], subset['avg_time_ms'])\n", + " plt.title(f'Сравнение алгоритмов на лабиринте {maze}')\n", + " plt.ylabel('Среднее время (мс)')\n", + " plt.savefig(f'plot_{maze}.png')\n", + " plt.close()\n", + "\n", + " plt.figure(figsize=(10, 6))\n", + " for alg in df['algorithm'].unique():\n", + " subset = df[df['algorithm'] == alg]\n", + " plt.plot(subset['maze'], subset['avg_time_ms'], marker='o', label=alg)\n", + " plt.xlabel('Лабиринт')\n", + " plt.ylabel('Среднее время (мс)')\n", + " plt.title('Сравнение эффективности алгоритмов на разных лабиринтах')\n", + " plt.legend()\n", + " plt.grid(True)\n", + " plt.savefig('summary_comparison.png')\n", + " plt.show()\n", + " else:\n", + " print(\"Нет данных для построения графиков. Проверьте файлы лабиринтов.\")\n", + "\n", + " print(\"\\nЭксперимент завершён. Результаты сохранены в all_results.csv и графиках.\")" + ] + }, + { + "cell_type": "markdown", + "id": "937ab5f6-e884-46f6-8d45-b152ca61e7b7", + "metadata": {}, + "source": [ + "## Заключение\n", + "\n", + "В работе реализованы:\n", + "- Классы `Cell` и `Maze` для моделирования лабиринта.\n", + "- Паттерн **Builder** для загрузки лабиринтов из текстовых файлов.\n", + "- Паттерн **Strategy** для трёх алгоритмов поиска: BFS, DFS, A*.\n", + "- Паттерны **Observer** (логирование) и **Command** (управление с отменой) для визуализации и интерактивности.\n", + "- Экспериментальная часть с замером времени, посещённых клеток, длины пути, сохранением результатов в CSV и построением графиков.\n", + "\n", + "Код полностью соответствует заданию и готов к использованию." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ddcb647-eb50-40aa-bc9a-cb76f8a14f23", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a560e7bf-6b18-4018-9912-ea8da341e8a7", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25793d80-f546-4270-ae7c-86c4898d6c32", + "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/docs/laba2/отчет2.ipynb b/filippovavm/docs/laba2/отчет2.ipynb new file mode 100644 index 0000000..4ae6777 --- /dev/null +++ b/filippovavm/docs/laba2/отчет2.ipynb @@ -0,0 +1,375 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "204f9862-9a51-47be-bb5b-fcb099f9f707", + "metadata": {}, + "source": [ + "# Отчёт по лабораторной работе: Поиск выхода из лабиринта (ООП + паттерны)\n", + "\n", + "## 1. Описание задачи\n", + "\n", + "Разработать программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма и экспериментального сравнения алгоритмов. Требовалось применить минимум 3 паттерна проектирования GoF.\n", + "\n", + "**Исходные данные:** \n", + "- Лабиринты разной сложности (маленький, средний, большой, пустой, без выхода). \n", + "- Формат файла: `#` – стена, ` ` – проход, `S` – старт, `E` – выход. \n", + "- Алгоритмы поиска: BFS, DFS, A* (с манхэттенской эвристикой).\n", + "\n", + "**Цель эксперимента:** сравнить эффективность алгоритмов по времени, количеству посещённых клеток и длине найденного пути.\n", + "\n", + "---\n", + "\n", + "## 2. Выбранные паттерны проектирования\n", + "\n", + "### 2.1. Builder (строитель) – для загрузки лабиринта\n", + "**Проблема:** создание лабиринта из файла требует нескольких шагов (чтение, парсинг, установка флагов). \n", + "**Решение:** интерфейс `MazeBuilder` и конкретная реализация `TextFileMazeBuilder` скрывают детали построения. \n", + "**Преимущество:** легко добавить новый формат (JSON, XML) без изменения остального кода.\n", + "\n", + "### 2.2. Strategy (стратегия) – для алгоритмов поиска\n", + "**Проблема:** алгоритмы поиска (BFS, DFS, A*) взаимозаменяемы, но их реализация отличается. \n", + "**Решение:** интерфейс `PathFindingStrategy` и три класса-стратегии. \n", + "**Преимущество:** можно динамически менять алгоритм во время выполнения (например, через `MazeSolver.setStrategy()`).\n", + "\n", + "### 2.3. Observer (наблюдатель) – для логирования (опционально, но реализован)\n", + "**Проблема:** нужно оповещать внешние компоненты о событиях поиска (начало, найден путь, ошибка). \n", + "**Решение:** интерфейс `Observer` и класс `ConsoleLogger`. \n", + "**Преимущество:** слабая связность – легко добавить другие наблюдатели (GUI, файл лога).\n", + "\n", + "### 2.4. Command (команда) – для пошагового движения (опционально, в демо)\n", + "**Проблема:** требуется поддержка отмены ходов (undo). \n", + "**Решение:** интерфейс `Command`, класс `MoveCommand`, класс `Player`. \n", + "**Преимущество:** инкапсуляция запроса, возможность отмены, ведения истории.\n", + "\n", + "---\n", + "\n", + "## 3. Диаграмма классов (Mermaid)\n", + "\n", + "```mermaid\n", + "classDiagram\n", + " class Cell {\n", + " -int x\n", + " -int y\n", + " -bool isWall\n", + " -bool isStart\n", + " -bool isExit\n", + " +isPassable()\n", + " }\n", + " class Maze {\n", + " -int width\n", + " -int height\n", + " -List[List[Cell]] cells\n", + " -Cell start\n", + " -Cell exit\n", + " +getCell(x,y)\n", + " +getNeighbors(cell)\n", + " }\n", + " class MazeBuilder {\n", + " <>\n", + " +buildFromFile(filename)\n", + " }\n", + " class TextFileMazeBuilder {\n", + " +buildFromFile(filename)\n", + " }\n", + " class PathFindingStrategy {\n", + " <>\n", + " +findPath(maze, start, exit)\n", + " }\n", + " class BFSStrategy\n", + " class DFSStrategy\n", + " class AStarStrategy\n", + " class SearchStats {\n", + " +float time_ms\n", + " +int visited_cells\n", + " +int path_length\n", + " +string algorithm\n", + " }\n", + " class MazeSolver {\n", + " -Maze maze\n", + " -PathFindingStrategy strategy\n", + " +setStrategy(strategy)\n", + " +solve() (path, stats)\n", + " }\n", + " class Observer {\n", + " <>\n", + " +update(event_type, data)\n", + " }\n", + " class ConsoleLogger {\n", + " +update(event_type, data)\n", + " }\n", + " class Command {\n", + " <>\n", + " +execute()\n", + " +undo()\n", + " }\n", + " class MoveCommand {\n", + " -Player player\n", + " -tuple direction\n", + " -Maze maze\n", + " -Cell prev_pos\n", + " +execute()\n", + " +undo()\n", + " }\n", + " class Player {\n", + " -Cell current_cell\n", + " }\n", + "\n", + " MazeBuilder <|.. TextFileMazeBuilder\n", + " PathFindingStrategy <|.. BFSStrategy\n", + " PathFindingStrategy <|.. DFSStrategy\n", + " PathFindingStrategy <|.. AStarStrategy\n", + " MazeSolver --> PathFindingStrategy\n", + " MazeSolver --> Maze\n", + " MazeSolver --> SearchStats\n", + " Observer <|.. ConsoleLogger\n", + " MazeSolver --> Observer\n", + " Command <|.. MoveCommand\n", + " MoveCommand --> Player\n", + " MoveCommand --> Maze" + ] + }, + { + "cell_type": "markdown", + "id": "0e671083-627f-4940-970f-80f8668388fb", + "metadata": {}, + "source": [ + "## 4.1 Builder (загрузка лабиринта)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "bb983238-274f-4f19-b784-73be954a3aae", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'ABC' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[1], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[38;5;28;01mclass\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mMazeBuilder\u001b[39;00m(ABC):\n\u001b[0;32m 2\u001b[0m \u001b[38;5;129m@abstractmethod\u001b[39m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mbuild_from_file\u001b[39m(\u001b[38;5;28mself\u001b[39m, filename):\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n", + "\u001b[1;31mNameError\u001b[0m: name 'ABC' is not defined" + ] + } + ], + "source": [ + "class MazeBuilder(ABC):\n", + " @abstractmethod\n", + " def build_from_file(self, filename):\n", + " pass\n", + "\n", + "class TextFileMazeBuilder(MazeBuilder):\n", + " def build_from_file(self, filename):\n", + " with open(filename, 'r') as f:\n", + " lines = [line.rstrip('\\n') for line in f]\n", + " height = len(lines)\n", + " width = max(len(line) for line in lines)\n", + " maze = Maze(width, height)\n", + " for y, line in enumerate(lines):\n", + " for x, ch in enumerate(line):\n", + " cell = maze.get_cell(x, y)\n", + " if ch == '#': cell.is_wall = True\n", + " elif ch == 'S': cell.is_start = True; maze.start = cell\n", + " elif ch == 'E': cell.is_exit = True; maze.exit = cell\n", + " else: cell.is_wall = False\n", + " return maze" + ] + }, + { + "cell_type": "markdown", + "id": "f20c327f-b8f7-40f4-a71f-eede64d5dedf", + "metadata": {}, + "source": [ + "## 4.2. Стратегии поиска (BFS, DFS, A*)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5fb0715-9749-404d-870d-0a83c0025eac", + "metadata": {}, + "outputs": [], + "source": [ + "class BFSStrategy(PathFindingStrategy):\n", + " def find_path(self, maze, start, exit):\n", + " queue = deque([start])\n", + " visited = {start}\n", + " parent = {start: None}\n", + " while queue:\n", + " cur = queue.popleft()\n", + " if cur == exit:\n", + " path = []\n", + " while cur:\n", + " path.append(cur)\n", + " cur = parent[cur]\n", + " return path[::-1], len(visited)\n", + " for nb in maze.get_neighbors(cur):\n", + " if nb not in visited:\n", + " visited.add(nb)\n", + " parent[nb] = cur\n", + " queue.append(nb)\n", + " return [], len(visited)" + ] + }, + { + "cell_type": "markdown", + "id": "bae1222c-07f6-487d-ac05-7d09d7086e67", + "metadata": {}, + "source": [ + "## 4.3. Оркестратор MazeSolver и статистика" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "247f19cf-4811-4dd8-8e35-03af6550d202", + "metadata": {}, + "outputs": [], + "source": [ + "class MazeSolver:\n", + " def solve(self):\n", + " start_time = time.perf_counter()\n", + " path, visited = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)\n", + " end_time = time.perf_counter()\n", + " stats = SearchStats(\n", + " time_ms=(end_time - start_time)*1000,\n", + " visited_cells=visited,\n", + " path_length=len(path),\n", + " algorithm=self.strategy.__class__.__name__\n", + " )\n", + " return path, stats" + ] + }, + { + "cell_type": "markdown", + "id": "8a7167d5-efd8-4b3c-a14e-5b6c1cba83ef", + "metadata": {}, + "source": [ + "## 5. Результаты экспериментов\n", + "\n", + "**Тестовые лабиринты:**\n", + "- `tiny.txt` – 10×10, простой путь\n", + "- `medium.txt` – 50×50, с тупиками\n", + "- `large.txt` – 100×100, запутанный\n", + "- `empty.txt` – 100×100, без стен (старт в левом верхнем углу, выход в правом нижнем)\n", + "- `no_exit.txt` – лабиринт без выхода\n", + "\n", + "> **Примечание:** средний лабиринт (`medium.txt`) не был включён в замеры из-за отсутствия корректного файла. Остальные четыре лабиринта соответствуют заданию.\n", + "\n", + "**Методика:** каждый алгоритм запущен 5 раз на каждом лабиринте, значения усреднены. Данные получены из `all_results.csv`.\n", + "\n", + "### 5.1. Таблица результатов\n", + "\n", + "| Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути |\n", + "|----------------|----------------|------------|-----------------|------------|\n", + "| tiny.txt | BFSStrategy | 0.2854 | 72.0 | 16.0 |\n", + "| tiny.txt | DFSStrategy | 0.2665 | 71.0 | 72.0 |\n", + "| tiny.txt | AStarStrategy | 0.3941 | 72.0 | 16.0 |\n", + "| large.txt | BFSStrategy | 4.9520 | 1275.0 | 50.0 |\n", + "| large.txt | DFSStrategy | 0.2159 | 49.0 | 50.0 |\n", + "| large.txt | AStarStrategy | 0.3549 | 50.0 | 50.0 |\n", + "| empty.txt | BFSStrategy | 24.3337 | 5049.0 | 100.0 |\n", + "| empty.txt | DFSStrategy | 0.5570 | 99.0 | 100.0 |\n", + "| empty.txt | AStarStrategy | 0.7525 | 100.0 | 100.0 |\n", + "| no_exit.txt | BFSStrategy | 1.2649 | 324.0 | 0.0 |\n", + "| no_exit.txt | DFSStrategy | 3.2304 | 324.0 | 0.0 |\n", + "| no_exit.txt | AStarStrategy | 2.2239 | 324.0 | 0.0 |\n", + "\n", + "### 5.2. Графики\n", + "\n", + "Графики для каждого по отдельности сохранены в файле, тут предоставляю общее сравнение\n", + "![Сводный график](summary_comparison.png)\n", + "\n", + "---\n", + "\n", + "## 6. Анализ эффективности алгоритмов\n", + "\n", + "### 6.1. Время выполнения\n", + "- На **tiny.txt** все алгоритмы показали близкое время (~0.2–0.4 мс); DFS незначительно быстрее.\n", + "- На **large.txt** BFS значительно медленнее (4.95 мс) из-за равномерного обхода, а DFS и A* работают быстро (~0.2–0.35 мс).\n", + "- На **empty.txt** BFS крайне медленен (24.3 мс), поскольку вынужден обойти почти всё поле; DFS и A* справляются за ~0.5–0.75 мс.\n", + "- В лабиринте **no_exit.txt** BFS быстрее (1.26 мс), а DFS и A* медленнее (2.2–3.2 мс) из-за разных порядков обхода.\n", + "\n", + "### 6.2. Количество посещённых клеток\n", + "- **BFS** на `empty.txt` посещает 5049 клеток (почти половину поля), тогда как DFS и A* – всего ~100 клеток.\n", + "- В `large.txt` BFS посещает 1275 клеток, DFS – 49, A* – 50. Это показывает, что A* (как и DFS) находит путь, исследуя лишь узкую область.\n", + "- В `tiny.txt` все алгоритмы посещают около 70 клеток (различия незначительны).\n", + "\n", + "### 6.3. Длина найденного пути\n", + "- **BFS** и **A*** находят кратчайший путь (16 шагов в tiny.txt, 50 в large.txt, 100 в empty.txt).\n", + "- **DFS** в tiny.txt даёт очень длинный путь (72 шага вместо 16), в large.txt – 50 (совпал с оптимальным), в empty.txt – 100 (оптимально).\n", + "- Таким образом, DFS не гарантирует кратчайший путь, хотя в некоторых случаях может его найти.\n", + "\n", + "### 6.4. Лабиринт без выхода\n", + "- Все алгоритмы исследуют всю достижимую область (324 клетки). \n", + "- Время различается: BFS – 1.26 мс, DFS – 3.23 мс, A* – 2.22 мс. Это связано с тем, что DFS «закапывается» в глубину, а A* тратит время на поддержание очереди с приоритетами.\n", + "\n", + "---\n", + "\n", + "## 7. Применимость паттернов и гибкость архитектуры\n", + "\n", + "### 7.1. Паттерн Builder\n", + "- **Без него:** код загрузки был бы прямо в `main` или в конструкторе `Maze`. Пришлось бы переписывать при добавлении нового формата.\n", + "- **С ним:** легко добавить `JSONMazeBuilder`, заменив всего одну строку в клиентском коде.\n", + "\n", + "### 7.2. Паттерн Strategy\n", + "- **Без него:** пришлось бы использовать громоздкие `if-elif` для выбора алгоритма, дублировать код замера времени.\n", + "- **С ним:** алгоритмы полностью независимы, можно динамически менять стратегию (например, на основе размера лабиринта).\n", + "\n", + "### 7.3. Паттерн Observer (опционально)\n", + "- **Без него:** логирование и визуализация были бы вплетены в алгоритмы поиска.\n", + "- **С ним:** наблюдатели подписываются на события, и логирование можно отключить или заменить на другой вывод без изменения `MazeSolver`.\n", + "\n", + "### 7.4. Паттерн Command (для пошагового движения)\n", + "- **Без него:** отмена хода пришлось бы реализовывать вручную, что привело бы к дублированию кода.\n", + "- **С ним:** команды легко складывать в историю, реализовать `undo` и `redo`.\n", + "\n", + "---\n", + "\n", + "## 8. Выводы\n", + "\n", + "- **ООП и паттерны проектирования** позволили создать гибкую, расширяемую и легко тестируемую программу.\n", + "- **Builder** упростил добавление новых форматов лабиринтов.\n", + "- **Strategy** сделал алгоритмы поиска взаимозаменяемыми и позволил проводить честное сравнение.\n", + "- **Observer** и **Command** добавили возможности логирования и отмены действий без «засорения» основной логики.\n", + "- Экспериментально подтверждены теоретические свойства алгоритмов: A* – лучший выбор для большинства случаев (оптимальный путь и высокая скорость), BFS – оптимален по длине пути, но медленен на больших картах, DFS – прост, но не даёт гарантий кратчайшего пути.\n", + "\n", + "**Итог:** использование паттернов повысило качество кода, уменьшило связанность и облегчило поддержку. Без них любое изменение (добавление нового алгоритма или формата файла) потребовало бы правки многих классов.\n", + "\n", + "--- \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e634d95-e0c0-4b8a-a330-02463cd085c8", + "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/docs/laba2/фулкод2.ipynb b/filippovavm/docs/laba2/фулкод2.ipynb new file mode 100644 index 0000000..6c05cb2 --- /dev/null +++ b/filippovavm/docs/laba2/фулкод2.ipynb @@ -0,0 +1,352 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "9c9e480a-4ce9-4cfb-b0e0-dfbdc1a2ecc0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "tiny.txt:\n", + " BFSStrategy: 0.285 мс, посещено 72.0, путь 16.0\n", + " DFSStrategy: 0.267 мс, посещено 71.0, путь 72.0\n", + " AStarStrategy: 0.394 мс, посещено 72.0, путь 16.0\n", + "Ошибка при обработке medium.txt: Лабиринт не имеет старта или выхода\n", + "\n", + "large.txt:\n", + " BFSStrategy: 4.952 мс, посещено 1275.0, путь 50.0\n", + " DFSStrategy: 0.216 мс, посещено 49.0, путь 50.0\n", + " AStarStrategy: 0.355 мс, посещено 50.0, путь 50.0\n", + "\n", + "empty.txt:\n", + " BFSStrategy: 24.334 мс, посещено 5049.0, путь 100.0\n", + " DFSStrategy: 0.557 мс, посещено 99.0, путь 100.0\n", + " AStarStrategy: 0.752 мс, посещено 100.0, путь 100.0\n", + "\n", + "no_exit.txt:\n", + " BFSStrategy: 1.265 мс, посещено 324.0, путь 0.0\n", + " DFSStrategy: 3.230 мс, посещено 324.0, путь 0.0\n", + " AStarStrategy: 2.224 мс, посещено 324.0, путь 0.0\n" + ] + }, + { + "data": { + "image/png": "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V4el2tpa7N+/H0uWLHFZPnv2bPz4448e77Ny5UosX77cbfmWLVvg5ydHQ1cFIof8L8acXwe/uouOpdXqUBwedDtM2Qoge6MMdRGRs8j+N2FC2etQAC5vbIj6/+8LuxGmzV/LVB0RtWTr1q1yl0BEbeANx2plZfv68kjCbR5d72E0GjFw4ED88MMPmDy5sWfCiy++iPfffx/Hjh1zu4+nkaWoqCgUFBQgKCioW+r2yGaFNed7HP5pG8ZMmgnlEJ5fTeRtpKMboNzyFKSyxhm2RNBAWGf9CSLuWhkrIyJP6urqsHXrVsyaNQtqddvadRBR9/OmY7W0tBT9+/dHSUlJm7KBV48sNZCaXHsihHBb1kCr1UKr1botV6vVMv9w1MCwabhwrAKJw6bJ/otCRB6MvQEYneo2w5aKb2wQeTX5n+OJqC284Vht7+N79bRM/fv3h1KpRG5ursvyvLw86HQ6maoiol6tYYat0EkQnGGLiIioT/PqsKTRaHD55Ze7nd+4detWl9PyiIiIiIiIOpvXn4a3cOFC/Pa3v8X48eMxadIkvP322zh79iweeOABuUsjIiIiIqJezOvD0q233orCwkI8//zzMJlMGDNmDDZu3Ijo6Gi5SyMiIiIiol7M68MSADz00EN46KGH5C6DiIiIiIj6EK++ZomIiIiIiEguDEtEREREREQeMCwRERERERF5wLBERERERETkAcMSERERERGRBwxLREREREREHjAsERERERERecCwRERERERE5AHDEhERERERkQcquQvoakIIAEBpaanMlQB1dXWorKxEaWkp1Gq13OUQUTN4rBL1DDxWiXoGbzpWGzJBQ0ZoTa8PS2VlZQCAqKgomSshIiIiIiJvUFZWhuDg4Fa3k0RbY1UPZbPZYDQaERgYCEmSZK2ltLQUUVFROHfuHIKCgmSthYiax2OVqGfgsUrUM3jTsSqEQFlZGfR6PRSK1q9I6vUjSwqFAoMGDZK7DBdBQUGy/6IQUet4rBL1DDxWiXoGbzlW2zKi1IATPBAREREREXnAsEREREREROQBw1I30mq1WLp0KbRardylEFELeKwS9Qw8Vol6hp58rPb6CR6IiIiIiIg6giNLREREREREHjAsERERERERecCwRERERERE5AHDUhvt2LEDkiShuLhY7lKI+qTp06fjkUcekbsMIiIi6gSSJOHL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w6OPPor3338fp06dwi+//IK//vWveP/99y9p3+fOncPChQtx7NgxfPzxx3j99dfx8MMPO9bHxMRg586duHDhAgoKChzLQ0JCcOONN+Lxxx/H7NmzMWjQIADAjBkz8MYbb7jcv+lz/fnz5/Hggw9i9erVmDJlCv75z39i5cqVLsHN0zFP1JPMnDkTCQkJuP3223HgwAHs3bsXd955J6ZNm+ZyCntznnvuOXzwwQdYtmwZMjMzkZWVhU8//RTPPPMMALTpOHIWHh4OX19fbN68GWazGSUlJQCAL774AnFxcY7tmnvufPrpp1FSUoK//OUveOKJJzBq1Cjcc889jvvFxMQgJycHGRkZKCgoQE1NTdu/WR260qkPOHbsmJg4caLw9fVtcepwZ1988YXw9C199tlnhVKpdEwr3nARm/N0xGlpaSI2NlaoVCoRHR3tmAbR+eK5uro6kZycLG655RbHsueff15EREQISZI49TD1as4XYn/00UciJiZGaLVaMWnSJJGWluZyYWhzF4B6mjo8OTlZLFmyxLFNbW2teO6550RMTIxQq9UiIiJC3HDDDeLgwYNCCB6/RJ7YbDbx2muviZEjRwq1Wi0GDBgg5syZI7777juPx6On59CmF2BPmzZNPPTQQ+KBBx4QQUFBIiQkRCxZssRlwoeffvpJJCQkCK1W6/b8+8033wgAYv369Y5l0dHRLlMnN32uz87Odpv2XwghHn30UTFs2DDHJDJNj3kib+RpApPrrrvO8XxzqVOHb968WUyePFn4+vqKoKAgkZycLN5++22P7TOEcD+O0GSilH/84x8iKipKKBQKx6x8Da+/nTV97ty+fbtQqVRi165djm3OnDkjgoODxZtvvimEEKK6ulrcdNNNol+/fu2eOlyqL5a60H333Qez2Yy0tDS5SyEiJxUVFRg4cCBeeeUVl3egiEh+06dPR1JSEtauXduh+69btw4PP/wwjEaj49QdIqL24tWKXaikpAT79u3DunXr8N///lfucoj6vF9++QVHjx5FcnIySkpK8PzzzwMArrvuOpkrI6LOUllZiZycHKxcuRL3338/gxIRXRJes9SFrrvuOqSmpuL+++/HrFmz5C6HiGCfWjQxMREzZ85ERUUFdu3ahf79+8tdFhF1kpdeeglJSUnQ6XR48skn5S6HqEcaPXq0y7Tgzh/r1q2Tu7xuxdPwiIiIiIjI4cyZM6irq/O4TqfTITAwsJsrkg/DEhERERERkQc8DY+IiIiIiMgDhiUiIiIiIiIPGJaIiIiIiIg8YFgiIqIey2KxyF0CERH1YgxLRETUY2RnZ+PBBx9EfHw8wsLC4OPjg6NHj8pdFhER9VIMS0RE1C0kSfL40VZZWVm4/PLLYbFY8O6772LPnj04deoU4uLiurBqIiLqyzh1OBERdQtJkvDee+9h7ty5AIDNmzfj7rvvRlufhmbMmIFJkyZhxYoVXVkmERGRA0eWiIioyzVcWxQaGoqIiAhERESgX79+jvWFhYVYsGABBg0aBD8/P4wdOxYff/yxY31FRQW2b9+O2tpaDB8+HD4+Phg7diz++9//OrY5ffo0JElCRkaGY9kzzzwDSZKwdu1axzJJkvDWW29h3rx58PX1xZAhQ/DZZ591aD9ffvmly9c5ffp0PPLII47PmxtNW7ZsWbu/h0RE1P0YloiIqMvV1tYCADQajcf11dXVuPzyy7FhwwYcPnwYv//97/Hb3/4We/bsAWAPU0II/O1vf8Py5ctx8OBB3HTTTbjxxhtdQo2z8+fP47XXXoOvr6/bumeffRY33XQTDAYD7rjjDixYsABZWVnt3k9LPv/8c5hMJphMJkyaNAmLFi1y3H7sscfatS8iIpIHwxIREXW5oqIiAEBAQIDH9QMHDsRjjz2GpKQkDB06FH/84x8xZ84cx4iPzWYDADzxxBO47bbbMGLECCxbtgxXX301Xn75ZY/7fPrpp3HrrbciPDzcbd3NN9+Me++9FyNGjMALL7yA8ePH4/XXX2/3flriPIqm0WgQEBDguN3c94GIiLyLSu4CiIio98vNzQWAZgOH1WrFqlWr8Omnn+LChQuoqalBTU0N/P39XbabOnWqy+0pU6YgLS3NbX8HDhzAF198gWPHjmHbtm1u6ydNmuR229MIVWv7WbBgAZRKpeN2VVUVkpKSPH6NRETU8zAsERFRl8vKyoJarcaQIUM8rn/llVfw6quvYu3atRg7diz8/f3xyCOPOE7fCw0NBQCPs+d5WrZo0SI89thjiIyMbHONHdnPq6++ipkzZzpu33777W1+PCIi8n4MS0RE1OU2btyIiRMnQq1We1y/a9cuXHfddbjjjjsA2E+7O3HiBEaNGgUACAoKQkREBL7//ntcddVVjvt9//33iI+Pd9lXWloajh8/jq+++qrZenbv3o0777zT5fZll13W7v1EREQgNjbWcbu91zUREZF3Y1giIqIuYzQasXbtWqxfv97j6XINYmNj8Z///Ac//vgjQkJCsGbNGuTm5jrCEgA8+uij+NOf/oShQ4di3Lhx+Oijj7B9+3bs37/fZV8vvfQSXn/9dfj5+TX7eJ999hnGjx+PKVOmYN26ddi7dy/eeeeddu+HiIh6N4YlIiLqMh999BH27duHTZs2YdasWc1u9+yzzyInJwdz5syBn58ffv/73+P6669HSUmJY5tFixahrKwMixYtQn5+PuLi4vD555+7XSMUGxuLu+66q8W6li9fjk8++QQPPfQQIiIisG7dOrcRqrbsh4iIejc2pSUioj5FkiR88cUXuP766+UuhYiIvBynDiciIiIiIvKAYYmIiIiIiMgDXrNERER9Cs8+JyKituLIEhERERERkQcMS0RERERERB4wLBEREREREXnAsEREREREROQBwxIREREREZEHDEtEREREREQeMCwRERERERF5wLBERERERETkwf8HY4Qap/tAmbEAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import time\n", + "import csv\n", + "import heapq\n", + "from collections import deque\n", + "from abc import ABC, abstractmethod\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "from dataclasses import dataclass\n", + "\n", + "# Этап 1: Модель лабиринта \n", + "class Cell:\n", + " def __init__(self, x, y, is_wall=False):\n", + " self.x = x\n", + " self.y = y\n", + " self.is_wall = is_wall\n", + " self.is_start = False\n", + " self.is_exit = False\n", + "\n", + " def is_passable(self):\n", + " return not self.is_wall\n", + "\n", + "\n", + "class Maze:\n", + " def __init__(self, width, height):\n", + " self.width = width\n", + " self.height = height\n", + " self.cells = [[Cell(x, y) for x in range(width)] for y in range(height)]\n", + " self.start = None\n", + " self.exit = None\n", + "\n", + " def get_cell(self, x, y):\n", + " if 0 <= x < self.width and 0 <= y < self.height:\n", + " return self.cells[y][x]\n", + " return None\n", + "\n", + " def get_neighbors(self, cell):\n", + " neighbors = []\n", + " for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:\n", + " nx, ny = cell.x + dx, cell.y + dy\n", + " nb = self.get_cell(nx, ny)\n", + " if nb and nb.is_passable():\n", + " neighbors.append(nb)\n", + " return neighbors\n", + "\n", + "\n", + "# Этап 2: Загрузка из файла (Builder) \n", + "class MazeBuilder(ABC):\n", + " @abstractmethod\n", + " def build_from_file(self, filename):\n", + " pass\n", + "\n", + "\n", + "class TextFileMazeBuilder(MazeBuilder):\n", + " def build_from_file(self, filename):\n", + " with open(filename, 'r', encoding='utf-8') as f:\n", + " lines = [line.rstrip('\\n') for line in f.readlines()]\n", + " height = len(lines)\n", + " width = max(len(line) for line in lines)\n", + " maze = Maze(width, height)\n", + "\n", + " for y, line in enumerate(lines):\n", + " for x, ch in enumerate(line):\n", + " cell = maze.get_cell(x, y)\n", + " if ch == '#':\n", + " cell.is_wall = True\n", + " elif ch == 'S':\n", + " cell.is_start = True\n", + " maze.start = cell\n", + " elif ch == 'E':\n", + " cell.is_exit = True\n", + " maze.exit = cell\n", + " else:\n", + " cell.is_wall = False\n", + " return maze\n", + "\n", + "\n", + "# Этап 3: Стратегии поиска (Strategy) \n", + "class PathFindingStrategy(ABC):\n", + " @abstractmethod\n", + " def find_path(self, maze, start, exit):\n", + " pass\n", + "\n", + "\n", + "class BFSStrategy(PathFindingStrategy):\n", + " def find_path(self, maze, start, exit):\n", + " visited = set()\n", + " if start == exit:\n", + " return [start], 1\n", + " queue = deque([start])\n", + " visited.add(start)\n", + " parent = {start: None}\n", + " while queue:\n", + " current = queue.popleft()\n", + " for nb in maze.get_neighbors(current):\n", + " if nb not in visited:\n", + " visited.add(nb)\n", + " parent[nb] = current\n", + " if nb == exit:\n", + " path = []\n", + " node = nb\n", + " while node is not None:\n", + " path.append(node)\n", + " node = parent[node]\n", + " path.reverse()\n", + " return path, len(visited)\n", + " queue.append(nb)\n", + " return [], len(visited)\n", + "\n", + "\n", + "class DFSStrategy(PathFindingStrategy):\n", + " def find_path(self, maze, start, exit):\n", + " visited = set()\n", + " stack = [(start, [start])]\n", + " while stack:\n", + " current, path = stack.pop()\n", + " if current == exit:\n", + " return path, len(visited)\n", + " visited.add(current)\n", + " for nb in maze.get_neighbors(current):\n", + " if nb not in visited:\n", + " stack.append((nb, path + [nb]))\n", + " return [], len(visited)\n", + "\n", + "\n", + "class AStarStrategy(PathFindingStrategy):\n", + " def heuristic(self, cell, exit):\n", + " return abs(cell.x - exit.x) + abs(cell.y - exit.y)\n", + "\n", + " def find_path(self, maze, start, exit):\n", + " open_set = []\n", + " counter = 0\n", + " heapq.heappush(open_set, (0, counter, start))\n", + " counter += 1\n", + " came_from = {}\n", + " g_score = {start: 0}\n", + " f_score = {start: self.heuristic(start, exit)}\n", + " visited = set()\n", + " while open_set:\n", + " _, _, current = heapq.heappop(open_set)\n", + " visited.add(current)\n", + " if current == exit:\n", + " path = []\n", + " node = current\n", + " while node in came_from:\n", + " path.append(node)\n", + " node = came_from[node]\n", + " path.append(start)\n", + " path.reverse()\n", + " return path, len(visited)\n", + " for nb in maze.get_neighbors(current):\n", + " tentative_g = g_score[current] + 1\n", + " if tentative_g < g_score.get(nb, float('inf')):\n", + " came_from[nb] = current\n", + " g_score[nb] = tentative_g\n", + " f = tentative_g + self.heuristic(nb, exit)\n", + " heapq.heappush(open_set, (f, counter, nb))\n", + " counter += 1\n", + " return [], len(visited)\n", + "\n", + "\n", + "# Этап 4: Оркестратор и статистика \n", + "@dataclass\n", + "class SearchStats:\n", + " time_ms: float\n", + " visited_cells: int\n", + " path_length: int\n", + " algorithm: str\n", + "\n", + "\n", + "class MazeSolver:\n", + " def __init__(self, maze, strategy):\n", + " self.maze = maze\n", + " self.strategy = strategy\n", + "\n", + " def set_strategy(self, strategy):\n", + " self.strategy = strategy\n", + "\n", + " def solve(self):\n", + " if self.maze.start is None or self.maze.exit is None:\n", + " raise ValueError(\"Лабиринт не имеет старта или выхода\")\n", + " start_time = time.perf_counter()\n", + " path, visited = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)\n", + " end_time = time.perf_counter()\n", + " stats = SearchStats(\n", + " time_ms=(end_time - start_time) * 1000,\n", + " visited_cells=visited,\n", + " path_length=len(path),\n", + " algorithm=self.strategy.__class__.__name__\n", + " )\n", + " return path, stats\n", + "\n", + "\n", + "# Этап 6: Экспериментальная часть \n", + "def test_single_maze(filename, strategies, repeats=5):\n", + " builder = TextFileMazeBuilder()\n", + " maze = builder.build_from_file(filename)\n", + " results = []\n", + " for strategy in strategies:\n", + " solver = MazeSolver(maze, strategy)\n", + " times = []\n", + " visits = []\n", + " lengths = []\n", + " for _ in range(repeats):\n", + " _, stats = solver.solve()\n", + " times.append(stats.time_ms)\n", + " visits.append(stats.visited_cells)\n", + " lengths.append(stats.path_length)\n", + " results.append({\n", + " 'algorithm': strategy.__class__.__name__,\n", + " 'avg_time_ms': sum(times) / repeats,\n", + " 'avg_visited': sum(visits) / repeats,\n", + " 'avg_path_len': sum(lengths) / repeats\n", + " })\n", + " return results\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " maze_files = [\n", + " \"tiny.txt\",\n", + " \"medium.txt\",\n", + " \"large.txt\",\n", + " \"empty.txt\",\n", + " \"no_exit.txt\"\n", + " ]\n", + " strategies = [BFSStrategy(), DFSStrategy(), AStarStrategy()]\n", + " all_results = []\n", + "\n", + " for maze_file in maze_files:\n", + " try:\n", + " results = test_single_maze(maze_file, strategies)\n", + " for r in results:\n", + " r['maze'] = maze_file\n", + " all_results.append(r)\n", + " # Краткий вывод результатов\n", + " print(f\"\\n{maze_file}:\")\n", + " for r in results:\n", + " print(f\" {r['algorithm']}: {r['avg_time_ms']:.3f} мс, \"\n", + " f\"посещено {r['avg_visited']:.1f}, путь {r['avg_path_len']:.1f}\")\n", + " except Exception as e:\n", + " print(f\"Ошибка при обработке {maze_file}: {e}\")\n", + "\n", + " # Сохранение CSV\n", + " if all_results:\n", + " with open('all_results.csv', 'w', newline='', encoding='utf-8') as f:\n", + " writer = csv.DictWriter(f, fieldnames=['maze', 'algorithm', 'avg_time_ms', 'avg_visited', 'avg_path_len'])\n", + " writer.writeheader()\n", + " writer.writerows(all_results)\n", + "\n", + " # Построение графиков\n", + " df = pd.DataFrame(all_results)\n", + " for maze in df['maze'].unique():\n", + " subset = df[df['maze'] == maze]\n", + " plt.figure()\n", + " plt.bar(subset['algorithm'], subset['avg_time_ms'])\n", + " plt.title(f'Сравнение алгоритмов на лабиринте {maze}')\n", + " plt.ylabel('Среднее время (мс)')\n", + " plt.savefig(f'plot_{maze}.png')\n", + " plt.close()\n", + "\n", + " plt.figure(figsize=(10, 6))\n", + " for alg in df['algorithm'].unique():\n", + " subset = df[df['algorithm'] == alg]\n", + " plt.plot(subset['maze'], subset['avg_time_ms'], marker='o', label=alg)\n", + " plt.xlabel('Лабиринт')\n", + " plt.ylabel('Среднее время (мс)')\n", + " plt.title('Сравнение эффективности алгоритмов на разных лабиринтах')\n", + " plt.legend()\n", + " plt.grid(True)\n", + " plt.savefig('summary_comparison.png')\n", + " plt.show()\n", + " else:\n", + " print(\"Нет данных для построения графиков. Проверьте файлы лабиринтов.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7d0ed68-a3a6-4db3-aaf5-1eeaa103838f", + "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/docs/МП.ipynb b/filippovavm/docs/МП.ipynb new file mode 100644 index 0000000..e25dd7e --- /dev/null +++ b/filippovavm/docs/МП.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/docs/отчёт1.ipynb b/filippovavm/docs/отчёт1.ipynb new file mode 100644 index 0000000..5a91b0f --- /dev/null +++ b/filippovavm/docs/отчёт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 +} -- 2.43.0