2026-rff_mp/AgapovaDS/docs/data/1-st/phonebook.py

336 lines
10 KiB
Python
Raw Normal View History

2026-09-02 22:45:10 +00:00
import random
2026-09-02 23:03:21 +00:00
import time
import sys
import csv
import os
import matplotlib.pyplot as plt
import numpy as np
sys.setrecursionlimit(10000)
2026-09-02 22:45:10 +00:00
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
2026-09-02 22:49:04 +00:00
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
2026-09-02 23:03:21 +00:00
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']
2026-09-02 22:49:04 +00:00
else:
2026-09-02 23:03:21 +00:00
if current['right'] is None:
current['right'] = bst_create_node(name, phone)
return
current = current['right']
2026-09-02 22:49:04 +00:00
def bst_find(name):
2026-09-02 23:03:21 +00:00
current = bst_root
while current is not None:
if name == current['name']:
return current['phone']
elif name < current['name']:
current = current['left']
2026-09-02 22:49:04 +00:00
else:
2026-09-02 23:03:21 +00:00
current = current['right']
return None
2026-09-02 22:49:04 +00:00
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
2026-09-02 23:03:21 +00:00
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}")
2026-09-02 22:45:10 +00:00
if __name__ == '__main__':
2026-09-02 23:03:21 +00:00
run_experiment()