2026-rff_mp/AgapovaDS/docs/data/1-st/phonebook.py
2026-09-03 02:03:21 +03:00

336 lines
10 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 random
import time
import sys
import csv
import os
import matplotlib.pyplot as plt
import numpy as np
sys.setrecursionlimit(10000)
ll_head = None
def ll_insert(name, phone):
global ll_head
cur = ll_head
while cur is not None:
if cur['name'] == name:
cur['phone'] = phone
return
cur = cur['next']
new_node = {'name': name, 'phone': phone, 'next': ll_head}
ll_head = new_node
def ll_find(name):
cur = ll_head
while cur is not None:
if cur['name'] == name:
return cur['phone']
cur = cur['next']
return None
def ll_delete(name):
global ll_head
if ll_head is None:
return
if ll_head['name'] == name:
ll_head = ll_head['next']
return
prev = ll_head
cur = ll_head['next']
while cur is not None:
if cur['name'] == name:
prev['next'] = cur['next']
return
prev = cur
cur = cur['next']
def ll_list_all():
records = []
cur = ll_head
while cur is not None:
records.append((cur['name'], cur['phone']))
cur = cur['next']
records.sort(key=lambda x: x[0])
return records
BUCKET_COUNT = 10
buckets = [None] * BUCKET_COUNT
def hash_func(name):
s = 0
for ch in name:
s += ord(ch)
return s % BUCKET_COUNT
def ht_insert(name, phone):
global buckets
idx = hash_func(name)
cur = buckets[idx]
while cur is not None:
if cur['name'] == name:
cur['phone'] = phone
return
cur = cur['next']
new_node = {'name': name, 'phone': phone, 'next': buckets[idx]}
buckets[idx] = new_node
def ht_find(name):
idx = hash_func(name)
cur = buckets[idx]
while cur is not None:
if cur['name'] == name:
return cur['phone']
cur = cur['next']
return None
def ht_delete(name):
global buckets
idx = hash_func(name)
head = buckets[idx]
if head is None:
return
if head['name'] == name:
buckets[idx] = head['next']
return
prev = head
cur = head['next']
while cur is not None:
if cur['name'] == name:
prev['next'] = cur['next']
return
prev = cur
cur = cur['next']
def ht_list_all():
all_records = []
for head in buckets:
cur = head
while cur is not None:
all_records.append((cur['name'], cur['phone']))
cur = cur['next']
all_records.sort(key=lambda x: x[0])
return all_records
bst_root = None
def bst_create_node(name, phone):
return {'name': name, 'phone': phone, 'left': None, 'right': None}
def bst_insert(name, phone):
global bst_root
if bst_root is None:
bst_root = bst_create_node(name, phone)
return
current = bst_root
while True:
if name == current['name']:
current['phone'] = phone
return
elif name < current['name']:
if current['left'] is None:
current['left'] = bst_create_node(name, phone)
return
current = current['left']
else:
if current['right'] is None:
current['right'] = bst_create_node(name, phone)
return
current = current['right']
def bst_find(name):
current = bst_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 find_min(node):
while node['left'] is not None:
node = node['left']
return node
def bst_delete(name):
global bst_root
def delete_rec(node):
if node is None:
return None
if name < node['name']:
node['left'] = delete_rec(node['left'])
elif name > node['name']:
node['right'] = delete_rec(node['right'])
else:
if node['left'] is None:
return node['right']
if node['right'] is None:
return node['left']
min_node = find_min(node['right'])
node['name'] = min_node['name']
node['phone'] = min_node['phone']
node['right'] = delete_rec(node['right'])
return node
bst_root = delete_rec(bst_root)
def bst_list_all():
result = []
def inorder(node):
if node is None:
return
inorder(node['left'])
result.append((node['name'], node['phone']))
inorder(node['right'])
inorder(bst_root)
return result
def generate_records(n):
records = []
for i in range(1, n+1):
name = f"User_{i:05d}"
phone = f"{random.randint(100,999)}-{random.randint(1000,9999)}"
records.append((name, phone))
return records
def run_experiment():
N = 1000
base = generate_records(N)
shuffled = base.copy()
random.shuffle(shuffled)
sorted_records = sorted(base, key=lambda x: x[0])
structures = [
('LinkedList', ll_insert, ll_find, ll_delete, ll_list_all),
('HashTable', ht_insert, ht_find, ht_delete, ht_list_all),
('BST', bst_insert, bst_find, bst_delete, bst_list_all)
]
all_results = [] # для CSV: список словарей
repeats = 5
for mode_name, data in [('random', shuffled), ('sorted', sorted_records)]:
for struct_name, ins, fnd, dele, lst in structures:
print(f"Testing {struct_name} on {mode_name}...")
for rep in range(repeats):
# сброс структур
global ll_head, buckets, bst_root
ll_head = None
buckets = [None] * BUCKET_COUNT
bst_root = None
# вставка
t0 = time.perf_counter()
for name, phone in data:
ins(name, phone)
t1 = time.perf_counter()
insert_time = t1 - t0
# поиск 110 записей (100 существующих + 10 несуществующих)
existing = [name for name, _ in data]
sample = random.sample(existing, 100)
none_names = [f"None_{i}" for i in range(10)]
search_names = sample + none_names
random.shuffle(search_names)
t0 = time.perf_counter()
for name in search_names:
fnd(name)
t1 = time.perf_counter()
find_time = t1 - t0
# удаление 10 записей
to_delete = random.sample(existing, 10)
t0 = time.perf_counter()
for name in to_delete:
dele(name)
t1 = time.perf_counter()
delete_time = t1 - t0
all_results.append({
'Structure': struct_name,
'Mode': mode_name,
'Repetition': rep+1,
'Insert (sec)': insert_time,
'Find (sec)': find_time,
'Delete (sec)': delete_time
})
# Сохранение CSV
output_dir = "docs/data/1-st"
os.makedirs(output_dir, exist_ok=True)
csv_path = os.path.join(output_dir, "experiment_results.csv")
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
fieldnames = ['Structure', 'Mode', 'Repetition', 'Insert (sec)', 'Find (sec)', 'Delete (sec)']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(all_results)
print(f"\nРезультаты сохранены в {csv_path}")
avg_data = {}
for r in all_results:
key = (r['Structure'], r['Mode'])
if key not in avg_data:
avg_data[key] = {'Insert': [], 'Find': [], 'Delete': []}
avg_data[key]['Insert'].append(r['Insert (sec)'])
avg_data[key]['Find'].append(r['Find (sec)'])
avg_data[key]['Delete'].append(r['Delete (sec)'])
structures_list = ['LinkedList', 'HashTable', 'BST']
modes_list = ['random', 'sorted']
insert_vals = {mode: [] for mode in modes_list}
find_vals = {mode: [] for mode in modes_list}
delete_vals = {mode: [] for mode in modes_list}
for mode in modes_list:
for struct in structures_list:
key = (struct, mode)
if key in avg_data:
insert_avg = sum(avg_data[key]['Insert']) / len(avg_data[key]['Insert'])
find_avg = sum(avg_data[key]['Find']) / len(avg_data[key]['Find'])
delete_avg = sum(avg_data[key]['Delete']) / len(avg_data[key]['Delete'])
else:
insert_avg = find_avg = delete_avg = 0
insert_vals[mode].append(insert_avg)
find_vals[mode].append(find_avg)
delete_vals[mode].append(delete_avg)
# Рисуем три столбчатые диаграммы
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
x = np.arange(len(structures_list))
width = 0.35
for ax, op_data, op_label, ylabel in zip(
axes,
[insert_vals, find_vals, delete_vals],
['Insert', 'Find', 'Delete'],
['Время вставки (с)', 'Время поиска (с)', 'Время удаления (с)']
):
random_vals = op_data['random']
sorted_vals = op_data['sorted']
ax.bar(x - width/2, random_vals, width, label='Случайный порядок', color='skyblue')
ax.bar(x + width/2, sorted_vals, width, label='Отсортированный порядок', color='salmon')
ax.set_xticks(x)
ax.set_xticklabels(structures_list)
ax.set_ylabel(ylabel)
ax.set_title(op_label)
ax.legend()
plt.tight_layout()
png_path = os.path.join(output_dir, "performance_comparison.png")
plt.savefig(png_path, dpi=150)
print(f"График сохранён в {png_path}")
plt.show()
# Вывод средних значений в консоль (для отчёта)
print("\nСредние значения (сек):")
print("Структура\tРежим\tВставка\tПоиск\tУдаление")
for (struct, mode), vals in avg_data.items():
ins_avg = sum(vals['Insert'])/len(vals['Insert'])
find_avg = sum(vals['Find'])/len(vals['Find'])
del_avg = sum(vals['Delete'])/len(vals['Delete'])
print(f"{struct}\t{mode}\t{ins_avg:.6f}\t{find_avg:.6f}\t{del_avg:.6f}")
if __name__ == '__main__':
run_experiment()