2026-rff_mp/osipovamd/docs/task1.py
2026-05-17 15:01:19 +03:00

493 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import time
import random
import csv
import matplotlib.pyplot as plt
import numpy as np
import sys
from collections import defaultdict
# Увеличиваем лимит рекурсии для BST
sys.setrecursionlimit(10000)
def ll_insert(head, name, phone):
current = head
while current:
if current['name'] == name:
current['phone'] = phone
return head
current = current['next']
new_node = {'name': name, 'phone': phone, 'next': head}
return new_node
def ll_find(head, name):
current = head
while current:
if current['name'] == name:
return current['phone']
current = current['next']
return None
def ll_delete(head, name):
if not head:
return None
if head['name'] == name:
return head['next']
current = head
while current['next']:
if current['next']['name'] == name:
current['next'] = current['next']['next']
return head
current = current['next']
return head
def ll_list_all(head):
records = []
current = head
while current:
records.append((current['name'], current['phone']))
current = current['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_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 bucket in buckets:
current = bucket
while current:
records.append((current['name'], current['phone']))
current = current['next']
records.sort(key=lambda x: x[0])
return records
def bst_insert(root, name, phone):
"""Итеративная вставка для избежания RecursionError"""
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
if root is None:
return new_node
current = root
while True:
if name < current['name']:
if current['left'] is None:
current['left'] = new_node
break
current = current['left']
elif name > current['name']:
if current['right'] is None:
current['right'] = new_node
break
current = current['right']
else:
current['phone'] = phone
break
return root
def bst_find(root, name):
current = root
while current:
if name == current['name']:
return current['phone']
elif name < current['name']:
current = current['left']
else:
current = current['right']
return None
def bst_find_min(node):
current = node
while current and current['left']:
current = current['left']
return current
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']
elif root['right'] is None:
return root['left']
min_node = bst_find_min(root['right'])
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):
records = []
stack = []
current = root
while stack or current:
while current:
stack.append(current)
current = current['left']
current = stack.pop()
records.append((current['name'], current['phone']))
current = current['right']
return records
def copy_linked_list(head):
if not head:
return None
new_head = {'name': head['name'], 'phone': head['phone'], 'next': None}
current_new = new_head
current_old = head['next']
while current_old:
current_new['next'] = {'name': current_old['name'], 'phone': current_old['phone'], 'next': None}
current_new = current_new['next']
current_old = current_old['next']
return new_head
def copy_bst(node):
if not node:
return None
return {
'name': node['name'],
'phone': node['phone'],
'left': copy_bst(node['left']),
'right': copy_bst(node['right'])
}
def generate_test_data(N=10000):
names = [f"User_{i:05d}" for i in range(N)]
records = [(name, f"+7-999-{random.randint(1000000, 9999999)}") for name in names]
records_shuffled = records.copy()
random.shuffle(records_shuffled)
records_sorted = sorted(records, key=lambda x: x[0])
return records_shuffled, records_sorted
def get_test_queries(records, num_existing=100, num_nonexisting=10):
existing_names = [name for name, _ in random.sample(records, min(num_existing, len(records)))]
nonexisting_names = [f"None_{i:05d}" for i in range(num_nonexisting)]
queries = existing_names + nonexisting_names
random.shuffle(queries)
return queries
def get_delete_names(records, num_to_delete=50):
return [name for name, _ in random.sample(records, min(num_to_delete, len(records)))]
def measure_insertion(structure_type, records, repeats=3):
times = []
for _ in range(repeats):
if structure_type == "LinkedList":
structure = None
insert_func = ll_insert
elif structure_type == "HashTable":
structure = ht_create(2000)
insert_func = ht_insert
elif structure_type == "BST":
structure = None
insert_func = bst_insert
else:
raise ValueError(f"Unknown structure: {structure_type}")
start = time.perf_counter()
for name, phone in records:
if structure_type == "HashTable":
insert_func(structure, name, phone)
else:
structure = insert_func(structure, name, phone)
end = time.perf_counter()
times.append(end - start)
return times
def measure_search(structure_type, structure, queries, repeats=3):
times = []
for _ in range(repeats):
start = time.perf_counter()
for name in queries:
if structure_type == "LinkedList":
ll_find(structure, name)
elif structure_type == "HashTable":
ht_find(structure, name)
elif structure_type == "BST":
bst_find(structure, name)
end = time.perf_counter()
times.append(end - start)
return times
def measure_deletion(structure_type, structure, names_to_delete, repeats=3):
times = []
for _ in range(repeats):
if structure_type == "LinkedList":
temp_structure = copy_linked_list(structure)
delete_func = ll_delete
elif structure_type == "HashTable":
temp_structure = structure.copy()
for i in range(len(temp_structure)):
if temp_structure[i]:
temp_structure[i] = copy_linked_list(temp_structure[i])
delete_func = ht_delete
elif structure_type == "BST":
temp_structure = copy_bst(structure)
delete_func = bst_delete
start = time.perf_counter()
for name in names_to_delete:
if structure_type == "HashTable":
delete_func(temp_structure, name)
else:
temp_structure = delete_func(temp_structure, name)
end = time.perf_counter()
times.append(end - start)
return times
def run_experiment(N=2000):
"""Запускает все эксперименты и возвращает результаты"""
print(f"Генерация тестовых данных (N={N})...")
records_shuffled, records_sorted = generate_test_data(N)
queries = get_test_queries(records_shuffled, num_existing=100, num_nonexisting=10)
delete_names = get_delete_names(records_shuffled, num_to_delete=50)
structures = ["LinkedList", "HashTable", "BST"]
modes = ["случайный", "отсортированный"]
results = []
print("\nНачало экспериментов:")
print("=" * 60)
for structure in structures:
print(f"\nТестирование {structure}...")
for mode in modes:
print(f" Режим: {mode}")
records = records_shuffled if mode == "случайный" else records_sorted
# Вставка
print(f" Измерение вставки...")
try:
insert_times = measure_insertion(structure, records, repeats=3)
avg_insert = sum(insert_times) / len(insert_times)
except RecursionError:
print(f" ОШИБКА: Превышена глубина рекурсии при вставке в {structure} для {mode} режима")
continue
print(f" Создание финальной структуры...")
if structure == "LinkedList":
final_structure = None
for name, phone in records:
final_structure = ll_insert(final_structure, name, phone)
elif structure == "HashTable":
final_structure = ht_create(2000)
for name, phone in records:
ht_insert(final_structure, name, phone)
elif structure == "BST":
final_structure = None
for name, phone in records:
final_structure = bst_insert(final_structure, name, phone)
print(f" Измерение поиска...")
search_times = measure_search(structure, final_structure, queries, repeats=3)
avg_search = sum(search_times) / len(search_times)
print(f" Измерение удаления...")
deletion_times = measure_deletion(structure, final_structure, delete_names, repeats=3)
avg_deletion = sum(deletion_times) / len(deletion_times)
results.append({
"Структура": structure,
"Режим": mode,
"Операция": "вставка",
"Замеры": insert_times,
"Среднее": avg_insert
})
results.append({
"Структура": structure,
"Режим": mode,
"Операция": "поиск",
"Замеры": search_times,
"Среднее": avg_search
})
results.append({
"Структура": structure,
"Режим": mode,
"Операция": "удаление",
"Замеры": deletion_times,
"Среднее": avg_deletion
})
print(f" Вставка: {avg_insert:.6f} сек")
print(f" Поиск: {avg_search:.6f} сек")
print(f" Удаление: {avg_deletion:.6f} сек")
return results
# ============================================================
# СОХРАНЕНИЕ РЕЗУЛЬТАТОВ В CSV
# ============================================================
import os
import csv
from datetime import datetime
def save_to_csv(results, filename="results.csv"):
save_dir = "/Users/mariiaos/2026-rff_mp/osipovamd/docs"
filepath = os.path.join(save_dir, filename)
with open(filepath, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Структура", "Режим", "Операция", "Замер1", "Замер2", "Замер3", "Среднее"])
for res in results:
row = [
res["Структура"],
res["Режим"],
res["Операция"],
*[f"{t:.6f}" for t in res["Замеры"]],
f"{res['Среднее']:.6f}"
]
writer.writerow(row)
print(f"\nРезультаты сохранены в: {filepath}")
return filepath
def plot_results(results):
if not results:
print("Нет данных для построения графиков!")
return
plt.style.use('seaborn-v0_8-darkgrid')
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
operations = ["вставка", "поиск", "удаление"]
structures = ["LinkedList", "HashTable", "BST"]
modes = ["случайный", "отсортированный"]
colors = {'LinkedList': '#FF6B6B', 'HashTable': '#4ECDC4', 'BST': '#45B7D1'}
for idx, operation in enumerate(operations):
ax = axes[idx]
x = np.arange(len(modes))
width = 0.25
multiplier = 0
for structure in structures:
values = []
for mode in modes:
found = False
for res in results:
if (res["Структура"] == structure and
res["Режим"] == mode and
res["Операция"] == operation):
values.append(res["Среднее"])
found = True
break
if not found:
values.append(0)
if max(values) > 0:
offset = width * multiplier
bars = ax.bar(x + offset, values, width, label=structure, color=colors[structure])
multiplier += 1
ax.set_xlabel('Режим данных', fontsize=12)
ax.set_ylabel('Время (секунды)', fontsize=12)
ax.set_title(f'{operation.capitalize()}', fontsize=14, fontweight='bold')
ax.set_xticks(x + width)
ax.set_xticklabels(modes)
ax.legend(loc='upper left')
ax.grid(True, alpha=0.3)
plt.suptitle('Сравнение производительности структур данных',
fontsize=16, fontweight='bold')
plt.tight_layout()
plt.savefig('performance_comparison.png', dpi=300, bbox_inches='tight')
plt.show()
print("\nГрафик сохранён как 'performance_comparison.png'")
if __name__ == "__main__":
print("=" * 60)
print("ТЕСТИРОВАНИЕ ПРОИЗВОДИТЕЛЬНОСТИ СТРУКТУР ДАННЫХ")
print("=" * 60)
results = run_experiment(N=1000)
save_to_csv(results)
if results:
print("\nПостроение графиков...")
plot_results(results)
print("\n" + "=" * 60)
print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ (среднее время в секундах)")
print("=" * 60)
print(f"{'Структура':<12} {'Режим':<12} {'Вставка':<10} {'Поиск':<10} {'Удаление':<10}")
print("-" * 60)
for structure in ["LinkedList", "HashTable", "BST"]:
for mode in ["случайный", "отсортированный"]:
insert_time = search_time = delete_time = 0
for res in results:
if res["Структура"] == structure and res["Режим"] == mode:
if res["Операция"] == "вставка":
insert_time = res["Среднее"]
elif res["Операция"] == "поиск":
search_time = res["Среднее"]
elif res["Операция"] == "удаление":
delete_time = res["Среднее"]
if insert_time > 0 or search_time > 0 or delete_time > 0:
print(f"{structure:<12} {mode:<12} {insert_time:<10.6f} {search_time:<10.6f} {delete_time:<10.6f}")
else:
print("\nЭксперимент не дал результатов из-за ошибок.")
print("\nЭксперимент завершён!")