import time import random import csv import sys sys.setrecursionlimit (50000) from datetime import datetime # ==================== СВЯЗНЫЙ СПИСОК ==================== def ll_insert(head, name, phone): """Добавляет или обновляет запись в связном списке""" # Проверяем, не существует ли уже такой name curr = head while curr: if curr['name'] == name: curr['phone'] = phone # обновляем return head curr = curr['next'] # Если не нашли, вставляем в конец new_node = {'name': name, 'phone': phone, 'next': None} if head is None: return new_node curr = head while curr['next']: curr = curr['next'] curr['next'] = new_node return head def ll_find(head, name): """Ищет запись по имени, возвращает телефон или None""" 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, size): """Простая хеш-функция""" return sum(ord(c) for c in name) % size def ht_insert(buckets, name, phone): """Вставляет запись в хеш-таблицу""" index = hash_function(name, len(buckets)) # Используем ll_insert для бакета buckets[index] = ll_insert(buckets[index], name, phone) return buckets 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) return buckets def ht_list_all(buckets): """Собирает все записи из всех бакетов и сортирует""" all_records = [] for bucket in buckets: curr = bucket while curr: all_records.append((curr['name'], curr['phone'])) curr = curr['next'] all_records.sort(key=lambda x: x[0]) return all_records # ==================== БИНАРНОЕ ДЕРЕВО ПОИСКА ==================== def bst_insert(root, name, phone): """Вставляет запись в BST (рекурсивно)""" 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): """Ищет запись в BST""" 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_find_min(node): """Находит узел с минимальным значением""" current = node while current and current['left']: current = current['left'] return current def bst_delete(root, name): """Удаляет запись из BST""" 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'] elif root['right'] is None: return root['left'] # Узел с двумя детьми temp = bst_find_min(root['right']) root['name'] = temp['name'] root['phone'] = temp['phone'] root['right'] = bst_delete(root['right'], temp['name']) return root def bst_list_all(root): """Центрированный обход (возвращает отсортированный список)""" records = [] def inorder_traversal(node): if node: inorder_traversal(node['left']) records.append((node['name'], node['phone'])) inorder_traversal(node['right']) inorder_traversal(root) return records # ==================== ГЕНЕРАЦИЯ ТЕСТОВЫХ ДАННЫХ ==================== def generate_test_data(n=10000): """Генерирует shuffled и sorted версии данных""" # Генерируем имена с небольшим количеством коллизий names_pool = [f"User_{i:05d}" for i in range(n // 10)] # 1000 уникальных имен records = [] for i in range(n): name = random.choice(names_pool) # повторяющиеся имена для коллизий phone = f"+7-999-{random.randint(1000000, 9999999)}" records.append((name, phone)) # Создаем shuffled и sorted версии 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(struct_type, records, buckets_size=None): """Замеряет время вставки всех записей""" if struct_type == "LinkedList": head = None start = time.perf_counter() for name, phone in records: head = ll_insert(head, name, phone) end = time.perf_counter() elif struct_type == "HashTable": if buckets_size is None: buckets_size = 1000 buckets = [None] * buckets_size start = time.perf_counter() for name, phone in records: buckets = ht_insert(buckets, name, phone) end = time.perf_counter() elif struct_type == "BST": root = None start = time.perf_counter() for name, phone in records: root = bst_insert(root, name, phone) end = time.perf_counter() else: return None, None elapsed = end - start return elapsed, (head if struct_type == "LinkedList" else (buckets if struct_type == "HashTable" else root)) def measure_find(struct_type, data_structure, records, num_existing=100, num_nonexistent=10): """Замеряет время поиска записей""" # Выбираем существующие записи existing_names = random.sample([name for name, _ in records[:len(records)//2]], min(num_existing, len(records))) # Создаем несуществующие имена nonexistent_names = [f"None_{i}" for i in range(num_nonexistent)] all_queries = existing_names + nonexistent_names random.shuffle(all_queries) start = time.perf_counter() for name in all_queries: if struct_type == "LinkedList": ll_find(data_structure, name) elif struct_type == "HashTable": ht_find(data_structure, name) elif struct_type == "BST": bst_find(data_structure, name) end = time.perf_counter() return end - start def measure_delete(struct_type, data_structure, records, num_to_delete=50): """Замеряет время удаления записей""" # Выбираем случайные имена для удаления names_to_delete = random.sample([name for name, _ in records[:len(records)//2]], min(num_to_delete, len(records))) start = time.perf_counter() for name in names_to_delete: if struct_type == "LinkedList": data_structure = ll_delete(data_structure, name) elif struct_type == "HashTable": data_structure = ht_delete(data_structure, name) elif struct_type == "BST": data_structure = bst_delete(data_structure, name) end = time.perf_counter() return end - start # ==================== ОСНОВНОЙ ЭКСПЕРИМЕНТ ==================== def run_experiment(): """Запускает все эксперименты и сохраняет результаты""" print("Генерация тестовых данных...") records_shuffled, records_sorted = generate_test_data(10000) structures = ["LinkedList", "HashTable", "BST"] modes = {"shuffled": records_shuffled, "sorted": records_sorted} all_results = [] all_results.append(["Структура", "Режим", "Операция", "Повторение", "Время (сек)"]) # Количество повторений repeats = 5 for struct in structures: for mode_name, records in modes.items(): print(f"\nТестирование: {struct}, режим {mode_name}") # Вставка insertion_times = [] for rep in range(repeats): print(f" Вставка, повторение {rep+1}/{repeats}...") elapsed, data_struct = measure_insertion(struct, records) insertion_times.append(elapsed) all_results.append([struct, mode_name, "вставка", rep+1, elapsed]) # Используем последнюю структуру для поиска и удаления # (пересоздаем для чистоты эксперимента) _, data_struct = measure_insertion(struct, records) # Поиск find_times = [] for rep in range(repeats): print(f" Поиск, повторение {rep+1}/{repeats}...") elapsed = measure_find(struct, data_struct, records) find_times.append(elapsed) all_results.append([struct, mode_name, "поиск", rep+1, elapsed]) # Удаление delete_times = [] # Создаем свежую структуру для удаления _, fresh_data_struct = measure_insertion(struct, records) for rep in range(repeats): print(f" Удаление, повторение {rep+1}/{repeats}...") elapsed = measure_delete(struct, fresh_data_struct, records) delete_times.append(elapsed) all_results.append([struct, mode_name, "удаление", rep+1, elapsed]) # Выводим средние значения print(f" Среднее время вставки: {sum(insertion_times)/len(insertion_times):.6f} сек") print(f" Среднее время поиска: {sum(find_times)/len(find_times):.6f} сек") print(f" Среднее время удаления: {sum(delete_times)/len(delete_times):.6f} сек") # Сохраняем результаты в CSV timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") csv_filename = f"experiment_results_{timestamp}.csv" with open(csv_filename, 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerows(all_results) print(f"\nРезультаты сохранены в файл: {csv_filename}") # Сохраняем также средние значения для удобства avg_results = [] avg_results.append(["Структура", "Режим", "Операция", "Среднее время (сек)", "Мин", "Макс"]) # Группируем по структуре, режиму, операции grouped = {} for row in all_results[1:]: # пропускаем заголовок struct, mode, op, rep, time_val = row key = (struct, mode, op) if key not in grouped: grouped[key] = [] grouped[key].append(time_val) for (struct, mode, op), times in grouped.items(): avg_time = sum(times) / len(times) min_time = min(times) max_time = max(times) avg_results.append([struct, mode, op, avg_time, min_time, max_time]) avg_filename = f"average_results_{timestamp}.csv" with open(avg_filename, 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerows(avg_results) print(f"Средние значения сохранены в файл: {avg_filename}") return all_results, avg_results if __name__ == "__main__": print("Начало эксперимента...") print("="*50) results, avg_results = run_experiment() print("="*50) print("Эксперимент завершен!")