diff --git a/KolbasovPD/docs/data/1-st_exercise/main.py b/KolbasovPD/docs/data/1-st_exercise/main.py new file mode 100644 index 0000000..06125d2 --- /dev/null +++ b/KolbasovPD/docs/data/1-st_exercise/main.py @@ -0,0 +1,395 @@ +import time +import random +import csv +import sys +sys.setrecursionlimit(100000) + +def ll_insert(head, name, phone): + new_node = {'name': name, 'phone': phone, 'next': None} + + if head is None: + return new_node + + curr = head + prev = None + while curr: + if curr['name'] == name: + curr['phone'] = phone + return head + prev = curr + curr = curr['next'] + + prev['next'] = new_node + return 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 head is None: + 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): + records = [] + curr = head + while curr: + records.append((curr['name'], curr['phone'])) + curr = curr['next'] + records.sort(key=lambda x: x[0]) + return records + +def hash_function(name, table_size): + return sum(ord(c) for c in name) % table_size + +def ht_create(size=1000): + return [None] * size + +def ht_insert(buckets, name, phone): + index = hash_function(name, len(buckets)) + buckets[index] = ll_insert(buckets[index], name, phone) + +def ht_find(buckets, name): + index = hash_function(name, len(buckets)) + return ll_find(buckets[index], name) + +def ht_delete(buckets, name): + index = hash_function(name, len(buckets)) + buckets[index] = ll_delete(buckets[index], name) + +def ht_list_all(buckets): + records = [] + for head in buckets: + curr = head + while curr: + records.append((curr['name'], curr['phone'])) + curr = curr['next'] + records.sort(key=lambda x: x[0]) + return records + +def bst_insert_iterative(root, name, phone): + new_node = {'name': name, 'phone': phone, 'left': None, 'right': None} + + if root is None: + return new_node + + curr = root + while True: + if name < curr['name']: + if curr['left'] is None: + curr['left'] = new_node + break + curr = curr['left'] + elif name > curr['name']: + if curr['right'] is None: + curr['right'] = new_node + break + curr = curr['right'] + else: + curr['phone'] = phone + break + + return root + +def bst_find_iterative(root, name): + curr = root + while curr: + if name == curr['name']: + return curr['phone'] + elif name < curr['name']: + curr = curr['left'] + else: + curr = curr['right'] + return None + +def bst_find_min(node): + while node and node['left']: + node = node['left'] + return node + +def bst_delete_iterative(root, name): + if root is None: + return None + + if name < root['name']: + root['left'] = bst_delete_iterative(root['left'], name) + elif name > root['name']: + root['right'] = bst_delete_iterative(root['right'], name) + else: + if root['left'] is None: + return root['right'] + elif root['right'] is None: + return root['left'] + + parent = root + successor = root['right'] + while successor['left']: + parent = successor + successor = successor['left'] + + root['name'] = successor['name'] + root['phone'] = successor['phone'] + + if parent == root: + parent['right'] = successor['right'] + else: + parent['left'] = successor['right'] + + return root + +def bst_list_all(root): + result = [] + stack = [] + curr = root + + while stack or curr: + while curr: + stack.append(curr) + curr = curr['left'] + curr = stack.pop() + result.append((curr['name'], curr['phone'])) + curr = curr['right'] + + return result + +def generate_test_data(N=10000): + names = [f"User_{i:05d}" for i in range(N)] + phones = [f"+7-999-{random.randint(1000000, 9999999)}" for _ in range(N)] + + records = list(zip(names, phones)) + + records_shuffled = records.copy() + random.shuffle(records_shuffled) + + records_sorted = sorted(records, key=lambda x: x[0]) + + return records_shuffled, records_sorted + +def measure_insertion(structure_type, records, ht_size=1000): + if structure_type == "LinkedList": + head = None + start = time.perf_counter() + for name, phone in records: + head = ll_insert(head, name, phone) + end = time.perf_counter() + return head, (end - start) + + elif structure_type == "HashTable": + buckets = ht_create(ht_size) + start = time.perf_counter() + for name, phone in records: + ht_insert(buckets, name, phone) + end = time.perf_counter() + return buckets, (end - start) + + elif structure_type == "BST": + root = None + start = time.perf_counter() + for name, phone in records: + root = bst_insert_iterative(root, name, phone) + end = time.perf_counter() + return root, (end - start) + +def measure_search(data_structure, structure_type, existing_names, non_existing_names): + start = time.perf_counter() + for name in existing_names: + if structure_type == "LinkedList": + ll_find(data_structure, name) + elif structure_type == "HashTable": + ht_find(data_structure, name) + elif structure_type == "BST": + bst_find_iterative(data_structure, name) + + for name in non_existing_names: + if structure_type == "LinkedList": + ll_find(data_structure, name) + elif structure_type == "HashTable": + ht_find(data_structure, name) + elif structure_type == "BST": + bst_find_iterative(data_structure, name) + end = time.perf_counter() + + return end - start + +def measure_deletion(data_structure, structure_type, names_to_delete): + start = time.perf_counter() + for name in names_to_delete: + if structure_type == "LinkedList": + data_structure = ll_delete(data_structure, name) + elif structure_type == "HashTable": + ht_delete(data_structure, name) + elif structure_type == "BST": + data_structure = bst_delete_iterative(data_structure, name) + end = time.perf_counter() + + return data_structure, (end - start) + +def run_experiment(N=5000, repeats=5): + print(f"Генерация тестовых данных (N={N})...") + records_shuffled, records_sorted = generate_test_data(N) + + existing_names = [name for name, _ in random.sample(records_shuffled, min(100, N))] + non_existing_names = [f"None_{i}" for i in range(10)] + delete_names = [name for name, _ in random.sample(records_shuffled, min(50, N))] + + results = [] + + structures = ["LinkedList", "HashTable", "BST"] + modes = ["случайный", "отсортированный"] + + for struct in structures: + for mode in modes: + records = records_shuffled if mode == "случайный" else records_sorted + + print(f"\nТестирование: {struct}, режим: {mode}") + + insertion_times = [] + search_times = [] + deletion_times = [] + + for rep in range(repeats): + print(f" Повторение {rep+1}/{repeats}...") + + data_structure, insert_time = measure_insertion(struct, records) + insertion_times.append(insert_time) + + search_time = measure_search(data_structure, struct, existing_names, non_existing_names) + search_times.append(search_time) + + data_structure, delete_time = measure_deletion(data_structure, struct, delete_names) + deletion_times.append(delete_time) + + avg_insert = sum(insertion_times) / repeats + avg_search = sum(search_times) / repeats + avg_delete = sum(deletion_times) / repeats + + results.append({ + "structure": struct, + "mode": mode, + "insertion_avg": avg_insert, + "insertion_all": insertion_times, + "search_avg": avg_search, + "search_all": search_times, + "deletion_avg": avg_delete, + "deletion_all": deletion_times + }) + + print(f" Вставка: {avg_insert:.6f} сек (замеры: {[f'{t:.6f}' for t in insertion_times]})") + print(f" Поиск: {avg_search:.6f} сек (замеры: {[f'{t:.6f}' for t in search_times]})") + print(f" Удаление: {avg_delete:.6f} сек (замеры: {[f'{t:.6f}' for t in deletion_times]})") + + return results + +def save_results_to_csv(results, filename="results.csv"): + with open(filename, 'w', newline='', encoding='utf-8') as csvfile: + writer = csv.writer(csvfile) + writer.writerow(["Структура", "Режим", "Операция", "Повторение", "Время (сек)"]) + + for res in results: + struct = res["structure"] + mode = res["mode"] + + for i, t in enumerate(res["insertion_all"]): + writer.writerow([struct, mode, "вставка", i+1, t]) + writer.writerow([struct, mode, "вставка", "СРЕДНЕЕ", res["insertion_avg"]]) + + for i, t in enumerate(res["search_all"]): + writer.writerow([struct, mode, "поиск", i+1, t]) + writer.writerow([struct, mode, "поиск", "СРЕДНЕЕ", res["search_avg"]]) + + for i, t in enumerate(res["deletion_all"]): + writer.writerow([struct, mode, "удаление", i+1, t]) + writer.writerow([struct, mode, "удаление", "СРЕДНЕЕ", res["deletion_avg"]]) + + print(f"\nРезультаты сохранены в {filename}") + +def print_summary_table(results): + print("\n" + "="*80) + print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ (среднее время в секундах)") + print("="*80) + print(f"{'Структура':<15} {'Режим':<12} {'Вставка':<12} {'Поиск (110)':<12} {'Удаление (50)':<12}") + print("-"*80) + + for res in results: + print(f"{res['structure']:<15} {res['mode']:<12} {res['insertion_avg']:<12.6f} " + f"{res['search_avg']:<12.6f} {res['deletion_avg']:<12.6f}") + + print("\n" + "="*80) + print("АНАЛИЗ ДЕГРАДАЦИИ BST") + print("="*80) + + bst_random = next(r for r in results if r['structure'] == "BST" and r['mode'] == "случайный") + bst_sorted = next(r for r in results if r['structure'] == "BST" and r['mode'] == "отсортированный") + + degradation = bst_sorted['insertion_avg'] / bst_random['insertion_avg'] + print(f"BST: отсортированные данные в {degradation:.1f} раз медленнее случайных") + print("Причина: вырождение дерева в линейный связный список (O(n) вместо O(log n))") + +if __name__ == "__main__": + print("="*80) + print("ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ СТРУКТУР ДАННЫХ ДЛЯ ТЕЛЕФОННОГО СПРАВОЧНИКА") + print("="*80) + + results = run_experiment(N=5000, repeats=5) + + save_results_to_csv(results) + + print_summary_table(results) + + print("\n" + "="*80) + print("ВЫВОДЫ И РЕКОМЕНДАЦИИ") + print("="*80) + print(""" +1. Хеш-таблица: + Лучшая производительность для операций поиска и вставки (O(1) в среднем) + Не чувствительна к порядку входных данных + Требует память под массив бакетов + Не поддерживает естественный порядок (нужна сортировка) + Идеально для справочников с частым поиском + +2. Двоичное дерево поиска: + Естественная сортировка (in-order обход) + Хорошая производительность на случайных данных (O(log n)) + Сильная деградация на отсортированных данных (O(n)) + Рекурсивные операции требуют больше памяти + Хорошо для задач, где нужен отсортированный вывод + +3. Связный список: + Простота реализации + Медленный поиск и удаление (O(n)) + Неэффективен для больших объёмов данных + Применим только для очень маленьких справочников + +РЕКОМЕНДАЦИИ ДЛЯ РЕАЛЬНЫХ ЗАДАЧ: +Частый поиск, редкие вставки -> ХЕШ-ТАБЛИЦА +Нужен отсортированный вывод -> ДЕРЕВО (с балансировкой) +Очень маленький справочник (<100 записей) -> СПИСОК +В реальных БД -> хеш-таблица + B-деревья + """) + + print("\n" + "="*80) + print("ДОПОЛНИТЕЛЬНЫЙ АНАЛИЗ") + print("="*80) + + for struct in ["LinkedList", "HashTable", "BST"]: + res_random = next(r for r in results if r['structure'] == struct and r['mode'] == "случайный") + print(f"{struct:12} поиск 110 записей: {res_random['search_avg']:.6f} сек") + + ll_random = next(r for r in results if r['structure'] == "LinkedList" and r['mode'] == "случайный") + ll_sorted = next(r for r in results if r['structure'] == "LinkedList" and r['mode'] == "отсортированный") + print(f"\nСвязный список: деградация {ll_sorted['insertion_avg'] / ll_random['insertion_avg']:.2f}х") \ No newline at end of file diff --git a/KolbasovPD/docs/data/1-st_exercise/results.csv b/KolbasovPD/docs/data/1-st_exercise/results.csv new file mode 100644 index 0000000..db7b3e7 --- /dev/null +++ b/KolbasovPD/docs/data/1-st_exercise/results.csv @@ -0,0 +1,109 @@ +Структура,Режим,Операция,Повторение,Время (сек) +LinkedList,случайный,вставка,1,0.7588584999975865 +LinkedList,случайный,вставка,2,0.7769573999976274 +LinkedList,случайный,вставка,3,0.7729451000013796 +LinkedList,случайный,вставка,4,0.7535389000004216 +LinkedList,случайный,вставка,5,0.758188899999368 +LinkedList,случайный,вставка,СРЕДНЕЕ,0.7640977599992766 +LinkedList,случайный,поиск,1,0.023368899997876724 +LinkedList,случайный,поиск,2,0.023365799999737646 +LinkedList,случайный,поиск,3,0.02325380000183941 +LinkedList,случайный,поиск,4,0.02314950000072713 +LinkedList,случайный,поиск,5,0.023074000000633532 +LinkedList,случайный,поиск,СРЕДНЕЕ,0.023242400000162887 +LinkedList,случайный,удаление,1,0.014257400001952192 +LinkedList,случайный,удаление,2,0.01445149999926798 +LinkedList,случайный,удаление,3,0.014200800000253366 +LinkedList,случайный,удаление,4,0.013934900001913775 +LinkedList,случайный,удаление,5,0.013907599997764919 +LinkedList,случайный,удаление,СРЕДНЕЕ,0.014150440000230446 +LinkedList,отсортированный,вставка,1,0.7295501999979024 +LinkedList,отсортированный,вставка,2,0.733855800001038 +LinkedList,отсортированный,вставка,3,0.9446520999990753 +LinkedList,отсортированный,вставка,4,0.8595158000025549 +LinkedList,отсортированный,вставка,5,0.7685707000018738 +LinkedList,отсортированный,вставка,СРЕДНЕЕ,0.8072289200004888 +LinkedList,отсортированный,поиск,1,0.022782500000175787 +LinkedList,отсортированный,поиск,2,0.022431400000641588 +LinkedList,отсортированный,поиск,3,0.029615100000228267 +LinkedList,отсортированный,поиск,4,0.026672600000893 +LinkedList,отсортированный,поиск,5,0.02327099999820348 +LinkedList,отсортированный,поиск,СРЕДНЕЕ,0.024954520000028423 +LinkedList,отсортированный,удаление,1,0.015000699997472111 +LinkedList,отсортированный,удаление,2,0.014776500000152737 +LinkedList,отсортированный,удаление,3,0.019435000001976732 +LinkedList,отсортированный,удаление,4,0.0165975999989314 +LinkedList,отсортированный,удаление,5,0.015432599997438956 +LinkedList,отсортированный,удаление,СРЕДНЕЕ,0.016248479999194387 +HashTable,случайный,вставка,1,0.06989740000062739 +HashTable,случайный,вставка,2,0.06307899999956135 +HashTable,случайный,вставка,3,0.06795570000031148 +HashTable,случайный,вставка,4,0.0703618000006827 +HashTable,случайный,вставка,5,0.06456320000143023 +HashTable,случайный,вставка,СРЕДНЕЕ,0.06717142000052263 +HashTable,случайный,поиск,1,0.0011812000011559576 +HashTable,случайный,поиск,2,0.0011990000020887237 +HashTable,случайный,поиск,3,0.0014205000006768387 +HashTable,случайный,поиск,4,0.0018674999992072117 +HashTable,случайный,поиск,5,0.0012002000003121793 +HashTable,случайный,поиск,СРЕДНЕЕ,0.001373680000688182 +HashTable,случайный,удаление,1,0.0008298000029753894 +HashTable,случайный,удаление,2,0.0008296999985759612 +HashTable,случайный,удаление,3,0.0010115000004589092 +HashTable,случайный,удаление,4,0.0009790000003704336 +HashTable,случайный,удаление,5,0.0008373999989998993 +HashTable,случайный,удаление,СРЕДНЕЕ,0.0008974800002761185 +HashTable,отсортированный,вставка,1,0.06598000000303728 +HashTable,отсортированный,вставка,2,0.06413769999926444 +HashTable,отсортированный,вставка,3,0.14105009999912 +HashTable,отсортированный,вставка,4,0.07664300000033109 +HashTable,отсортированный,вставка,5,0.06128300000273157 +HashTable,отсортированный,вставка,СРЕДНЕЕ,0.08181876000089687 +HashTable,отсортированный,поиск,1,0.001160599997092504 +HashTable,отсортированный,поиск,2,0.004666700002417201 +HashTable,отсортированный,поиск,3,0.00214869999763323 +HashTable,отсортированный,поиск,4,0.0016174000011233147 +HashTable,отсортированный,поиск,5,0.0013016000011703 +HashTable,отсортированный,поиск,СРЕДНЕЕ,0.00217899999988731 +HashTable,отсортированный,удаление,1,0.0008883999980753288 +HashTable,отсортированный,удаление,2,0.002087799999571871 +HashTable,отсортированный,удаление,3,0.001078500001312932 +HashTable,отсортированный,удаление,4,0.0009227000009559561 +HashTable,отсортированный,удаление,5,0.000898300000699237 +HashTable,отсортированный,удаление,СРЕДНЕЕ,0.001175140000123065 +BST,случайный,вставка,1,0.01702110000042012 +BST,случайный,вставка,2,0.014504000002489192 +BST,случайный,вставка,3,0.013862499999959255 +BST,случайный,вставка,4,0.01633149999906891 +BST,случайный,вставка,5,0.015226100000290899 +BST,случайный,вставка,СРЕДНЕЕ,0.015389040000445674 +BST,случайный,поиск,1,0.000405700000555953 +BST,случайный,поиск,2,0.0002680999969015829 +BST,случайный,поиск,3,0.00024119999943650328 +BST,случайный,поиск,4,0.00027720000070985407 +BST,случайный,поиск,5,0.00030730000071343966 +BST,случайный,поиск,СРЕДНЕЕ,0.0002998999996634666 +BST,случайный,удаление,1,0.00024620000112918206 +BST,случайный,удаление,2,0.00016830000095069408 +BST,случайный,удаление,3,0.0001548000000184402 +BST,случайный,удаление,4,0.00018679999993764795 +BST,случайный,удаление,5,0.00022140000146464445 +BST,случайный,удаление,СРЕДНЕЕ,0.00019550000070012175 +BST,отсортированный,вставка,1,1.875409899999795 +BST,отсортированный,вставка,2,1.8619785999981104 +BST,отсортированный,вставка,3,1.8126238000004378 +BST,отсортированный,вставка,4,1.8052959000015107 +BST,отсортированный,вставка,5,1.7978389999989304 +BST,отсортированный,вставка,СРЕДНЕЕ,1.830629439999757 +BST,отсортированный,поиск,1,0.03237050000097952 +BST,отсортированный,поиск,2,0.03320049999820185 +BST,отсортированный,поиск,3,0.033564300003490644 +BST,отсортированный,поиск,4,0.031019200003356673 +BST,отсортированный,поиск,5,0.03037219999896479 +BST,отсортированный,поиск,СРЕДНЕЕ,0.0321053400009987 +BST,отсортированный,удаление,1,0.03479439999864553 +BST,отсортированный,удаление,2,0.0337948000014876 +BST,отсортированный,удаление,3,0.034445099998265505 +BST,отсортированный,удаление,4,0.03366980000282638 +BST,отсортированный,удаление,5,0.03821500000049127 +BST,отсортированный,удаление,СРЕДНЕЕ,0.03498382000034326 diff --git a/KolbasovPD/docs/data/2-nd_exercise/main.py b/KolbasovPD/docs/data/2-nd_exercise/main.py new file mode 100644 index 0000000..e69de29