{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2acfa743",
   "metadata": {},
   "source": [
    "# 0. Подготовим окружение"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "4689b73e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import os\n",
    "sys.path.insert(0, os.path.abspath( '../task1'))\n",
    "sys.path.insert(0, os.path.abspath( '../'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37cc11a5",
   "metadata": {},
   "source": [
    "# 1. Генерация тестовых данных\n",
    "\n",
    "Создадим список records из N=10000 элементов. Каждый элемент — кортеж (name, phone).  \n",
    "Имена возъмём случайные из небольшого набора (чтобы были повторения и коллизии).  \n",
    "Для проверки влияния порядка подготовим два варианта:  \n",
    "\n",
    "_records_shuffled_ — случайный порядок.  \n",
    "_records_sorted_ — отсортированный по имени (по алфавиту)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "a3b5c31b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from util.randomNames import generate_test_data\n",
    "from util.timeTester import test\n",
    "\n",
    "records_shuffled = generate_test_data(N=10000)\n",
    "records_sorted = generate_test_data(N=10000, _sorted=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2f4989c",
   "metadata": {},
   "source": [
    "# 2. Проведение замеров"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "df12d41d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Подготовим функции СД, которые будем тестировать\n",
    "from structures.LinkedList import *\n",
    "from structures.HashTable import *\n",
    "from structures.BinaryTree import *\n",
    "\n",
    "func_list = {\"Связанный список\" : (ll_insert,  ll_find,  ll_delete),\n",
    "             \"Хэш-таблица\" : (ht_insert,  ht_find,  ht_delete),\n",
    "             \"Бинарное дерево\" : (bst_insert, bst_find, bst_delete)}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "cc8d0436",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Проведём замеры\n",
    "report = [[\"Структура\", \"Режим\", \"Вставка\", \"Поиск\", \"Удаление\"]]\n",
    "records = {\"Cлучайный\" : records_shuffled, \"Отсортированный\" : records_sorted}\n",
    "\n",
    "TEST_ITERATIONS_NUM = 5\n",
    "\n",
    "for _ in range(TEST_ITERATIONS_NUM):\n",
    "    for mode, data in records.items():\n",
    "        for struct_name, fns in func_list.items():\n",
    "            result = test(data, *fns)\n",
    "            row = [struct_name, mode,\n",
    "                result[\"insert_time\"],\n",
    "                result[\"find_time\"],\n",
    "                result[\"delete_time\"]]\n",
    "            report.append(row)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "2eedf056",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Сохраним данные в csv\n",
    "import csv\n",
    "with open(\"data/task1/results.csv\", \"w\", newline=\"\") as f:\n",
    "    writer = csv.writer(f)\n",
    "    writer.writerows(report)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94335af1",
   "metadata": {},
   "source": [
    "# 3. Построение графиков и их анализ"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "cad64d2f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 53580 characters]",
      "text/plain": [
       "<Figure size 1500x500 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "df = pd.read_csv('data/task1/results.csv')\n",
    "mean_times = df.groupby(['Структура', 'Режим'])[['Вставка', 'Поиск', 'Удаление']].mean().reset_index()\n",
    "structures = mean_times['Структура'].unique()\n",
    "modes = mean_times['Режим'].unique()\n",
    "\n",
    "fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n",
    "operations = ['Вставка', 'Поиск', 'Удаление']\n",
    "\n",
    "for ax, op in zip(axes, operations):\n",
    "    # a\n",
    "    x = np.arange(len(structures))\n",
    "    width = 0.35\n",
    "    \n",
    "    random_vals = []\n",
    "    sorted_vals = []\n",
    "    for s in structures:\n",
    "        random_row = mean_times[(mean_times['Структура']==s) & (mean_times['Режим']=='Cлучайный')]\n",
    "        sorted_row = mean_times[(mean_times['Структура']==s) & (mean_times['Режим']=='Отсортированный')]\n",
    "        random_vals.append(random_row[op].values[0] if not random_row.empty else 0)\n",
    "        sorted_vals.append(sorted_row[op].values[0] if not sorted_row.empty else 0)\n",
    "    \n",
    "    ax.bar(x - width/2, random_vals, width, label='Случайный')\n",
    "    ax.bar(x + width/2, sorted_vals, width, label='Отсортированный')\n",
    "    ax.set_xticks(x)\n",
    "    ax.set_xticklabels(structures)\n",
    "    ax.set_ylabel('Время (сек)')\n",
    "    ax.set_title(op)\n",
    "    ax.legend()\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig('data/task1/performance_plot.png', dpi=150)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "1d86131d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Структура</th>\n",
       "      <th>Режим</th>\n",
       "      <th>Вставка</th>\n",
       "      <th>Поиск</th>\n",
       "      <th>Удаление</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Бинарное дерево</td>\n",
       "      <td>Cлучайный</td>\n",
       "      <td>0.011921</td>\n",
       "      <td>0.000150</td>\n",
       "      <td>0.000137</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Бинарное дерево</td>\n",
       "      <td>Отсортированный</td>\n",
       "      <td>0.122171</td>\n",
       "      <td>0.001627</td>\n",
       "      <td>0.000873</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Связанный список</td>\n",
       "      <td>Cлучайный</td>\n",
       "      <td>0.090039</td>\n",
       "      <td>0.000893</td>\n",
       "      <td>0.000605</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Связанный список</td>\n",
       "      <td>Отсортированный</td>\n",
       "      <td>0.162447</td>\n",
       "      <td>0.001707</td>\n",
       "      <td>0.000915</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Хэш-таблица</td>\n",
       "      <td>Cлучайный</td>\n",
       "      <td>0.044831</td>\n",
       "      <td>0.000618</td>\n",
       "      <td>0.000324</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Хэш-таблица</td>\n",
       "      <td>Отсортированный</td>\n",
       "      <td>0.049369</td>\n",
       "      <td>0.000527</td>\n",
       "      <td>0.000272</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          Структура            Режим   Вставка     Поиск  Удаление\n",
       "0   Бинарное дерево        Cлучайный  0.011921  0.000150  0.000137\n",
       "1   Бинарное дерево  Отсортированный  0.122171  0.001627  0.000873\n",
       "2  Связанный список        Cлучайный  0.090039  0.000893  0.000605\n",
       "3  Связанный список  Отсортированный  0.162447  0.001707  0.000915\n",
       "4       Хэш-таблица        Cлучайный  0.044831  0.000618  0.000324\n",
       "5       Хэш-таблица  Отсортированный  0.049369  0.000527  0.000272"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = pd.read_csv('data/task1/results.csv')\n",
    "df.groupby(['Структура', 'Режим'])[['Вставка', 'Поиск', 'Удаление']].mean().reset_index()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9a486a5",
   "metadata": {},
   "source": [
    "# 4. Анализ результатов\n",
    "---\n",
    "### 4.1 Влияние порядка данных на вставку в BST\n",
    "При вставке элементов в отсортированном порядке в бинарное дерево оно превращается в связный список - это связанно с тем, что все элементы вставляются в одну ветвь дерева. Сложность всех операций приблтижается к **O(n)**. Вставка в BST на отсортированных данных заняла 0.122171c вместо 0.011921с, разница более чем в 10 раз. Причём, время вставки даже хуже, чем у чистого связнного списка - это связанно с дополнительными расходами бинарного дерева. Поиск так же ухудшился, примерно в 10 раз, а с ним ухудшилось и удаление.\n",
    "\n",
    "### 4.2 Почему хэш-таблица почти не чувствительна к порядку\n",
    "По графикам видно, что для хэш-таблицы время операций почти не изменяется. Исключение составляет лишь поиск, его время больше на отсортированных данных. Это связано с особенностями теста - поиск 10 несуществующих записей ухудшают результат для отсортированных данных. Все эти наблюдения связаны с механизмом работы хэш-таблицы - она распределяет данные по корзинам независимо от порядка поступления. Получается, что сложность всех операций **O(1)**\n",
    "\n",
    "### 4.3 Почему связный список всегда медленен при поиске\n",
    "Для поиска в связном списке нужно просматривать все элементы по порядку, так что сложность всех операций **O(n)**\n",
    "\n",
    "### 4.4 Сравнение удаления\n",
    "\n",
    "- **Связаный список** удаление требует сначала найти элемент за O(n), затем переставить ссылки за O(1). Время удаления (0.000605 с) близко ко времени поиска, что логично.\n",
    "- **Хеш-таблица:** при удалении, поиск корзины за O(1) и поиск в коротком связаном списке за O(n) удаляется элемент. Время удаления (0.000324) меньше, чем в списке.\n",
    "- **BST:** на случайных данных удаление очень быстрое (0.000137 с) благодаря логарифмической высоте. На отсортированных данных время возрастает до 0.000873, что отражает деградацию до O(n)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7ed5470",
   "metadata": {},
   "source": [
    "# 5. Вывод\n",
    "На основе полученных результатов можно сформулировать следующие рекомендации:\n",
    "\n",
    "- Хеш-таблица – хороший выбор, если приоритетом является максимальная скорость вставки, поиска и удаления по ключу, а порядок элементов не имеет значения. Время операций близко к **O(1)** и практически не зависит от упорядоченности входных данных. Идеальна для кэшей, словарей и частых запросов по идентификатору.\n",
    "\n",
    "- Двоичное дерево поиска – следует применять, когда необходимо получать данные в отсортированном порядке. На случайных данных демонстрирует хорошую производительность **O(log n)**, однако при поступлении заранее отсортированных элементов вырождается в связный список с падением скорости до **O(n)**.\n",
    "\n",
    "- Связный список – демонстрирует линейную сложность поиска и удаления **O(n)** что делает его непригодным для задач с частым доступом к произвольным элементам. Может быть оправдан только в узких случаях, где вставки и удаления происходят исключительно в начале или конце коллекции (очереди, стеки) и не требуется поиск.\n",
    "\n",
    "Таким образом, для реальных задач чаще всего выбирают хеш-таблицы или сбалансированные деревья в зависимости от требований к упорядоченности данных.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.14.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
