diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c9ebf2d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:system" +} \ No newline at end of file diff --git a/benchmark_chart.png b/benchmark_chart.png index 84c386e..19c11ba 100644 Binary files a/benchmark_chart.png and b/benchmark_chart.png differ diff --git a/results.csv b/results.csv index 4891afc..fedd423 100644 --- a/results.csv +++ b/results.csv @@ -1,91 +1,91 @@ структура,режим,операция,повторение,время (сек) -LinkedList,случайный,вставка,1,0.13309969999954774 -LinkedList,случайный,поиск,1,0.009379099999932805 -LinkedList,случайный,удаление,1,0.004902000000583939 -LinkedList,случайный,вставка,2,0.11298999999962689 -LinkedList,случайный,поиск,2,0.00728259999959846 -LinkedList,случайный,удаление,2,0.003859800000100222 -LinkedList,случайный,вставка,3,0.12435430000004999 -LinkedList,случайный,поиск,3,0.006995800000368035 -LinkedList,случайный,удаление,3,0.0038894999997864943 -LinkedList,случайный,вставка,4,0.11281289999988076 -LinkedList,случайный,поиск,4,0.0073154000001522945 -LinkedList,случайный,удаление,4,0.004016099999716971 -LinkedList,случайный,вставка,5,0.12206900000001042 -LinkedList,случайный,поиск,5,0.007770400000481459 -LinkedList,случайный,удаление,5,0.0039008999992802273 -LinkedList,отсортированный,вставка,1,0.12199670000063634 -LinkedList,отсортированный,поиск,1,0.006072700000004261 -LinkedList,отсортированный,удаление,1,0.004619700000148441 -LinkedList,отсортированный,вставка,2,0.11778119999962655 -LinkedList,отсортированный,поиск,2,0.005873400000382389 -LinkedList,отсортированный,удаление,2,0.004716499999631196 -LinkedList,отсортированный,вставка,3,0.11716749999959575 -LinkedList,отсортированный,поиск,3,0.006211500000063097 -LinkedList,отсортированный,удаление,3,0.004824199999347911 -LinkedList,отсортированный,вставка,4,0.1168800999994346 -LinkedList,отсортированный,поиск,4,0.00615879999986646 -LinkedList,отсортированный,удаление,4,0.004850500000429747 -LinkedList,отсортированный,вставка,5,0.12217359999976907 -LinkedList,отсортированный,поиск,5,0.0060359999997672276 -LinkedList,отсортированный,удаление,5,0.0046704000005775015 -HashTable,случайный,вставка,1,0.001177799999823037 -HashTable,случайный,поиск,1,3.729999934876105e-05 -HashTable,случайный,удаление,1,2.009999934671214e-05 -HashTable,случайный,вставка,2,0.001008700000056706 -HashTable,случайный,поиск,2,3.290000040578889e-05 -HashTable,случайный,удаление,2,1.6900000446185004e-05 -HashTable,случайный,вставка,3,0.0010118999998667277 -HashTable,случайный,поиск,3,3.500000002532033e-05 -HashTable,случайный,удаление,3,2.0599999515980016e-05 -HashTable,случайный,вставка,4,0.0010044999999081483 -HashTable,случайный,поиск,4,3.1800000215298496e-05 -HashTable,случайный,удаление,4,1.700000029813964e-05 -HashTable,случайный,вставка,5,0.001006999999844993 -HashTable,случайный,поиск,5,3.1599999601894524e-05 -HashTable,случайный,удаление,5,1.649999921937706e-05 -HashTable,отсортированный,вставка,1,0.0009728999993967591 -HashTable,отсортированный,поиск,1,3.0100000003585592e-05 -HashTable,отсортированный,удаление,1,1.9299999621580355e-05 -HashTable,отсортированный,вставка,2,0.0009723000002850313 -HashTable,отсортированный,поиск,2,2.9599999834317714e-05 -HashTable,отсортированный,удаление,2,1.919999976962572e-05 -HashTable,отсортированный,вставка,3,0.0009663999999247608 -HashTable,отсортированный,поиск,3,3.0500000320898835e-05 -HashTable,отсортированный,удаление,3,1.9699999938893598e-05 -HashTable,отсортированный,вставка,4,0.0011242000000493135 -HashTable,отсортированный,поиск,4,3.400000059627928e-05 -HashTable,отсортированный,удаление,4,2.080000012938399e-05 -HashTable,отсортированный,вставка,5,0.0009773000001587206 -HashTable,отсортированный,поиск,5,3.100000049016671e-05 -HashTable,отсортированный,удаление,5,1.9699999938893598e-05 -BST,случайный,вставка,1,0.004384899999422487 -BST,случайный,поиск,1,0.0001329000006080605 -BST,случайный,удаление,1,8.230000003095483e-05 -BST,случайный,вставка,2,0.004304400000364694 -BST,случайный,поиск,2,0.00014240000018617138 -BST,случайный,удаление,2,8.129999969241908e-05 -BST,случайный,вставка,3,0.004376699999738776 -BST,случайный,поиск,3,0.00014179999925545417 -BST,случайный,удаление,3,8.269999943877338e-05 -BST,случайный,вставка,4,0.004673599999478029 -BST,случайный,поиск,4,0.0001511999998911051 -BST,случайный,удаление,4,0.00012150000020483276 -BST,случайный,вставка,5,0.004335399999945366 -BST,случайный,поиск,5,0.00013080000007903436 -BST,случайный,удаление,5,7.93999997767969e-05 -BST,отсортированный,вставка,1,0.7882552000000942 -BST,отсортированный,поиск,1,0.01960369999960676 -BST,отсортированный,удаление,1,0.013939499999651161 -BST,отсортированный,вставка,2,0.7601769000002605 -BST,отсортированный,поиск,2,0.019820599999547994 -BST,отсортированный,удаление,2,0.01359989999946265 -BST,отсортированный,вставка,3,0.741146899999876 -BST,отсортированный,поиск,3,0.018209700000625162 -BST,отсортированный,удаление,3,0.012622499999451975 -BST,отсортированный,вставка,4,0.7348717000004399 -BST,отсортированный,поиск,4,0.01830170000084763 -BST,отсортированный,удаление,4,0.012606900000719179 -BST,отсортированный,вставка,5,0.7420961000007082 -BST,отсортированный,поиск,5,0.018224400000690366 -BST,отсортированный,удаление,5,0.012539400000605383 +LinkedList,случайный,вставка,1,0.11277499999999918 +LinkedList,случайный,поиск,1,0.006451900000001842 +LinkedList,случайный,удаление,1,0.003666800000019066 +LinkedList,случайный,вставка,2,0.10957519999999477 +LinkedList,случайный,поиск,2,0.0064522000000124535 +LinkedList,случайный,удаление,2,0.003728399999999965 +LinkedList,случайный,вставка,3,0.109341000000029 +LinkedList,случайный,поиск,3,0.006500899999991816 +LinkedList,случайный,удаление,3,0.0038011999999980617 +LinkedList,случайный,вставка,4,0.1110247999999956 +LinkedList,случайный,поиск,4,0.006476099999986218 +LinkedList,случайный,удаление,4,0.0036711999999852196 +LinkedList,случайный,вставка,5,0.11070879999999761 +LinkedList,случайный,поиск,5,0.0064365000000066175 +LinkedList,случайный,удаление,5,0.003677400000015041 +LinkedList,отсортированный,вставка,1,0.11145989999999983 +LinkedList,отсортированный,поиск,1,0.005919100000028266 +LinkedList,отсортированный,удаление,1,0.004607999999961976 +LinkedList,отсортированный,вставка,2,0.11035959999998113 +LinkedList,отсортированный,поиск,2,0.005787800000007337 +LinkedList,отсортированный,удаление,2,0.004521399999987352 +LinkedList,отсортированный,вставка,3,0.11167460000001483 +LinkedList,отсортированный,поиск,3,0.006033900000034009 +LinkedList,отсортированный,удаление,3,0.004642099999955462 +LinkedList,отсортированный,вставка,4,0.11137540000004265 +LinkedList,отсортированный,поиск,4,0.0057623999999805164 +LinkedList,отсортированный,удаление,4,0.004488399999956982 +LinkedList,отсортированный,вставка,5,0.11472939999998744 +LinkedList,отсортированный,поиск,5,0.005918899999983296 +LinkedList,отсортированный,удаление,5,0.004527400000029047 +HashTable,случайный,вставка,1,0.0012038000000416105 +HashTable,случайный,поиск,1,3.46000000490676e-05 +HashTable,случайный,удаление,1,2.0000000006348273e-05 +HashTable,случайный,вставка,2,0.0010017999999831773 +HashTable,случайный,поиск,2,3.1499999977313564e-05 +HashTable,случайный,удаление,2,1.720000000204891e-05 +HashTable,случайный,вставка,3,0.0010786999999936597 +HashTable,случайный,поиск,3,3.4999999968476914e-05 +HashTable,случайный,удаление,3,1.9199999996999395e-05 +HashTable,случайный,вставка,4,0.0009729999999876782 +HashTable,случайный,поиск,4,3.090000001293447e-05 +HashTable,случайный,удаление,4,1.7399999990175274e-05 +HashTable,случайный,вставка,5,0.0009918999999740663 +HashTable,случайный,поиск,5,3.049999997983832e-05 +HashTable,случайный,удаление,5,1.6399999992700032e-05 +HashTable,отсортированный,вставка,1,0.0009690999999634187 +HashTable,отсортированный,поиск,1,3.5900000000310683e-05 +HashTable,отсортированный,удаление,1,1.8599999975776882e-05 +HashTable,отсортированный,вставка,2,0.0009684999999990396 +HashTable,отсортированный,поиск,2,3.0100000003585592e-05 +HashTable,отсортированный,удаление,2,1.759999997830164e-05 +HashTable,отсортированный,вставка,3,0.001049399999999423 +HashTable,отсортированный,поиск,3,3.1100000001060835e-05 +HashTable,отсортированный,удаление,3,1.8299999965165625e-05 +HashTable,отсортированный,вставка,4,0.000957400000004327 +HashTable,отсортированный,поиск,4,2.9900000015459227e-05 +HashTable,отсортированный,удаление,4,1.7500000012660166e-05 +HashTable,отсортированный,вставка,5,0.0009610000000179753 +HashTable,отсортированный,поиск,5,2.9500000039206498e-05 +HashTable,отсортированный,удаление,5,1.770000000078653e-05 +BST,случайный,вставка,1,0.004438699999980145 +BST,случайный,поиск,1,0.00013129999996408515 +BST,случайный,удаление,1,8.220000000846994e-05 +BST,случайный,вставка,2,0.004387300000018968 +BST,случайный,поиск,2,0.00012960000003658934 +BST,случайный,удаление,2,7.93000000385291e-05 +BST,случайный,вставка,3,0.004303200000038032 +BST,случайный,поиск,3,0.0001283999999941443 +BST,случайный,удаление,3,8.050000002413071e-05 +BST,случайный,вставка,4,0.004263700000024073 +BST,случайный,поиск,4,0.00012770000000728032 +BST,случайный,удаление,4,7.650000003422974e-05 +BST,случайный,вставка,5,0.004260499999986678 +BST,случайный,поиск,5,0.00012939999999161955 +BST,случайный,удаление,5,7.969999995793842e-05 +BST,отсортированный,вставка,1,0.7347394000000236 +BST,отсортированный,поиск,1,0.018492599999945014 +BST,отсортированный,удаление,1,0.013188500000012482 +BST,отсортированный,вставка,2,0.7407673999999815 +BST,отсортированный,поиск,2,0.018303899999978057 +BST,отсортированный,удаление,2,0.012820200000021487 +BST,отсортированный,вставка,3,0.7382302000000323 +BST,отсортированный,поиск,3,0.01834120000000894 +BST,отсортированный,удаление,3,0.012760500000013053 +BST,отсортированный,вставка,4,0.7377933000000212 +BST,отсортированный,поиск,4,0.01829400000002579 +BST,отсортированный,удаление,4,0.012722399999972822 +BST,отсортированный,вставка,5,0.7360582999999679 +BST,отсортированный,поиск,5,0.01833809999999403 +BST,отсортированный,удаление,5,0.012751900000012029 diff --git a/sorokinfi/427.md b/sorokinfi/427.md deleted file mode 100644 index f2b1dc8..0000000 --- a/sorokinfi/427.md +++ /dev/null @@ -1,348 +0,0 @@ -import csv -import random -import sys -import time -from collections import defaultdict -import pandas as pd -import matplotlib.pyplot as plt - -# увеличиваем лимит рекурсии -sys.setrecursionlimit(25000) - - -# 1. связный список, узел: {'name': 'Имя', 'phone': '123', 'next': None} - -# проходит до конца и добавляет в конец -def ll_insert(head, name, phone): - new_node = {'name': name, 'phone': phone, 'next': None} - if head is None: - return new_node - current = head - while current['next'] is not None: - current = current['next'] - current['next'] = new_node - return head - -# ищет узел, возвращает телефон или None -def ll_find(head, name): - current = head - while current is not None: - if current['name'] == name: - return current['phone'] - current = current['next'] - return None - -# удаляет узел, возвращает новую голову -def ll_delete(head, name): - if head is None: - return None - if head['name'] == name: - return head['next'] - - current = head - while current['next'] is not None: - if current['next']['name'] == name: - current['next'] = current['next']['next'] - return head - current = current['next'] - return head - -# собирает все записи в список и сортирует -def ll_list_all(head): - records = [] - current = head - while current is not None: - records.append((current['name'], current['phone'])) - current = current['next'] - records.sort(key=lambda x: x[0]) - return records - - -# 2. хеш-таблица - -# хеш-функция для вычисления бекета -def ht_hash(name, size): - return hash(name) % size - -# вычисляет индекс, вызывает ll_insert для соответствующего бакета -def ht_insert(buckets, name, phone): - size = len(buckets) - idx = ht_hash(name, size) - buckets[idx] = ll_insert(buckets[idx], name, phone) - -# поиск по хеш-таблице -def ht_find(buckets, name): - size = len(buckets) - idx = ht_hash(name, size) - return ll_find(buckets[idx], name) - -# удаление из хеш-таблицы -def ht_delete(buckets, name): - size = len(buckets) - idx = ht_hash(name, size) - buckets[idx] = ll_delete(buckets[idx], name) - -# собирает все записи из всех бакетов и сортирует -def ht_list_all(buckets): - all_records = [] - for head in buckets: - current = head - while current is not None: - all_records.append((current['name'], current['phone'])) - current = current['next'] - all_records.sort(key=lambda x: x[0]) - return all_records - - -# 3. двоичное дерево поиска -# узел — словарь: {'name': 'Имя', 'phone': '123', 'left': None, 'right': None} - -# рекурсивно или итеративно вставляет, возвращает новый корень (если корень меняется) -def bst_insert(root, name, phone): - if root is None: - return {'name': name, 'phone': phone, 'left': None, 'right': None} - - if name < root['name']: - root['left'] = bst_insert(root['left'], name, phone) - elif name > root['name']: - root['right'] = bst_insert(root['right'], name, phone) - else: - root['phone'] = phone - return root - -# поиск -def bst_find(root, name): - if root is None: - return None - if name == root['name']: - return root['phone'] - elif name < root['name']: - return bst_find(root['left'], name) - else: - return bst_find(root['right'], name) - -# удаление, возвращает новый корень -def bst_delete(root, name): - if root is None: - return None - - if name < root['name']: - root['left'] = bst_delete(root['left'], name) - elif name > root['name']: - root['right'] = bst_delete(root['right'], name) - else: - # одна ветвь или её отсутствие - if root['left'] is None: - return root['right'] - if root['right'] is None: - return root['left'] - # две ветви - successor = root['right'] - while successor['left'] is not None: - successor = successor['left'] - - root['name'] = successor['name'] - root['phone'] = successor['phone'] - root['right'] = bst_delete(root['right'], successor['name']) - - return root - -# центрированный обход (рекурсивно собирает записи в отсортированном порядке) -def bst_list_all(root): - records = [] - def _inorder(node): - if node is not None: - _inorder(node['left']) - records.append((node['name'], node['phone'])) - _inorder(node['right']) - _inorder(root) - return records - - -# 4. ЭКСПЕРИМЕНТАЛЬНАЯ ЧАСТЬ - -def run_experiments(): - N = 3000 - HASH_SIZE = 1007 - - print(f"генерация тестовых данных для N = {N}...") - records_sorted = [(f"User_{i:05d}", f"+7999123{i:04d}") for i in range(N)] - records_shuffled = records_sorted.copy() - random.seed(42) - random.shuffle(records_shuffled) - - # подготовка выборок - existing_sample = [r[0] for r in random.sample(records_sorted, min(100, N))] - non_existing_sample = [f"None_{i}" for i in range(10)] - search_names = existing_sample + non_existing_sample - - delete_names = [r[0] for r in random.sample(records_sorted, min(50, N))] - csv_rows = [["структура", "режим", "операция", "повторение", "время (сек)"]] - modes = [("случайный", records_shuffled), ("отсортированный", records_sorted)] - - print("запуск экспериментов (5 повторений для каждого режима)") - -# ТЕСТ: СВЯЗНЫЙ СПИСОК - - for mode_name, data in modes: - for rep in range(1, 6): - head = None - t_start = time.perf_counter() - for name, phone in data: - head = ll_insert(head, name, phone) - t_end = time.perf_counter() - csv_rows.append(["LinkedList", mode_name, "вставка", rep, t_end - t_start]) - - t_start = time.perf_counter() - for name in search_names: - ll_find(head, name) - t_end = time.perf_counter() - csv_rows.append(["LinkedList", mode_name, "поиск", rep, t_end - t_start]) - - t_start = time.perf_counter() - for name in delete_names: - head = ll_delete(head, name) - t_end = time.perf_counter() - csv_rows.append(["LinkedList", mode_name, "удаление", rep, t_end - t_start]) - -# ТЕСТ: ХЕШ-ТАБЛИЦА - - for mode_name, data in modes: - for rep in range(1, 6): - buckets = [None] * HASH_SIZE - t_start = time.perf_counter() - for name, phone in data: - ht_insert(buckets, name, phone) - t_end = time.perf_counter() - csv_rows.append(["HashTable", mode_name, "вставка", rep, t_end - t_start]) - - t_start = time.perf_counter() - for name in search_names: - ht_find(buckets, name) - t_end = time.perf_counter() - csv_rows.append(["HashTable", mode_name, "поиск", rep, t_end - t_start]) - - t_start = time.perf_counter() - for name in delete_names: - ht_delete(buckets, name) - t_end = time.perf_counter() - csv_rows.append(["HashTable", mode_name, "удаление", rep, t_end - t_start]) - -# ТЕСТ: ДЕРЕВО ПОИСКА (BST) - - for mode_name, data in modes: - for rep in range(1, 6): - root = None - t_start = time.perf_counter() - for name, phone in data: - root = bst_insert(root, name, phone) - t_end = time.perf_counter() - csv_rows.append(["BST", mode_name, "вставка", rep, t_end - t_start]) - - t_start = time.perf_counter() - for name in search_names: - bst_find(root, name) - t_end = time.perf_counter() - csv_rows.append(["BST", mode_name, "поиск", rep, t_end - t_start]) - - t_start = time.perf_counter() - for name in delete_names: - root = bst_delete(root, name) - t_end = time.perf_counter() - csv_rows.append(["BST", mode_name, "удаление", rep, t_end - t_start]) - -# сохранение в csv - with open("results.csv", "w", newline="", encoding="utf-8") as f: - writer = csv.writer(f) - writer.writerows(csv_rows) - print("\nвсе замеры сохранены в файл 'results.csv'.") - - show_summary(csv_rows) - -# функция для подсчета и вывода среднего времени -def show_summary(rows): - summary = defaultdict(list) - for row in rows[1:]: - struct, mode, op, rep, elapsed = row - summary[(struct, mode, op)].append(elapsed) - - print("\nСВОДНЫЕ РЕЗУЛЬТАТЫ (СРЕДНЕЕ ВРЕМЯ ИЗ 5 ЗАПУСКОВ)") - print(f"{'структура':<12} | {'режим данных':<15} | {'операция':<10} | {'время (сек)':<12}") - print("-" * 59) - for (struct, mode, op), times in sorted(summary.items()): - avg_time = sum(times) / len(times) - print(f"{struct:<12} | {mode:<15} | {op:<10} | {avg_time:.6f}") - - - -# 5. АНАЛИЗ РЕЗУЛЬТАТОВ - -def plot_results(csv_filename="results.csv"): - print("построение графика") - try: - df = pd.read_csv(csv_filename) - - df_insert = df[df["операция"] == "вставка"] - - pivot_df = df_insert.pivot_table( - index="структура", - columns="режим", - values="время (сек)", - aggfunc="mean" - ) - - pivot_df.plot(kind="bar", figsize=(10, 6), color=['#1f77b4', '#ff7f0e']) - - plt.title("сравнение времени вставки (N=3000)") - plt.ylabel("среднее время выполнения (сек)") - plt.xlabel("структура данных") - plt.xticks(rotation=0) - plt.grid(axis='y', linestyle='--', alpha=0.7) - plt.savefig("benchmark_chart.png") - print("график сохранен как benchmark_chart.png") - plt.show() - - except FileNotFoundError: - print(f"файл {csv_filename} не найден. сначала запустите тесты.") - - -def print_report(): - report = """ - - 5. АНАЛИЗ РЕЗУЛЬТАТОВ ЭКСПЕРИМЕНТОВ - -1. Влияние порядка входных данных на скорость вставки в BST: - - На случайных данных BST строится сбалансированным. Высота дерева составляет - примерно O(log N), поэтому вставка происходит почти мгновенно. - - На отсортированных данных происходит ДЕГРАДАЦИЯ дерева. Каждый элемент больше - предыдущего и вставляется строго вправо. Дерево вырождается в связный список. - Сложность возрастает до O(N), что отчетливо видно по гигантскому пику на графике. - -2. Чувствительность Хеш-таблицы к порядку: - - Хеш-таблица НЕ ЧУВСТВИТЕЛЬНА к порядку данных. Математическая хеш-функция - превращает любое имя в хаотичный индекс и равномерно распределяет записи - по бакетам. В обоих режимах операции выполняются за константное время O(1). - -3. Почему связный список всегда медленен при поиске: - - У связного списка нет индексов для прямого доступа. Поиск всегда линейный O(N) - — алгоритм вынужден последовательно перебирать элементы от головы к хвосту. - -4. Как удаление работает в каждой структуре: - - Связный список: O(N) затрачивается на линейный поиск узла, само удаление — O(1). - - Хеш-таблица: O(1) нахождение бакета по хешу, удаление из цепочки коллизий мгновенно. - - BST: В среднем O(log N), в худшем O(N). Требует поиска узла и перестройки связей - (замена удаляемого узла на его потомка или минимальный элемент правого поддерева). - -ВЫВОД: - - ДЛЯ ЧАСТЫХ ВСТАВОК И ПОИСКА: Идеально подходит Хеш-таблица благодаря скорости O(1). - - ДЛЯ ПОЛУЧЕНИЯ ДАННЫХ В ПОРЯДКЕ (АЛФАВИТНОМ): Стоит выбирать Двоичное дерево поиска - (BST), так как обход дерева (In-order traversal) сразу выдает отсортированные данные. - """ - print(report) - -if __name__ == "__main__": - run_experiments() - plot_results() - print_report() - -