2026-rff_mp/tseremonnikovaaa/task 1/docs/data/main.py

505 lines
20 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 sys
import matplotlib.pyplot as plt
import numpy as np
sys.setrecursionlimit(20000)
REPEATS = 5
N = 10000
def ll_insert(head, name, phone):
current = head
prev = None
while current is not None:
if current['name'] == name:
current['phone'] = phone
return head
prev = current
current = current['next']
new_node = {'name': name, 'phone': phone, 'next': None}
if prev is None:
return new_node
else:
prev['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_collect_all(head):
records = []
current = head
while current is not None:
records.append((current['name'], current['phone']))
current = current['next']
records.sort(key=lambda x: x[0])
return records
def hash_function(name, size):
total = 0
for ch in name:
total = (total * 31 + ord(ch)) % size
return total
def ht_create(size=2000):
return [None] * size
def ht_insert(buckets, name, phone):
idx = hash_function(name, len(buckets))
buckets[idx] = ll_insert(buckets[idx], name, phone)
def ht_find(buckets, name):
idx = hash_function(name, len(buckets))
return ll_find(buckets[idx], name)
def ht_delete(buckets, name):
idx = hash_function(name, len(buckets))
buckets[idx] = ll_delete(buckets[idx], name)
def ht_collect_all(buckets):
all_records = []
for bucket in buckets:
current = bucket
while current is not None:
all_records.append((current['name'], current['phone']))
current = current['next']
all_records.sort(key=lambda x: x[0])
return all_records
def bst_insert(root, name, phone):
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 is not None:
if name < current['name']:
current = current['left']
elif name > current['name']:
current = current['right']
else:
return current['phone']
return None
def bst_find_min(node):
while node['left'] is not None:
node = node['left']
return node
def bst_delete(root, name):
parent = None
current = root
while current is not None and current['name'] != name:
parent = current
if name < current['name']:
current = current['left']
else:
current = current['right']
if current is None:
return root
if current['left'] is None and current['right'] is None:
if parent is None:
return None
if parent['left'] is current:
parent['left'] = None
else:
parent['right'] = None
return root
if current['left'] is None:
if parent is None:
return current['right']
if parent['left'] is current:
parent['left'] = current['right']
else:
parent['right'] = current['right']
return root
if current['right'] is None:
if parent is None:
return current['left']
if parent['left'] is current:
parent['left'] = current['left']
else:
parent['right'] = current['left']
return root
succ_parent = current
succ = current['right']
while succ['left'] is not None:
succ_parent = succ
succ = succ['left']
current['name'] = succ['name']
current['phone'] = succ['phone']
if succ_parent['left'] is succ:
succ_parent['left'] = succ['right']
else:
succ_parent['right'] = succ['right']
return root
def bst_inorder_collect(root, records=None):
if records is None:
records = []
if root is not None:
bst_inorder_collect(root['left'], records)
records.append((root['name'], root['phone']))
bst_inorder_collect(root['right'], records)
return records
def generate_records(N=10000):
records = []
for i in range(N):
name = f"User_{i:05d}"
phone = f"+7-999-{random.randint(1000000, 9999999)}"
records.append((name, phone))
return records
def measure_insertion(struct_type, records):
times = []
for _ in range(REPEATS):
if struct_type == 'll':
head = None
start = time.perf_counter()
for name, phone in records:
head = ll_insert(head, name, phone)
end = time.perf_counter()
elif struct_type == 'ht':
buckets = ht_create(2000)
start = time.perf_counter()
for name, phone in records:
ht_insert(buckets, name, phone)
end = time.perf_counter()
else:
root = None
start = time.perf_counter()
for name, phone in records:
root = bst_insert(root, name, phone)
end = time.perf_counter()
times.append(end - start)
return times
def build_structure(struct_type, records):
if struct_type == 'll':
head = None
for name, phone in records:
head = ll_insert(head, name, phone)
return head
elif struct_type == 'ht':
buckets = ht_create(2000)
for name, phone in records:
ht_insert(buckets, name, phone)
return buckets
else:
root = None
for name, phone in records:
root = bst_insert(root, name, phone)
return root
def measure_search(struct_type, structure, records):
times = []
N_records = len(records)
for _ in range(REPEATS):
indices = random.sample(range(N_records), 100)
existing_names = [records[i][0] for i in indices]
missing_names = [f"None_{i}" for i in range(10)]
search_names = existing_names + missing_names
random.shuffle(search_names)
start = time.perf_counter()
if struct_type == 'll':
for name in search_names:
ll_find(structure, name)
elif struct_type == 'ht':
for name in search_names:
ht_find(structure, name)
else:
for name in search_names:
bst_find(structure, name)
times.append(time.perf_counter() - start)
return times
def measure_deletion(struct_type, records):
times = []
N_records = len(records)
for _ in range(REPEATS):
indices = random.sample(range(N_records), 50)
delete_names = [records[i][0] for i in indices]
if struct_type == 'll':
head = None
for name, phone in records:
head = ll_insert(head, name, phone)
start = time.perf_counter()
for name in delete_names:
head = ll_delete(head, name)
end = time.perf_counter()
elif struct_type == 'ht':
buckets = ht_create(2000)
for name, phone in records:
ht_insert(buckets, name, phone)
start = time.perf_counter()
for name in delete_names:
ht_delete(buckets, name)
end = time.perf_counter()
else:
root = None
for name, phone in records:
root = bst_insert(root, name, phone)
start = time.perf_counter()
for name in delete_names:
root = bst_delete(root, name)
end = time.perf_counter()
times.append(end - start)
return times
def plot_bar_charts(insert_data, search_data, delete_data):
"""Построение столбчатых диаграмм"""
structures = ['ll', 'ht', 'bst']
labels = ['Связный список', 'Хеш-таблица', 'Двоичное дерево']
mode_labels = ['Случайный порядок', 'Отсортированный порядок']
colors = ['skyblue', 'salmon']
x = np.arange(len(structures))
width = 0.35
# График вставки
fig1, ax1 = plt.subplots(figsize=(10, 6))
means_sh = [sum(insert_data[s]['shuffled'])/len(insert_data[s]['shuffled']) for s in structures]
means_so = [sum(insert_data[s]['sorted'])/len(insert_data[s]['sorted']) for s in structures]
rects1 = ax1.bar(x - width/2, means_sh, width, label=mode_labels[0], color=colors[0])
rects2 = ax1.bar(x + width/2, means_so, width, label=mode_labels[1], color=colors[1])
ax1.set_ylabel('Время (секунды)')
ax1.set_title('Вставка всех записей (10000 шт.)')
ax1.set_xticks(x)
ax1.set_xticklabels(labels)
ax1.legend()
ax1.set_yscale('log')
for rect in rects1 + rects2:
h = rect.get_height()
ax1.annotate(f'{h:.4f}', xy=(rect.get_x() + rect.get_width()/2, h),
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=8)
plt.tight_layout()
plt.savefig('insert_comparison.png', dpi=150)
plt.show()
print(" График вставки сохранён: insert_comparison.png")
# График поиска
fig2, ax2 = plt.subplots(figsize=(10, 6))
means_sh = [sum(search_data[s]['shuffled'])/len(search_data[s]['shuffled']) for s in structures]
means_so = [sum(search_data[s]['sorted'])/len(search_data[s]['sorted']) for s in structures]
rects1 = ax2.bar(x - width/2, means_sh, width, label=mode_labels[0], color=colors[0])
rects2 = ax2.bar(x + width/2, means_so, width, label=mode_labels[1], color=colors[1])
ax2.set_ylabel('Время (секунды)')
ax2.set_title('Поиск (100 существующих + 10 отсутствующих)')
ax2.set_xticks(x)
ax2.set_xticklabels(labels)
ax2.legend()
for rect in rects1 + rects2:
h = rect.get_height()
ax2.annotate(f'{h:.6f}', xy=(rect.get_x() + rect.get_width()/2, h),
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=8)
plt.tight_layout()
plt.savefig('search_comparison.png', dpi=150)
plt.show()
print(" График поиска сохранён: search_comparison.png")
# График удаления
fig3, ax3 = plt.subplots(figsize=(10, 6))
means_sh = [sum(delete_data[s]['shuffled'])/len(delete_data[s]['shuffled']) for s in structures]
means_so = [sum(delete_data[s]['sorted'])/len(delete_data[s]['sorted']) for s in structures]
rects1 = ax3.bar(x - width/2, means_sh, width, label=mode_labels[0], color=colors[0])
rects2 = ax3.bar(x + width/2, means_so, width, label=mode_labels[1], color=colors[1])
ax3.set_ylabel('Время (секунды)')
ax3.set_title('Удаление 50 случайных записей')
ax3.set_xticks(x)
ax3.set_xticklabels(labels)
ax3.legend()
for rect in rects1 + rects2:
h = rect.get_height()
ax3.annotate(f'{h:.6f}', xy=(rect.get_x() + rect.get_width()/2, h),
xytext=(0, 3), textcoords="offset points", ha='center', va='bottom', fontsize=8)
plt.tight_layout()
plt.savefig('delete_comparison.png', dpi=150)
plt.show()
print(" График удаления сохранён: delete_comparison.png")
def plot_attempts_graphs(data, op_name, op_title):
"""Построение графиков по 5 попыткам"""
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
struct_config = [
('ll', 'Связный список', 'red', 'o'),
('ht', 'Хеш-таблица', 'green', 's'),
('bst', 'Двоичное дерево', 'blue', '^')
]
# Случайный порядок
for struct, label, color, marker in struct_config:
times = data[struct]['shuffled']
x = range(1, len(times) + 1)
ax1.plot(x, times, marker=marker, color=color, label=label,
linestyle='--', linewidth=1)
ax1.scatter(x, times, color=color, s=60, zorder=5)
ax1.set_xlabel('Номер попытки')
ax1.set_ylabel('Время (секунды)')
ax1.set_title(f'{op_title} случайный порядок')
ax1.legend()
ax1.grid(True, linestyle=':', alpha=0.7)
# Отсортированный порядок
for struct, label, color, marker in struct_config:
times = data[struct]['sorted']
x = range(1, len(times) + 1)
ax2.plot(x, times, marker=marker, color=color, label=label,
linestyle='--', linewidth=1)
ax2.scatter(x, times, color=color, s=60, zorder=5)
ax2.set_xlabel('Номер попытки')
ax2.set_ylabel('Время (секунды)')
ax2.set_title(f'{op_title} отсортированный порядок')
ax2.legend()
ax2.grid(True, linestyle=':', alpha=0.7)
plt.tight_layout()
plt.savefig(f'{op_name}_5attempts.png', dpi=150)
plt.show()
print(f" График {op_name}_5attempts.png сохранён")
def main():
print("ЛАБОРАТОРНАЯ РАБОТА №1: СРАВНЕНИЕ СТРУКТУР ДАННЫХ")
print("\n1. Генерация тестовых данных...")
records = generate_records(N)
random.shuffle(records)
records_sorted = sorted(records, key=lambda x: x[0])
print(f" Сгенерировано {N} записей")
results = []
struct_names = {'ll': 'Связный список', 'ht': 'Хеш-таблица', 'bst': 'Двоичное дерево'}
mode_names = {'shuffled': 'случайный', 'sorted': 'отсортированный'}
op_names = {'insert': 'Вставка всех записей', 'find': 'Поиск записей', 'delete': 'Удаление записей'}
insert_data = {'ll': {}, 'ht': {}, 'bst': {}}
search_data = {'ll': {}, 'ht': {}, 'bst': {}}
delete_data = {'ll': {}, 'ht': {}, 'bst': {}}
# Вставка
print("\n2. Тестирование ВСТАВКИ (10000 записей):")
for struct in ['ll', 'ht', 'bst']:
print(f"\n {struct_names[struct]}:")
times_sh = measure_insertion(struct, records)
times_so = measure_insertion(struct, records_sorted)
insert_data[struct]['shuffled'] = times_sh
insert_data[struct]['sorted'] = times_so
print(f" случайный: {[round(t,6) for t in times_sh]}, среднее = {sum(times_sh)/len(times_sh):.6f}")
print(f" отсортированный: {[round(t,6) for t in times_so]}, среднее = {sum(times_so)/len(times_so):.6f}")
results.append([struct_names[struct], mode_names['shuffled'], op_names['insert'], sum(times_sh)/len(times_sh)] + times_sh)
results.append([struct_names[struct], mode_names['sorted'], op_names['insert'], sum(times_so)/len(times_so)] + times_so)
# Поиск
print("\n3. Тестирование ПОИСКА (110 запросов):")
for struct in ['ll', 'ht', 'bst']:
print(f"\n {struct_names[struct]}:")
structure_sh = build_structure(struct, records)
times_find_sh = measure_search(struct, structure_sh, records)
search_data[struct]['shuffled'] = times_find_sh
print(f" случайный: {[round(t,6) for t in times_find_sh]}, среднее = {sum(times_find_sh)/len(times_find_sh):.6f}")
results.append([struct_names[struct], mode_names['shuffled'], op_names['find'], sum(times_find_sh)/len(times_find_sh)] + times_find_sh)
structure_so = build_structure(struct, records_sorted)
times_find_so = measure_search(struct, structure_so, records_sorted)
search_data[struct]['sorted'] = times_find_so
print(f" отсортированный: {[round(t,6) for t in times_find_so]}, среднее = {sum(times_find_so)/len(times_find_so):.6f}")
results.append([struct_names[struct], mode_names['sorted'], op_names['find'], sum(times_find_so)/len(times_find_so)] + times_find_so)
# Удаление
print("\n4. Тестирование УДАЛЕНИЯ (50 записей):")
for struct in ['ll', 'ht', 'bst']:
print(f"\n {struct_names[struct]}:")
times_del_sh = measure_deletion(struct, records)
delete_data[struct]['shuffled'] = times_del_sh
print(f" случайный: {[round(t,6) for t in times_del_sh]}, среднее = {sum(times_del_sh)/len(times_del_sh):.6f}")
results.append([struct_names[struct], mode_names['shuffled'], op_names['delete'], sum(times_del_sh)/len(times_del_sh)] + times_del_sh)
times_del_so = measure_deletion(struct, records_sorted)
delete_data[struct]['sorted'] = times_del_so
print(f" отсортированный: {[round(t,6) for t in times_del_so]}, среднее = {sum(times_del_so)/len(times_del_so):.6f}")
results.append([struct_names[struct], mode_names['sorted'], op_names['delete'], sum(times_del_so)/len(times_del_so)] + times_del_so)
# Сохранение CSV
print("\n5. Сохранение результатов в CSV...")
with open("phonebook_results.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(['Структура', 'Режим', 'Операция', 'Среднее', 'Замер1', 'Замер2', 'Замер3', 'Замер4', 'Замер5'])
writer.writerows(results)
print(" CSV-файл сохранён: phonebook_results.csv")
# Сводная таблица
print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ (средние значения)")
print(f"{'Структура':<20} {'Режим':<15} {'Вставка(с)':<12} {'Поиск(с)':<12} {'Удаление(с)':<12}")
print("-" * 70)
for struct in ['ll', 'ht', 'bst']:
for mode in ['shuffled', 'sorted']:
ins_avg = sum(insert_data[struct][mode]) / REPEATS
sea_avg = sum(search_data[struct][mode]) / REPEATS
del_avg = sum(delete_data[struct][mode]) / REPEATS
mode_rus = "случайный" if mode == 'shuffled' else "отсортированный"
print(f"{struct_names[struct]:<20} {mode_rus:<15} {ins_avg:<12.6f} {sea_avg:<12.6f} {del_avg:<12.6f}")
# Построение графиков
print("\n6. Построение графиков...")
try:
plot_bar_charts(insert_data, search_data, delete_data)
plot_attempts_graphs(insert_data, 'insert', 'Вставка')
plot_attempts_graphs(search_data, 'search', 'Поиск')
plot_attempts_graphs(delete_data, 'delete', 'Удаление')
print("\n Все графики успешно сохранены!")
except Exception as e:
print(f" Ошибка при построении графиков: {e}")