diff --git a/kalinovskiymi/docs/data/benchmark_results.png b/kalinovskiymi/docs/data/benchmark_results.png new file mode 100644 index 0000000..a97eb48 Binary files /dev/null and b/kalinovskiymi/docs/data/benchmark_results.png differ diff --git a/kalinovskiymi/docs/data/results.csv b/kalinovskiymi/docs/data/results.csv new file mode 100644 index 0000000..2c555e4 --- /dev/null +++ b/kalinovskiymi/docs/data/results.csv @@ -0,0 +1,91 @@ +structure,order,operation,time,run +LinkedList,shuffled,insert,0.030281299999999955,1 +LinkedList,shuffled,find,0.004695899999999975,1 +LinkedList,shuffled,delete,0.0033233999999999764,1 +LinkedList,shuffled,insert,0.0353907,2 +LinkedList,shuffled,find,0.005563899999999955,2 +LinkedList,shuffled,delete,0.0034716999999999665,2 +LinkedList,shuffled,insert,0.01944009999999996,3 +LinkedList,shuffled,find,0.002577700000000016,3 +LinkedList,shuffled,delete,0.0018714000000000786,3 +LinkedList,shuffled,insert,0.022181000000000006,4 +LinkedList,shuffled,find,0.004234000000000071,4 +LinkedList,shuffled,delete,0.0031284999999999785,4 +LinkedList,shuffled,insert,0.02823869999999995,5 +LinkedList,shuffled,find,0.004262699999999953,5 +LinkedList,shuffled,delete,0.0031122999999999568,5 +HashTable,shuffled,insert,0.000658699999999901,1 +HashTable,shuffled,find,5.359999999998699e-05,1 +HashTable,shuffled,delete,2.869999999999262e-05,1 +HashTable,shuffled,insert,0.0008105999999999947,2 +HashTable,shuffled,find,5.250000000001087e-05,2 +HashTable,shuffled,delete,2.8000000000028002e-05,2 +HashTable,shuffled,insert,0.0006085000000000118,3 +HashTable,shuffled,find,5.270000000001662e-05,3 +HashTable,shuffled,delete,2.76000000000165e-05,3 +HashTable,shuffled,insert,0.0005973000000000228,4 +HashTable,shuffled,find,5.179999999993523e-05,4 +HashTable,shuffled,delete,2.740000000001075e-05,4 +HashTable,shuffled,insert,0.0005963999999999414,5 +HashTable,shuffled,find,5.250000000001087e-05,5 +HashTable,shuffled,delete,2.7699999999963865e-05,5 +BST,shuffled,insert,0.0025841999999999254,1 +BST,shuffled,find,0.0001540000000000985,1 +BST,shuffled,delete,0.00013839999999998298,1 +BST,shuffled,insert,0.0025424000000000557,2 +BST,shuffled,find,0.000152800000000064,2 +BST,shuffled,delete,0.00013700000000005375,2 +BST,shuffled,insert,0.0025473999999999775,3 +BST,shuffled,find,0.00015370000000003436,3 +BST,shuffled,delete,0.00013370000000001436,3 +BST,shuffled,insert,0.0025645000000000806,4 +BST,shuffled,find,0.00015940000000003174,4 +BST,shuffled,delete,0.00013249999999997986,4 +BST,shuffled,insert,0.0025673999999999975,5 +BST,shuffled,find,0.00015799999999999148,5 +BST,shuffled,delete,0.0007081999999999367,5 +LinkedList,sorted,insert,0.04947849999999998,1 +LinkedList,sorted,find,0.0053706999999999505,1 +LinkedList,sorted,delete,0.003119800000000006,1 +LinkedList,sorted,insert,0.05800839999999996,2 +LinkedList,sorted,find,0.005768099999999943,2 +LinkedList,sorted,delete,0.0038080000000000336,2 +LinkedList,sorted,insert,0.05770470000000005,3 +LinkedList,sorted,find,0.0049146999999999386,3 +LinkedList,sorted,delete,0.003128599999999926,3 +LinkedList,sorted,insert,0.05588850000000001,4 +LinkedList,sorted,find,0.004705699999999924,4 +LinkedList,sorted,delete,0.0031733000000000455,4 +LinkedList,sorted,insert,0.05095189999999983,5 +LinkedList,sorted,find,0.0028690999999998468,5 +LinkedList,sorted,delete,0.0018724999999999437,5 +HashTable,sorted,insert,0.0004965999999999582,1 +HashTable,sorted,find,3.719999999995949e-05,1 +HashTable,sorted,delete,2.10999999998851e-05,1 +HashTable,sorted,insert,0.0004729999999999457,2 +HashTable,sorted,find,3.5199999999901976e-05,2 +HashTable,sorted,delete,2.0600000000037255e-05,2 +HashTable,sorted,insert,0.00046370000000006684,3 +HashTable,sorted,find,3.5699999999971865e-05,3 +HashTable,sorted,delete,2.0400000000142526e-05,3 +HashTable,sorted,insert,0.0004648999999998793,4 +HashTable,sorted,find,3.520000000012402e-05,4 +HashTable,sorted,delete,2.0600000000037255e-05,4 +HashTable,sorted,insert,0.000461600000000173,5 +HashTable,sorted,find,3.690000000000637e-05,5 +HashTable,sorted,delete,2.1499999999896602e-05,5 +BST,sorted,insert,0.09513269999999996,1 +BST,sorted,find,0.006302800000000053,1 +BST,sorted,delete,0.006097499999999867,1 +BST,sorted,insert,0.12047529999999984,2 +BST,sorted,find,0.006049799999999994,2 +BST,sorted,delete,0.0062197999999999976,2 +BST,sorted,insert,0.10559420000000008,3 +BST,sorted,find,0.003817999999999877,3 +BST,sorted,delete,0.004166000000000114,3 +BST,sorted,insert,0.09572439999999993,4 +BST,sorted,find,0.005789299999999997,4 +BST,sorted,delete,0.006035599999999919,4 +BST,sorted,insert,0.13064960000000014,5 +BST,sorted,find,0.006988800000000017,5 +BST,sorted,delete,0.006825800000000104,5 diff --git a/kalinovskiymi/docs/data/task_1.py b/kalinovskiymi/docs/data/task_1.py new file mode 100644 index 0000000..ee7c141 --- /dev/null +++ b/kalinovskiymi/docs/data/task_1.py @@ -0,0 +1,377 @@ +import sys + +sys.setrecursionlimit(10000) + +import time +import random +import csv +import os +import matplotlib.pyplot as plt +import numpy as np + + +def ll_insert(head, name, phone): + new_node = {'name': name, 'phone': phone, 'next': None} + if head is None: + return new_node + if head['name'] > name: + new_node['next'] = head + return new_node + current = head + while current['next'] is not None and current['next']['name'] < name: + current = current['next'] + if current['name'] == name: + current['phone'] = phone + return head + new_node['next'] = current['next'] + current['next'] = new_node + return head + + +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): + result = [] + current = head + while current is not None: + result.append((current['name'], current['phone'])) + current = current['next'] + result.sort(key=lambda x: x[0]) + return result + + +def ht_insert(buckets, name, phone): + index = hash(name) % len(buckets) + buckets[index] = ll_insert(buckets[index], name, phone) + + +def ht_find(buckets, name): + index = hash(name) % len(buckets) + return ll_find(buckets[index], name) + + +def ht_delete(buckets, name): + index = hash(name) % len(buckets) + buckets[index] = ll_delete(buckets[index], name) + + +def ht_list_all(buckets): + result = [] + for bucket in buckets: + if bucket is not None: + current = bucket + while current is not None: + result.append((current['name'], current['phone'])) + current = current['next'] + result.sort(key=lambda x: x[0]) + return result + + +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): + current = root + while current is not None: + if name == current['name']: + return current['phone'] + elif name < current['name']: + current = current['left'] + else: + current = current['right'] + return None + + +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'] + min_node = root['right'] + while min_node['left'] is not None: + min_node = min_node['left'] + root['name'] = min_node['name'] + root['phone'] = min_node['phone'] + root['right'] = bst_delete(root['right'], min_node['name']) + return root + + +def bst_list_all(root): + result = [] + + def inorder(node): + if node is not None: + inorder(node['left']) + result.append((node['name'], node['phone'])) + inorder(node['right']) + + inorder(root) + return result + + +def generate_records(n, mode='shuffled'): + names = [f"User_{i:05d}" for i in range(n)] + if mode == 'shuffled': + random.shuffle(names) + records = [(name, f"Phone_{i}") for i, name in enumerate(names)] + return records + + +def benchmark_insert(structure_type, records, buckets_size=512): + 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 end - start, head + elif structure_type == 'HashTable': + buckets = [None] * buckets_size + start = time.perf_counter() + for name, phone in records: + ht_insert(buckets, name, phone) + end = time.perf_counter() + return end - start, buckets + elif structure_type == 'BST': + root = None + start = time.perf_counter() + for name, phone in records: + root = bst_insert(root, name, phone) + end = time.perf_counter() + return end - start, root + + +def benchmark_find(structure_type, structure, names): + start = time.perf_counter() + if structure_type == 'LinkedList': + for name in names: + ll_find(structure, name) + elif structure_type == 'HashTable': + for name in names: + ht_find(structure, name) + elif structure_type == 'BST': + for name in names: + bst_find(structure, name) + end = time.perf_counter() + return end - start + + +def benchmark_delete(structure_type, structure, names): + start = time.perf_counter() + if structure_type == 'LinkedList': + head = structure + for name in names: + head = ll_delete(head, name) + end = time.perf_counter() + return end - start, head + elif structure_type == 'HashTable': + buckets = structure + for name in names: + ht_delete(buckets, name) + end = time.perf_counter() + return end - start, buckets + elif structure_type == 'BST': + root = structure + for name in names: + root = bst_delete(root, name) + end = time.perf_counter() + return end - start, root + + +def benchmark_list_all(structure_type, structure): + start = time.perf_counter() + if structure_type == 'LinkedList': + ll_list_all(structure) + elif structure_type == 'HashTable': + ht_list_all(structure) + elif structure_type == 'BST': + bst_list_all(structure) + end = time.perf_counter() + return end - start + + +def main(): + random.seed(42) + N = 1000 + REPETITIONS = 5 + + all_results = [] + structures = ['LinkedList', 'HashTable', 'BST'] + modes = ['shuffled', 'sorted'] + + os.makedirs('docs/data', exist_ok=True) + + for mode in modes: + records = generate_records(N, mode) + find_names = [random.choice(records)[0] for _ in range(100)] + [f"None_{i}" for i in range(10)] + delete_names = [random.choice(records)[0] for _ in range(50)] + + for struct in structures: + print(f"Тестирую {struct} на {mode} данных...") + for rep in range(REPETITIONS): + insert_time, structure = benchmark_insert(struct, records) + all_results.append([struct, mode, 'insert', insert_time, rep + 1]) + + find_time = benchmark_find(struct, structure, find_names) + all_results.append([struct, mode, 'find', find_time, rep + 1]) + + delete_time, _ = benchmark_delete(struct, structure, delete_names) + all_results.append([struct, mode, 'delete', delete_time, rep + 1]) + + with open('docs/data/results.csv', 'w', newline='') as f: + writer = csv.writer(f) + writer.writerow(['structure', 'order', 'operation', 'time', 'run']) + writer.writerows(all_results) + + print("\nCSV сохранён в docs/data/results.csv") + + data = {} + for row in all_results: + key = (row[0], row[1], row[2]) + if key not in data: + data[key] = [] + data[key].append(row[3]) + + averaged = [] + for key, times in data.items(): + avg_time = sum(times) / len(times) + averaged.append([key[0], key[1], key[2], avg_time]) + + print("\nУСРЕДНЁННЫЕ РЕЗУЛЬТАТЫ (сек):") + print(f"{'Структура':<15} {'Режим':<15} {'Операция':<10} {'Среднее время':<15}") + print("-" * 55) + for row in sorted(averaged, key=lambda x: (x[0], x[1], x[2])): + print(f"{row[0]:<15} {row[1]:<15} {row[2]:<10} {row[3]:<15.6f}") + + structures_list = ['LinkedList', 'HashTable', 'BST'] + operations_list = ['insert', 'find', 'delete'] + + fig, axes = plt.subplots(1, 3, figsize=(18, 6)) + + for i, op in enumerate(operations_list): + ax = axes[i] + x = np.arange(len(structures_list)) + width = 0.35 + + shuffled_times = [] + sorted_times = [] + + for struct in structures_list: + shuffled_times.append( + next(row[3] for row in averaged if row[0] == struct and row[1] == 'shuffled' and row[2] == op)) + sorted_times.append( + next(row[3] for row in averaged if row[0] == struct and row[1] == 'sorted' and row[2] == op)) + + bars1 = ax.bar(x - width / 2, shuffled_times, width, label='Случайный', color='#3498db') + bars2 = ax.bar(x + width / 2, sorted_times, width, label='Отсортированный', color='#e74c3c') + + for bar in bars1: + height = bar.get_height() + ax.text(bar.get_x() + bar.get_width() / 2., height + max(shuffled_times) * 0.01, + f'{height:.6f}', ha='center', va='bottom', fontsize=7) + for bar in bars2: + height = bar.get_height() + ax.text(bar.get_x() + bar.get_width() / 2., height + max(sorted_times) * 0.01, + f'{height:.6f}', ha='center', va='bottom', fontsize=7) + + ax.set_xlabel('Структура данных') + ax.set_ylabel('Время (сек)') + ax.set_title(f'Операция: {op}') + ax.set_xticks(x) + ax.set_xticklabels(structures_list) + ax.legend() + ax.grid(axis='y', alpha=0.3) + + if op == 'find': + ax.set_yscale('log') + + plt.suptitle(f'Сравнение производительности структур данных (N = {N} записей)', + fontsize=14, fontweight='bold') + plt.tight_layout() + + os.makedirs('docs', exist_ok=True) + plt.savefig('docs/benchmark_results.png', dpi=150, bbox_inches='tight') + plt.show() + print(f"График сохранён в docs/benchmark_results.png") + + print("\nАНАЛИЗ РЕЗУЛЬТАТОВ") + + print("\n1. Влияние порядка данных на BST:") + bst_shuffled_insert = next( + row[3] for row in averaged if row[0] == 'BST' and row[1] == 'shuffled' and row[2] == 'insert') + bst_sorted_insert = next( + row[3] for row in averaged if row[0] == 'BST' and row[1] == 'sorted' and row[2] == 'insert') + print(f" - Случайные данные: {bst_shuffled_insert:.6f} сек") + print(f" - Отсортированные данные: {bst_sorted_insert:.6f} сек") + if bst_shuffled_insert > 0: + print(f" - Замедление в {bst_sorted_insert / bst_shuffled_insert:.1f} раз") + print(" Причина: на отсортированных данных BST вырождается в связный список (глубина = N)") + + print("\n2. Стабильность хеш-таблицы:") + ht_shuffled = next( + row[3] for row in averaged if row[0] == 'HashTable' and row[1] == 'shuffled' and row[2] == 'insert') + ht_sorted = next(row[3] for row in averaged if row[0] == 'HashTable' and row[1] == 'sorted' and row[2] == 'insert') + print(f" - Случайные: {ht_shuffled:.6f} сек") + print(f" - Отсортированные: {ht_sorted:.6f} сек") + print(" Причина: хеш-функция равномерно распределяет ключи независимо от порядка") + + print("\n3. Медленный поиск в связном списке:") + ll_search = next(row[3] for row in averaged if row[0] == 'LinkedList' and row[1] == 'shuffled' and row[2] == 'find') + ht_search = next(row[3] for row in averaged if row[0] == 'HashTable' and row[1] == 'shuffled' and row[2] == 'find') + print(f" - LinkedList: {ll_search:.6f} сек") + print(f" - HashTable: {ht_search:.6f} сек") + if ht_search > 0: + print(f" - Хеш-таблица быстрее в {ll_search / ht_search:.1f} раз") + print(" Причина: поиск в списке всегда O(n), в хеш-таблице ~O(1)") + + print("\n4. Удаление:") + for label in ['LinkedList', 'HashTable', 'BST']: + del_shuff = next(row[3] for row in averaged if row[0] == label and row[1] == 'shuffled' and row[2] == 'delete') + del_sort = next(row[3] for row in averaged if row[0] == label and row[1] == 'sorted' and row[2] == 'delete') + print(f" - {label:15}: случ.={del_shuff:.6f} сек, отсорт.={del_sort:.6f} сек") + + print("\n5. Рекомендации:") + print(" - Частый поиск + вставки → Хеш-таблица") + print(" - Нужна сортировка «из коробки» → Сбалансированное BST (AVL/Красно-чёрное)") + print(" - Только добавление в конец → Связный список") + print(" - Обычный BST опасен на реальных частично упорядоченных данных!") + +if __name__ == '__main__': + main() \ No newline at end of file