diff --git a/krasnovia/lab1/docs/data/lab1.py b/krasnovia/lab1/docs/data/lab1.py new file mode 100644 index 0000000..ef15ae5 --- /dev/null +++ b/krasnovia/lab1/docs/data/lab1.py @@ -0,0 +1,249 @@ +import time +import random +import csv +import os +import sys +import matplotlib.pyplot as plt + +sys.setrecursionlimit(20000) + +BASE_PATH = r"E:\репозиторий\2026-rff_mp\krasnovia\lab1" +DOCS_PATH = os.path.join(BASE_PATH, "docs") +DATA_PATH = os.path.join(DOCS_PATH, "data") + +for p in [DOCS_PATH, DATA_PATH]: + if not os.path.exists(p): + os.makedirs(p) + +def ll_insert(head, name, phone): + return {'name': name, 'phone': phone, 'next': head} + +def ll_find(head, name): + curr = head + while curr: + if curr['name'] == name: return curr['phone'] + curr = curr['next'] + return None + +def ll_delete(head, name): + if not head: return None + if head['name'] == name: return head['next'] + curr = head + while curr['next']: + if curr['next']['name'] == name: + curr['next'] = curr['next']['next'] + return head + curr = curr['next'] + return head + +def ll_list_all(head): + res = [] + curr = head + while curr: + res.append((curr['name'], curr['phone'])) + curr = curr['next'] + return sorted(res) + +def ht_insert(buckets, name, phone): + idx = hash(name) % len(buckets) + buckets[idx] = ll_insert(buckets[idx], name, phone) + +def ht_find(buckets, name): + idx = hash(name) % len(buckets) + return ll_find(buckets[idx], name) + +def ht_delete(buckets, name): + idx = hash(name) % len(buckets) + buckets[idx] = ll_delete(buckets[idx], name) + +def ht_list_all(buckets): + all_recs = [] + for b in buckets: + curr = b + while curr: + all_recs.append((curr['name'], curr['phone'])) + curr = curr['next'] + return sorted(all_recs) + +def bst_insert(root, name, phone): + if not root: + 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 not root: return None + if root['name'] == name: return root['phone'] + if name < root['name']: return bst_find(root['left'], name) + return bst_find(root['right'], name) + +def bst_delete(root, name): + if not root: 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 not root['left']: return root['right'] + if not root['right']: return root['left'] + temp = root['right'] + while temp['left']: temp = temp['left'] + root['name'], root['phone'] = temp['name'], temp['phone'] + root['right'] = bst_delete(root['right'], temp['name']) + return root + +def bst_list_all(root): + res = [] + def _inorder(node): + if node: + _inorder(node['left']) + res.append((node['name'], node['phone'])) + _inorder(node['right']) + _inorder(root) + return res + +all_results_csv = [] +summary_for_report = [] + +def run_experiment(struct_type, mode, data): + print(f"Processing: {struct_type} ({mode})") + ins_times, find_times, del_times = [], [], [] + + for i in range(5): + container = [None]*1000 if struct_type == "HashTable" else None + + start = time.perf_counter() + for n, p in data: + if struct_type == "LinkedList": container = ll_insert(container, n, p) + elif struct_type == "HashTable": ht_insert(container, n, p) + elif struct_type == "BST": container = bst_insert(container, n, p) + ins_times.append(time.perf_counter() - start) + + search_list = [d[0] for d in random.sample(data, 100)] + [f"None_{j}" for j in range(10)] + start = time.perf_counter() + for s_name in search_list: + if struct_type == "LinkedList": ll_find(container, s_name) + elif struct_type == "HashTable": ht_find(container, s_name) + elif struct_type == "BST": bst_find(container, s_name) + find_times.append(time.perf_counter() - start) + + del_list = [d[0] for d in random.sample(data, 50)] + start = time.perf_counter() + for d_name in del_list: + if struct_type == "LinkedList": container = ll_delete(container, d_name) + elif struct_type == "HashTable": ht_delete(container, d_name) + elif struct_type == "BST": container = bst_delete(container, d_name) + del_times.append(time.perf_counter() - start) + + all_results_csv.append([struct_type, mode, f"Run {i+1}", ins_times[-1], find_times[-1], del_times[-1]]) + + avg_ins = sum(ins_times) / 5 + avg_find = sum(find_times) / 5 + avg_del = sum(del_times) / 5 + + all_results_csv.append([struct_type, mode, "AVERAGE", avg_ins, avg_find, avg_del]) + summary_for_report.append({"name": struct_type, "mode": mode, "ins": avg_ins, "find": avg_find, "del": avg_del}) + +N = 10000 +records_raw = [(f"User_{i:05d}", f"8-900-{random.randint(100, 999)}") for i in range(N)] +records_shuffled = records_raw[:] +random.shuffle(records_shuffled) +records_sorted = sorted(records_raw) + +for m_name, d_set in [("случайный", records_shuffled), ("сортированный", records_sorted)]: + for s_type in ["LinkedList", "HashTable", "BST"]: + run_experiment(s_type, m_name, d_set) + +with open(os.path.join(DATA_PATH, "results.csv"), "w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(["Структура", "Режим", "Итерация", "Вставка", "Поиск", "Удаление"]) + writer.writerows(all_results_csv) + +def create_plots(): + labels = ["insert", "find", "delete"] + structs = ["LinkedList", "HashTable", "BST"] + colors = ['#5dade2', '#e67e22', '#58d68d'] + + fig1, axs = plt.subplots(1, 3, figsize=(18, 6)) + fig1.suptitle("Влияние порядка данных на время операций", fontsize=16, fontweight='bold') + + for i, s_name in enumerate(structs): + rand_data = next(r for r in summary_for_report if r['name'] == s_name and r['mode'] == "случайный") + sort_data = next(r for r in summary_for_report if r['name'] == s_name and r['mode'] == "сортированный") + + x = [0, 1, 2] + width = 0.35 + axs[i].bar([p - width/2 for p in x], [rand_data['ins'], rand_data['find'], rand_data['del']], width, label='случайный', color=colors[0]) + axs[i].bar([p + width/2 for p in x], [sort_data['ins'], sort_data['find'], sort_data['del']], width, label='сортированный', color='#e74c3c', alpha=0.8) + + axs[i].set_title(s_name, fontweight='bold') + axs[i].set_xticks(x) + axs[i].set_xticklabels(labels) + axs[i].set_ylabel("Время (с)") + axs[i].legend() + axs[i].grid(axis='y', linestyle='--', alpha=0.3) + + plt.tight_layout(rect=[0, 0.03, 1, 0.95]) + plt.savefig(os.path.join(DATA_PATH, "order_impact.png")) + + fig2, axs2 = plt.subplots(1, 3, figsize=(18, 6)) + fig2.suptitle(f"Сравнение структур данных (N={N})", fontsize=16, fontweight='bold') + + op_keys = ['ins', 'find', 'del'] + op_names = ['insert', 'find', 'delete'] + + for i, op in enumerate(op_keys): + plot_labels = [] + plot_values = [] + plot_colors = [] + + for r in summary_for_report: + plot_labels.append(f"{r['name']}\n({r['mode'][:4]})") + plot_values.append(r[op]) + if r['name'] == "LinkedList": plot_colors.append(colors[0]) + elif r['name'] == "HashTable": plot_colors.append(colors[1]) + else: plot_colors.append(colors[2]) + + bars = axs2[i].bar(plot_labels, plot_values, color=plot_colors) + axs2[i].set_title(f"Операция: {op_names[i]}", fontweight='bold') + axs2[i].set_ylabel("Время (с)") + axs2[i].tick_params(axis='x', rotation=15) + + for bar in bars: + height = bar.get_height() + axs2[i].text(bar.get_x() + bar.get_width()/2., height, f'{height:.4f}', ha='center', va='bottom', fontsize=8) + + plt.tight_layout(rect=[0, 0.03, 1, 0.95]) + plt.savefig(os.path.join(DATA_PATH, "struct_comparison.png")) + +create_plots() + +with open(os.path.join(DOCS_PATH, "report.md"), "w", encoding="utf-8") as f: + f.write("# Технический отчет: Сравнительный анализ структур данных\n\n") + f.write("## 1. Вводные данные\n") + f.write(f"Целью теста является оценка производительности LinkedList, HashTable и BST на массиве из {N} элементов. ") + f.write("Анализировались сценарии со случайным распределением и предварительной сортировкой ключей.\n\n") + + f.write("## 2. Результаты измерений (AVG)\n") + f.write("| Алгоритм | Входные данные | Вставка (с) | Поиск (с) | Удаление (с) |\n") + f.write("| :--- | :--- | :--- | :--- | :--- |\n") + for r in summary_for_report: + f.write(f"| {r['name']} | {r['mode']} | {r['ins']:.6f} | {r['find']:.6f} | {r['del']:.6f} |\n") + + f.write("\n## 3. Визуальный анализ\n") + f.write("### Сравнение по типам операций\n![Сравнение](data/struct_comparison.png)\n\n") + f.write("### Влияние упорядоченности на производительность\n![Влияние порядка](data/order_impact.png)\n\n") + + f.write("## 4. Экспертные выводы\n") + f.write("- **Эффект вырождения BST:** На отсортированных последовательностях BST демонстрирует критический рост времени выполнения (деградация до $O(N)$). ") + f.write("Это связано с отсутствием балансировки, превращающим дерево в линейный список.\n") + f.write("- **Инвариантность HashTable:** Хеш-таблица показывает наиболее стабильные результаты. Скорость доступа не коррелирует с порядком входных данных.\n") + f.write("- **Линейная сложность LinkedList:** Связный список предсказуемо неэффективен при поиске, так как требует итерации по всей глубине структуры.\n") + f.write("- **Итоговая оценка:** Для систем с высокой интенсивностью поиска и вставки оптимальным выбором является HashTable.") + +print("Готово.") \ No newline at end of file diff --git a/krasnovia/lab1/docs/data/order_impact.png b/krasnovia/lab1/docs/data/order_impact.png new file mode 100644 index 0000000..e39a5cc Binary files /dev/null and b/krasnovia/lab1/docs/data/order_impact.png differ diff --git a/krasnovia/lab1/docs/data/results.csv b/krasnovia/lab1/docs/data/results.csv new file mode 100644 index 0000000..48e1492 --- /dev/null +++ b/krasnovia/lab1/docs/data/results.csv @@ -0,0 +1,37 @@ +Структура,Режим,Итерация,Вставка,Поиск,Удаление +LinkedList,случайный,Run 1,0.0036254000006010756,0.06929340001079254,0.040904800000134856 +LinkedList,случайный,Run 2,0.002705999999307096,0.09314480000466574,0.038945499996771105 +LinkedList,случайный,Run 3,0.0035097999934805557,0.0652599999884842,0.03580480000528041 +LinkedList,случайный,Run 4,0.004379200006951578,0.060941299991100095,0.04131999998935498 +LinkedList,случайный,Run 5,0.003272000001743436,0.06662459998915438,0.03727009998692665 +LinkedList,случайный,AVERAGE,0.0034984800004167482,0.07105281999683939,0.0388490399956936 +HashTable,случайный,Run 1,0.007146899995859712,0.00018819999240804464,8.869999146554619e-05 +HashTable,случайный,Run 2,0.006990299996687099,0.00013760000001639128,8.589999924879521e-05 +HashTable,случайный,Run 3,0.007395300010102801,0.0001466999965487048,8.320000779349357e-05 +HashTable,случайный,Run 4,0.007719999994151294,0.00023800000781193376,0.00013099999341648072 +HashTable,случайный,Run 5,0.007573700000648387,0.00018960000306833535,0.00011110000195913017 +HashTable,случайный,AVERAGE,0.007365239999489859,0.00018001999997068195,9.997999877668917e-05 +BST,случайный,Run 1,0.04626839999400545,0.00047990000166464597,0.00024569999368395656 +BST,случайный,Run 2,0.0475841000006767,0.0004754999972647056,0.00026119999529328197 +BST,случайный,Run 3,0.046892100013792515,0.0004844000068260357,0.0002472000051056966 +BST,случайный,Run 4,0.047048299995367415,0.0004321000014897436,0.00024170000688172877 +BST,случайный,Run 5,0.04865149999386631,0.0004741000011563301,0.00025040000036824495 +BST,случайный,AVERAGE,0.04728887999954168,0.0004692000016802922,0.00024924000026658175 +LinkedList,сортированный,Run 1,0.004157000003033318,0.08125500001187902,0.044403499996406026 +LinkedList,сортированный,Run 2,0.0029534000059356913,0.06697529999655671,0.04485000000568107 +LinkedList,сортированный,Run 3,0.002979500000947155,0.06968830000550952,0.04757019999669865 +LinkedList,сортированный,Run 4,0.003208699999959208,0.06227809999836609,0.03610610000032466 +LinkedList,сортированный,Run 5,0.002962500002468005,0.06485759999486618,0.03632800000195857 +LinkedList,сортированный,AVERAGE,0.0032522200024686755,0.0690108600014355,0.0418515600002138 +HashTable,сортированный,Run 1,0.006838200002675876,0.00020619999850168824,0.00011320000339765102 +HashTable,сортированный,Run 2,0.006913500008522533,0.00015800000983290374,8.230000094044954e-05 +HashTable,сортированный,Run 3,0.006470899999840185,0.00016349999350495636,8.939999679569155e-05 +HashTable,сортированный,Run 4,0.0065700999984983355,0.00014420000661630183,8.969999908003956e-05 +HashTable,сортированный,Run 5,0.006396099997800775,0.00014509999891743064,9.229998977389187e-05 +HashTable,сортированный,AVERAGE,0.006637760001467541,0.00016340000147465616,9.33799979975447e-05 +BST,сортированный,Run 1,19.100887599997805,0.17849370000476483,0.09569349999947008 +BST,сортированный,Run 2,19.370542799995746,0.15886150000733323,0.11082600000372622 +BST,сортированный,Run 3,19.196645500007435,0.17154130000562873,0.1037713999976404 +BST,сортированный,Run 4,19.184918099999777,0.16993090001051314,0.11102890000620391 +BST,сортированный,Run 5,19.424080700002378,0.16240569998626597,0.0897938999987673 +BST,сортированный,AVERAGE,19.255414940000627,0.1682466200029012,0.10222274000116158 diff --git a/krasnovia/lab1/docs/data/struct_comparison.png b/krasnovia/lab1/docs/data/struct_comparison.png new file mode 100644 index 0000000..508d310 Binary files /dev/null and b/krasnovia/lab1/docs/data/struct_comparison.png differ diff --git a/krasnovia/lab1/docs/report.md b/krasnovia/lab1/docs/report.md new file mode 100644 index 0000000..3b25b3f --- /dev/null +++ b/krasnovia/lab1/docs/report.md @@ -0,0 +1,27 @@ +# Технический отчет: Сравнительный анализ структур данных + +## 1. Вводные данные +Целью теста является оценка производительности LinkedList, HashTable и BST на массиве из 10000 элементов. Анализировались сценарии со случайным распределением и предварительной сортировкой ключей. + +## 2. Результаты измерений (AVG) +| Алгоритм | Входные данные | Вставка (с) | Поиск (с) | Удаление (с) | +| :--- | :--- | :--- | :--- | :--- | +| LinkedList | случайный | 0.003498 | 0.071053 | 0.038849 | +| HashTable | случайный | 0.007365 | 0.000180 | 0.000100 | +| BST | случайный | 0.047289 | 0.000469 | 0.000249 | +| LinkedList | сортированный | 0.003252 | 0.069011 | 0.041852 | +| HashTable | сортированный | 0.006638 | 0.000163 | 0.000093 | +| BST | сортированный | 19.255415 | 0.168247 | 0.102223 | + +## 3. Визуальный анализ +### Сравнение по типам операций +![Сравнение](data/struct_comparison.png) + +### Влияние упорядоченности на производительность +![Влияние порядка](data/order_impact.png) + +## 4. Экспертные выводы +- **Эффект вырождения BST:** На отсортированных последовательностях BST демонстрирует критический рост времени выполнения (деградация до $O(N)$). Это связано с отсутствием балансировки, превращающим дерево в линейный список. +- **Инвариантность HashTable:** Хеш-таблица показывает наиболее стабильные результаты. Скорость доступа не коррелирует с порядком входных данных. +- **Линейная сложность LinkedList:** Связный список предсказуемо неэффективен при поиске, так как требует итерации по всей глубине структуры. +- **Итоговая оценка:** Для систем с высокой интенсивностью поиска и вставки оптимальным выбором является HashTable. \ No newline at end of file