2026-rff_mp/filippovavm/docs/МП.ipynb
2026-05-24 18:27:35 +03:00

595 lines
27 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"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
}