diff --git a/smirnovad/docs/README.md b/smirnovad/docs/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/smirnovad/lab1/docs/data/lab1.py b/smirnovad/lab1/docs/data/lab1.py new file mode 100644 index 0000000..0230135 --- /dev/null +++ b/smirnovad/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"C:\Users\andre\2026-rff_mp\smirnovad\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/smirnovad/lab1/docs/data/order_impact.png b/smirnovad/lab1/docs/data/order_impact.png new file mode 100644 index 0000000..d7355ac Binary files /dev/null and b/smirnovad/lab1/docs/data/order_impact.png differ diff --git a/smirnovad/lab1/docs/data/results.csv b/smirnovad/lab1/docs/data/results.csv new file mode 100644 index 0000000..f962189 --- /dev/null +++ b/smirnovad/lab1/docs/data/results.csv @@ -0,0 +1,37 @@ +Структура,Режим,Итерация,Вставка,Поиск,Удаление +LinkedList,случайный,Run 1,0.0019714999943971634,0.029020800022408366,0.017652600072324276 +LinkedList,случайный,Run 2,0.0012699998915195465,0.03224149998277426,0.019751599989831448 +LinkedList,случайный,Run 3,0.0015686999540776014,0.032726099947467446,0.023450100095942616 +LinkedList,случайный,Run 4,0.0012663998641073704,0.031491999980062246,0.02140070009045303 +LinkedList,случайный,Run 5,0.001316299894824624,0.03115670010447502,0.01875569997355342 +LinkedList,случайный,AVERAGE,0.0014785799197852612,0.03132742000743747,0.020202140044420957 +HashTable,случайный,Run 1,0.0036369001027196646,7.810001261532307e-05,4.20999713242054e-05 +HashTable,случайный,Run 2,0.0027166998479515314,6.680004298686981e-05,3.690016455948353e-05 +HashTable,случайный,Run 3,0.002825999865308404,6.940006278455257e-05,4.0599843487143517e-05 +HashTable,случайный,Run 4,0.0038201999850571156,9.25001222640276e-05,4.6800123527646065e-05 +HashTable,случайный,Run 5,0.0028741999994963408,7.349997758865356e-05,4.6900007873773575e-05 +HashTable,случайный,AVERAGE,0.0031747999601066113,7.606004364788533e-05,4.2660022154450415e-05 +BST,случайный,Run 1,0.02340110018849373,0.000219699926674366,0.00012899981811642647 +BST,случайный,Run 2,0.02266499982215464,0.000223799841478467,0.00011649983935058117 +BST,случайный,Run 3,0.02390270004980266,0.0002661000471562147,0.00011879997327923775 +BST,случайный,Run 4,0.02376510016620159,0.00023330003023147583,0.00013200007379055023 +BST,случайный,Run 5,0.039811399998143315,0.00020990008488297462,0.00011849985457956791 +BST,случайный,AVERAGE,0.02670906004495919,0.00023055998608469963,0.00012295991182327272 +LinkedList,сортированный,Run 1,0.001537499949336052,0.033511900110170245,0.019870299845933914 +LinkedList,сортированный,Run 2,0.001307999948039651,0.0319586000405252,0.014161000028252602 +LinkedList,сортированный,Run 3,0.0014808999840170145,0.0326090999878943,0.018076500156894326 +LinkedList,сортированный,Run 4,0.0013822999317198992,0.033412199933081865,0.022219100035727024 +LinkedList,сортированный,Run 5,0.0012627998366951942,0.030183800030499697,0.016802300000563264 +LinkedList,сортированный,AVERAGE,0.001394299929961562,0.03233512002043426,0.018225840013474225 +HashTable,сортированный,Run 1,0.0034910000395029783,8.500018157064915e-05,4.449998959898949e-05 +HashTable,сортированный,Run 2,0.002676100004464388,6.62999227643013e-05,3.50000336766243e-05 +HashTable,сортированный,Run 3,0.0026501999236643314,6.309989839792252e-05,3.5800039768218994e-05 +HashTable,сортированный,Run 4,0.003009800100699067,6.360001862049103e-05,3.800005652010441e-05 +HashTable,сортированный,Run 5,0.002735199872404337,8.360017091035843e-05,3.979983739554882e-05 +HashTable,сортированный,AVERAGE,0.0029124599881470204,7.232003845274449e-05,3.86199913918972e-05 +BST,сортированный,Run 1,10.096050000051036,0.08805329981260002,0.06355300010181963 +BST,сортированный,Run 2,10.27557749999687,0.0852949998807162,0.04538960009813309 +BST,сортированный,Run 3,9.790069000096992,0.0812387999612838,0.0491947999689728 +BST,сортированный,Run 4,9.99310930003412,0.08915449981577694,0.04604210006073117 +BST,сортированный,Run 5,9.97979940008372,0.07943259994499385,0.049180999863892794 +BST,сортированный,AVERAGE,10.026921040052548,0.08463483988307416,0.0506721000187099 diff --git a/smirnovad/lab1/docs/data/struct_comparison.png b/smirnovad/lab1/docs/data/struct_comparison.png new file mode 100644 index 0000000..bbacb56 Binary files /dev/null and b/smirnovad/lab1/docs/data/struct_comparison.png differ diff --git a/smirnovad/lab1/docs/report.md b/smirnovad/lab1/docs/report.md new file mode 100644 index 0000000..0672fcf --- /dev/null +++ b/smirnovad/lab1/docs/report.md @@ -0,0 +1,27 @@ +# Технический отчет: Сравнительный анализ структур данных + +## 1. Вводные данные +Целью теста является оценка производительности LinkedList, HashTable и BST на массиве из 10000 элементов. Анализировались сценарии со случайным распределением и предварительной сортировкой ключей. + +## 2. Результаты измерений (AVG) +| Алгоритм | Входные данные | Вставка (с) | Поиск (с) | Удаление (с) | +| :--- | :--- | :--- | :--- | :--- | +| LinkedList | случайный | 0.001479 | 0.031327 | 0.020202 | +| HashTable | случайный | 0.003175 | 0.000076 | 0.000043 | +| BST | случайный | 0.026709 | 0.000231 | 0.000123 | +| LinkedList | сортированный | 0.001394 | 0.032335 | 0.018226 | +| HashTable | сортированный | 0.002912 | 0.000072 | 0.000039 | +| BST | сортированный | 10.026921 | 0.084635 | 0.050672 | + +## 3. Визуальный анализ +### Сравнение по типам операций +![Сравнение](data/struct_comparison.png) + +### Влияние упорядоченности на производительность +![Влияние порядка](data/order_impact.png) + +## 4. Экспертные выводы +- **Эффект вырождения BST:** На отсортированных последовательностях BST демонстрирует критический рост времени выполнения (деградация до $O(N)$). Это связано с отсутствием балансировки, превращающим дерево в линейный список. +- **Инвариантность HashTable:** Хеш-таблица показывает наиболее стабильные результаты. Скорость доступа не коррелирует с порядком входных данных. +- **Линейная сложность LinkedList:** Связный список предсказуемо неэффективен при поиске, так как требует итерации по всей глубине структуры. +- **Итоговая оценка:** Для систем с высокой интенсивностью поиска и вставки оптимальным выбором является HashTable. \ No newline at end of file