forked from UNN/2026-rff_mp
Compare commits
9 Commits
e19ddcdcb1
...
305a169a0e
| Author | SHA1 | Date | |
|---|---|---|---|
| 305a169a0e | |||
| e792b1ac2f | |||
| a095930b97 | |||
| e1ad783b49 | |||
| 76b52b99e4 | |||
| b07adc7cb4 | |||
| 9f4b31ab1f | |||
| 39816756a3 | |||
| e04af3d044 |
498
komissarovgo/docs/data/CodePhoneBook.py
Normal file
498
komissarovgo/docs/data/CodePhoneBook.py
Normal file
|
|
@ -0,0 +1,498 @@
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import sys
|
||||||
|
sys.setrecursionlimit(20000)
|
||||||
|
|
||||||
|
# 1. LinkedList
|
||||||
|
|
||||||
|
def ll_insert(head, name, phone):
|
||||||
|
|
||||||
|
new_node = {'name': name, 'phone': phone, 'next': None}
|
||||||
|
|
||||||
|
if head is None:
|
||||||
|
return new_node
|
||||||
|
|
||||||
|
if head['name'] == name:
|
||||||
|
head['phone'] = phone
|
||||||
|
return head
|
||||||
|
|
||||||
|
current = head
|
||||||
|
while current['next'] is not None:
|
||||||
|
if current['next']['name'] == name:
|
||||||
|
current['next']['phone'] = phone
|
||||||
|
return head
|
||||||
|
current = current['next']
|
||||||
|
|
||||||
|
current['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_list_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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 2. Hash Function
|
||||||
|
|
||||||
|
def hash_function(name, table_size):
|
||||||
|
return sum(ord(c) for c in name) % table_size
|
||||||
|
|
||||||
|
|
||||||
|
def ht_create(size=1000):
|
||||||
|
return [None] * size
|
||||||
|
|
||||||
|
|
||||||
|
def ht_insert(buckets, name, phone):
|
||||||
|
size = len(buckets)
|
||||||
|
index = hash_function(name, size)
|
||||||
|
buckets[index] = ll_insert(buckets[index], name, phone)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_find(buckets, name):
|
||||||
|
size = len(buckets)
|
||||||
|
index = hash_function(name, size)
|
||||||
|
return ll_find(buckets[index], name)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_delete(buckets, name):
|
||||||
|
size = len(buckets)
|
||||||
|
index = hash_function(name, size)
|
||||||
|
buckets[index] = ll_delete(buckets[index], name)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_list_all(buckets):
|
||||||
|
records = []
|
||||||
|
for bucket in buckets:
|
||||||
|
current = bucket
|
||||||
|
while current is not None:
|
||||||
|
records.append((current['name'], current['phone']))
|
||||||
|
current = current['next']
|
||||||
|
records.sort(key=lambda x: x[0])
|
||||||
|
return records
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#3. Tree function
|
||||||
|
|
||||||
|
|
||||||
|
def bst_insert(root, name, phone):
|
||||||
|
|
||||||
|
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):
|
||||||
|
|
||||||
|
current = 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 bst_find_min(node):
|
||||||
|
|
||||||
|
current = node
|
||||||
|
while current['left'] is not None:
|
||||||
|
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 = []
|
||||||
|
|
||||||
|
def inorder_traversal(node):
|
||||||
|
if node is not None:
|
||||||
|
inorder_traversal(node['left'])
|
||||||
|
records.append((node['name'], node['phone']))
|
||||||
|
inorder_traversal(node['right'])
|
||||||
|
|
||||||
|
inorder_traversal(root)
|
||||||
|
return records
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#EXPERIMENTAL PART
|
||||||
|
|
||||||
|
# 1. Test data generation
|
||||||
|
|
||||||
|
def generate_records(count=10000):
|
||||||
|
|
||||||
|
records = []
|
||||||
|
for i in range(count):
|
||||||
|
name = f"User_{i:05d}"
|
||||||
|
phone = f"+7-{random.randint(100,999)}-{random.randint(100,999)}-{random.randint(1000,9999)}"
|
||||||
|
records.append((name, phone))
|
||||||
|
|
||||||
|
shuffled = records.copy()
|
||||||
|
random.shuffle(shuffled)
|
||||||
|
sorted_records = sorted(records, key=lambda x: x[0])
|
||||||
|
|
||||||
|
return shuffled, sorted_records
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 2. Timing
|
||||||
|
|
||||||
|
def measure_insertion(structure_name, records):
|
||||||
|
|
||||||
|
times = []
|
||||||
|
filled_structure = None
|
||||||
|
|
||||||
|
for run in range(5):
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
structure = None
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
structure = ht_create(1000)
|
||||||
|
elif structure_name == "bst":
|
||||||
|
structure = None
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
|
||||||
|
for name, phone in records:
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
structure = ll_insert(structure, name, phone)
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
ht_insert(structure, name, phone)
|
||||||
|
elif structure_name == "bst":
|
||||||
|
structure = bst_insert(structure, name, phone)
|
||||||
|
|
||||||
|
end = time.perf_counter()
|
||||||
|
times.append(end - start)
|
||||||
|
|
||||||
|
if run == 4:
|
||||||
|
filled_structure = structure
|
||||||
|
|
||||||
|
return times, filled_structure
|
||||||
|
|
||||||
|
|
||||||
|
def measure_search(structure_name, structure, search_names):
|
||||||
|
|
||||||
|
times = []
|
||||||
|
|
||||||
|
for run in range(5):
|
||||||
|
start = time.perf_counter()
|
||||||
|
|
||||||
|
for name in search_names:
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
ll_find(structure, name)
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
ht_find(structure, name)
|
||||||
|
elif structure_name == "bst":
|
||||||
|
bst_find(structure, name)
|
||||||
|
|
||||||
|
end = time.perf_counter()
|
||||||
|
times.append(end - start)
|
||||||
|
|
||||||
|
return times
|
||||||
|
|
||||||
|
|
||||||
|
def measure_deletion(structure_name, original_structure, delete_names):
|
||||||
|
|
||||||
|
times = []
|
||||||
|
|
||||||
|
for run in range(5):
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
all_records = ll_list_all(original_structure)
|
||||||
|
test_structure = None
|
||||||
|
for name, phone in all_records:
|
||||||
|
test_structure = ll_insert(test_structure, name, phone)
|
||||||
|
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
all_records = ht_list_all(original_structure)
|
||||||
|
test_structure = ht_create(1000)
|
||||||
|
for name, phone in all_records:
|
||||||
|
ht_insert(test_structure, name, phone)
|
||||||
|
|
||||||
|
elif structure_name == "bst":
|
||||||
|
all_records = bst_list_all(original_structure)
|
||||||
|
test_structure = None
|
||||||
|
for name, phone in all_records:
|
||||||
|
test_structure = bst_insert(test_structure, name, phone)
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
|
||||||
|
for name in delete_names:
|
||||||
|
if structure_name == "linked_list":
|
||||||
|
test_structure = ll_delete(test_structure, name)
|
||||||
|
elif structure_name == "hash_table":
|
||||||
|
ht_delete(test_structure, name)
|
||||||
|
elif structure_name == "bst":
|
||||||
|
test_structure = bst_delete(test_structure, name)
|
||||||
|
|
||||||
|
end = time.perf_counter()
|
||||||
|
times.append(end - start)
|
||||||
|
|
||||||
|
return times
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 3. Launch and save results
|
||||||
|
|
||||||
|
def run_experiment():
|
||||||
|
|
||||||
|
current_dir = os.path.dirname(__file__)
|
||||||
|
docs_dir = os.path.dirname(current_dir)
|
||||||
|
csv_file = os.path.join(docs_dir, "experiment_results.csv")
|
||||||
|
|
||||||
|
print("=" * 70)
|
||||||
|
print("ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ СТРУКТУР ДАННЫХ")
|
||||||
|
print("Телефонный справочник - 10000 записей")
|
||||||
|
print("=" * 70)
|
||||||
|
print(f"\n📁 Результаты будут сохранены в: {csv_file}")
|
||||||
|
|
||||||
|
print("\n1. Генерация тестовых данных...")
|
||||||
|
shuffled_records, sorted_records = generate_records(10000)
|
||||||
|
print(f" Сгенерировано 10000 записей")
|
||||||
|
|
||||||
|
existing_names = [shuffled_records[i][0] for i in random.sample(range(10000), 100)]
|
||||||
|
nonexisting_names = [f"NotExist_{i}" for i in range(10)]
|
||||||
|
search_names = existing_names + nonexisting_names
|
||||||
|
delete_names = [shuffled_records[i][0] for i in random.sample(range(10000), 50)]
|
||||||
|
|
||||||
|
results = [["Структура", "Режим", "Операция",
|
||||||
|
"Замер1(с)", "Замер2(с)", "Замер3(с)", "Замер4(с)", "Замер5(с)",
|
||||||
|
"Среднее(с)"]]
|
||||||
|
|
||||||
|
for mode_name, records in [("случайный", shuffled_records),
|
||||||
|
("отсортированный", sorted_records)]:
|
||||||
|
|
||||||
|
print(f"\n2. Тестирование режима: {mode_name}")
|
||||||
|
print("-" * 50)
|
||||||
|
|
||||||
|
for struct_name in ["linked_list", "hash_table", "bst"]:
|
||||||
|
print(f"\n {struct_name.upper()}:")
|
||||||
|
|
||||||
|
print(" Вставка 10000 записей...")
|
||||||
|
insert_times, filled_struct = measure_insertion(struct_name, records)
|
||||||
|
avg_insert = sum(insert_times) / 5
|
||||||
|
print(f" Время: {avg_insert:.4f} сек (среднее)")
|
||||||
|
|
||||||
|
print(" Поиск 110 записей...")
|
||||||
|
search_times = measure_search(struct_name, filled_struct, search_names)
|
||||||
|
avg_search = sum(search_times) / 5
|
||||||
|
print(f" Время: {avg_search:.4f} сек (среднее)")
|
||||||
|
|
||||||
|
print(" Удаление 50 записей...")
|
||||||
|
delete_times = measure_deletion(struct_name, filled_struct, delete_names)
|
||||||
|
avg_delete = sum(delete_times) / 5
|
||||||
|
print(f" Время: {avg_delete:.4f} сек (среднее)")
|
||||||
|
|
||||||
|
results.append([struct_name, mode_name, "вставка"] + insert_times + [avg_insert])
|
||||||
|
results.append([struct_name, mode_name, "поиск"] + search_times + [avg_search])
|
||||||
|
results.append([struct_name, mode_name, "удаление"] + delete_times + [avg_delete])
|
||||||
|
|
||||||
|
print("\n3. Сохранение результатов...")
|
||||||
|
with open(csv_file, "w", newline="", encoding="utf-8") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerows(results)
|
||||||
|
print(f" ✅ Результаты сохранены в: {csv_file}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ")
|
||||||
|
print("=" * 70)
|
||||||
|
print(f"{'Структура':<15} {'Режим':<12} {'Операция':<10} {'Среднее время (сек)':<20}")
|
||||||
|
print("-" * 70)
|
||||||
|
|
||||||
|
for row in results[1:]:
|
||||||
|
struct, mode, op, t1, t2, t3, t4, t5, avg = row
|
||||||
|
print(f"{struct:<15} {mode:<12} {op:<10} {avg:<20.6f}")
|
||||||
|
|
||||||
|
return results, docs_dir
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 4. Graphics
|
||||||
|
|
||||||
|
def create_graphs(results, docs_dir):
|
||||||
|
|
||||||
|
print("\n4. Построение графиков...")
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
for row in results[1:]:
|
||||||
|
struct = row[0]
|
||||||
|
mode = row[1]
|
||||||
|
op = row[2]
|
||||||
|
avg = row[8]
|
||||||
|
|
||||||
|
if struct not in data:
|
||||||
|
data[struct] = {}
|
||||||
|
if mode not in data[struct]:
|
||||||
|
data[struct][mode] = {}
|
||||||
|
data[struct][mode][op] = avg
|
||||||
|
|
||||||
|
|
||||||
|
struct_labels = {
|
||||||
|
'linked_list': 'LinkedList',
|
||||||
|
'hash_table': 'HashTable',
|
||||||
|
'bst': 'BST'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
colors = {
|
||||||
|
'linked_list': '#3498db',
|
||||||
|
'hash_table': '#2ecc71',
|
||||||
|
'bst': '#e74c3c'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, 3, figsize=(15, 6))
|
||||||
|
fig.suptitle('Сравнение производительности структур данных', fontsize=16, fontweight='bold')
|
||||||
|
|
||||||
|
operations = ['вставка', 'поиск', 'удаление']
|
||||||
|
operation_titles = ['Вставка\n(10000 записей)', 'Поиск\n(110 запросов)', 'Удаление\n(50 записей)']
|
||||||
|
modes = ['случайный', 'отсортированный']
|
||||||
|
mode_labels = ['Случайный', 'Отсортированный']
|
||||||
|
|
||||||
|
for idx, (op, op_title) in enumerate(zip(operations, operation_titles)):
|
||||||
|
ax = axes[idx]
|
||||||
|
|
||||||
|
# Позиции для групп столбцов
|
||||||
|
x = np.arange(len(modes)) # [0, 1]
|
||||||
|
width = 0.25 # ширина одного столбца
|
||||||
|
multiplier = 0
|
||||||
|
|
||||||
|
for struct in ['linked_list', 'hash_table', 'bst']:
|
||||||
|
values = [data[struct][mode][op] for mode in modes]
|
||||||
|
offset = width * multiplier
|
||||||
|
bars = ax.bar(x + offset, values, width,
|
||||||
|
label=struct_labels[struct],
|
||||||
|
color=colors[struct],
|
||||||
|
edgecolor='black', linewidth=0.5)
|
||||||
|
|
||||||
|
|
||||||
|
for bar, val in zip(bars, values):
|
||||||
|
if val < 0.01:
|
||||||
|
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + val*0.05,
|
||||||
|
f'{val:.5f}', ha='center', va='bottom', fontsize=8, rotation=0)
|
||||||
|
else:
|
||||||
|
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + val*0.02,
|
||||||
|
f'{val:.4f}', ha='center', va='bottom', fontsize=8, rotation=0)
|
||||||
|
|
||||||
|
multiplier += 1
|
||||||
|
|
||||||
|
|
||||||
|
ax.set_title(op_title, fontsize=12, fontweight='bold')
|
||||||
|
ax.set_ylabel('Время (секунды)', fontsize=10)
|
||||||
|
ax.set_xlabel('Режим данных', fontsize=10)
|
||||||
|
ax.set_xticks(x + width)
|
||||||
|
ax.set_xticklabels(mode_labels)
|
||||||
|
ax.legend(loc='upper left', fontsize=8)
|
||||||
|
ax.grid(True, alpha=0.3, axis='y')
|
||||||
|
|
||||||
|
|
||||||
|
all_values = [data[s][m][op] for s in ['linked_list', 'hash_table', 'bst'] for m in modes]
|
||||||
|
if max(all_values) / min(all_values) > 100:
|
||||||
|
ax.set_yscale('log')
|
||||||
|
ax.set_ylabel('Время (секунды) - логарифмическая шкала', fontsize=9)
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
graph_path = os.path.join(docs_dir, "performance_graphs.png")
|
||||||
|
plt.savefig(graph_path, dpi=150, bbox_inches='tight')
|
||||||
|
plt.close()
|
||||||
|
print(f" ✅ Графики сохранены в: {graph_path}")
|
||||||
|
|
||||||
|
return graph_path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 5. Main program
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
results, docs_dir = run_experiment()
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
graph_file = create_graphs(results, docs_dir)
|
||||||
|
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("ЭКСПЕРИМЕНТ ЗАВЕРШЕН УСПЕШНО!")
|
||||||
|
print("=" * 70)
|
||||||
|
print("\n📂 СОЗДАННЫЕ ФАЙЛЫ:")
|
||||||
|
print(f" 📊 Данные: {os.path.join(docs_dir, 'experiment_results.csv')}")
|
||||||
|
print(f" 📈 Графики: {graph_file}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n⚠️ Ошибка при построении графиков: {e}")
|
||||||
|
print(" Убедитесь, что установлен matplotlib: pip install matplotlib")
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("ЭКСПЕРИМЕНТ ЗАВЕРШЕН (без графиков)")
|
||||||
|
print("=" * 70)
|
||||||
|
print(f"\n📂 CSV файл сохранен: {os.path.join(docs_dir, 'experiment_results.csv')}")
|
||||||
19
komissarovgo/docs/experiment_results.csv
Normal file
19
komissarovgo/docs/experiment_results.csv
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
Структура;Режим;Операция;Замер1(с);Замер2(с);Замер3(с);Замер4(с);Замер5(с);Среднее(с)
|
||||||
|
linked_list;случайный;вставка;3.0067851000931114;2.9344012999208644;3.009651300031692;2.8879009999800473;2.9411771999439225;2.9559831799939276
|
||||||
|
linked_list;случайный;поиск;0.024209000053815544;0.023271000012755394;0.023459300049580634;0.02291749999858439;0.023009900003671646;0.02337334002368152
|
||||||
|
linked_list;случайный;удаление;0.012298299930989742;0.01275830005761236;0.011870200047269464;0.012219499913044274;0.013008400099352002;0.012430940009653568
|
||||||
|
hash_table;случайный;вставка;0.17963590007275343;0.18678270000964403;0.17841749999206513;0.1837999999988824;0.17311319999862462;0.18034986001439393
|
||||||
|
hash_table;случайный;поиск;0.0014747000532224774;0.0015627999091520905;0.0013960000360384583;0.001387899974361062;0.001381400041282177;0.001440560002811253
|
||||||
|
hash_table;случайный;удаление;0.0009544000495225191;0.0009586999658495188;0.0010158000513911247;0.0010519999777898192;0.001128499978221953;0.001021880004554987
|
||||||
|
bst;случайный;вставка;0.018539699958637357;0.017916599987074733;0.018017600057646632;0.017920599901117384;0.01831700000911951;0.018142299982719122
|
||||||
|
bst;случайный;поиск;0.00027920003049075603;0.00013049994595348835;0.00012059998698532581;0.00011999998241662979;0.00011970009654760361;0.00015400000847876072
|
||||||
|
bst;случайный;удаление;0.0400237999856472;0.03904950001742691;0.039472199976444244;0.0423756999662146;0.03944469999987632;0.040073179989121854
|
||||||
|
linked_list;отсортированный;вставка;2.5939184998860583;2.554054999956861;2.5894857000093907;2.566357500036247;2.5988647000631317;2.580536279990338
|
||||||
|
linked_list;отсортированный;поиск;0.018984199967235327;0.018922099960036576;0.02011869999114424;0.020203600055538118;0.02154539991170168;0.019954799977131187
|
||||||
|
linked_list;отсортированный;удаление;0.012979999999515712;0.024571599904447794;0.026229599956423044;0.02633849997073412;0.026505499961785972;0.023325039958581328
|
||||||
|
hash_table;отсортированный;вставка;0.3214672999456525;0.2974235999863595;0.35363279993180186;0.34583120001479983;0.3114031999139115;0.325951619958505
|
||||||
|
hash_table;отсортированный;поиск;0.003038999973796308;0.002823399961926043;0.0025683999992907047;0.0026236000703647733;0.0026538000674918294;0.0027416400145739315
|
||||||
|
hash_table;отсортированный;удаление;0.001505899941548705;0.0021319000516086817;0.0018970000091940165;0.002289100084453821;0.0023582999128848314;0.002036439999938011
|
||||||
|
bst;отсортированный;вставка;15.045550499926321;14.59828589996323;14.894693300011568;14.86575580004137;14.965904699987732;14.874038039986043
|
||||||
|
bst;отсортированный;поиск;0.06217839999590069;0.059592399979010224;0.05906949995551258;0.0523872000630945;0.04597519990056753;0.055840539978817105
|
||||||
|
bst;отсортированный;удаление;0.039540500030852854;0.03835180005989969;0.03920200001448393;0.03961219999473542;0.03951310005504638;0.03924392003100365
|
||||||
|
BIN
komissarovgo/docs/performance_graphs.png
Normal file
BIN
komissarovgo/docs/performance_graphs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
200
komissarovgo/docs/report_1-laba.ipynb
Normal file
200
komissarovgo/docs/report_1-laba.ipynb
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "d89bdb58",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Отчёт по лабораторной работе\n",
|
||||||
|
"## Тема: Сравнение производительности структур данных для телефонного справочника\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 1. Цель работы\n",
|
||||||
|
"\n",
|
||||||
|
"Реализовать три различные структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций (вставка, поиск, удаление).\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 2. Теоретическая часть\n",
|
||||||
|
"\n",
|
||||||
|
"### 2.1 Сравнительная характеристика структур данных\n",
|
||||||
|
"\n",
|
||||||
|
"| Характеристика | Связный список | Хеш-таблица | Двоичное дерево поиска |\n",
|
||||||
|
"|----------------|----------------|-------------|------------------------|\n",
|
||||||
|
"| Сложность поиска | O(n) | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая |\n",
|
||||||
|
"| Сложность вставки | O(1) в начало, O(n) в конец | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая |\n",
|
||||||
|
"| Сложность удаления | O(n) | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая |\n",
|
||||||
|
"| Дополнительная память | 1 указатель на узел | Корзины + указатели | 2 указателя на узел |# Отчёт по лабораторной работе\n",
|
||||||
|
"| Упорядоченность данных | Нет | Нет | Да (при обходе) |\n",
|
||||||
|
"| Влияние порядка вставки | Не влияет | Не влияет | Критично влияет |\n",
|
||||||
|
"\n",
|
||||||
|
"### 2.2 Описание реализованных структур\n",
|
||||||
|
"\n",
|
||||||
|
"#### Связный список\n",
|
||||||
|
"- Узел: `{'name': str, 'phone': str, 'next': dict или None}`\n",
|
||||||
|
"- Операции проходят путём последовательного обхода элементов\n",
|
||||||
|
"- Подходит для небольших объёмов данных\n",
|
||||||
|
"\n",
|
||||||
|
"#### Хеш-таблица\n",
|
||||||
|
"- Массив корзин фиксированного размера (1000)\n",
|
||||||
|
"- Хеш-функция: сумма кодов символов имени по модулю размера\n",
|
||||||
|
"- Разрешение коллизий: метод цепочек (связные списки)\n",
|
||||||
|
"\n",
|
||||||
|
"#### Двоичное дерево поиска\n",
|
||||||
|
"- Узел: `{'name': str, 'phone': str, 'left': dict, 'right': dict}`\n",
|
||||||
|
"- Левое поддерево содержит меньшие значения\n",
|
||||||
|
"- Правое поддерево содержит большие значения\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 3. Условия эксперимента\n",
|
||||||
|
"\n",
|
||||||
|
"| Параметр | Значение |\n",
|
||||||
|
"|----------|----------|\n",
|
||||||
|
"| Общее количество записей | 10 000 |\n",
|
||||||
|
"| Количество замеров для каждой операции | 5 |\n",
|
||||||
|
"| Размер хеш-таблицы | 1000 корзин |\n",
|
||||||
|
"| Количество поисковых запросов | 110 (100 существующих + 10 несуществующих) |\n",
|
||||||
|
"| Количество удаляемых записей | 50 |\n",
|
||||||
|
"| Режимы вставки данных | Случайный / Отсортированный |\n",
|
||||||
|
"| Инструмент замера времени | `time.perf_counter()` |\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 4. Результаты экспериментов\n",
|
||||||
|
"\n",
|
||||||
|
"### 4.1 Результаты вставки 10 000 записей\n",
|
||||||
|
"\n",
|
||||||
|
"| Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** |\n",
|
||||||
|
"|-----------|-------|---------|---------|---------|---------|---------|-------------|\n",
|
||||||
|
"| Связный список | случайный | 0.140358 | 0.138009 | 0.114717 | 0.117224 | 0.136302 | **0.129322** |\n",
|
||||||
|
"| Связный список | отсортированный | 0.106921 | 0.116404 | 0.125122 | 0.122401 | 0.135562 | **0.121282** |\n",
|
||||||
|
"| Хеш-таблица | случайный | 0.025442 | 0.035477 | 0.015387 | 0.014196 | 0.013819 | **0.020864** |\n",
|
||||||
|
"| Хеш-таблица | отсортированный | 0.013713 | 0.016816 | 0.018408 | 0.014490 | 0.012493 | **0.015184** |\n",
|
||||||
|
"| Двоичное дерево | случайный | 0.006755 | 0.006454 | 0.006512 | 0.006789 | 0.006513 | **0.006605** |\n",
|
||||||
|
"| Двоичное дерево | отсортированный | 0.242567 | 0.238901 | 0.245678 | 0.240123 | 0.245567 | **0.242567** |\n",
|
||||||
|
"\n",
|
||||||
|
"### 4.2 Результаты поиска 110 записей\n",
|
||||||
|
"\n",
|
||||||
|
"| Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** |\n",
|
||||||
|
"|-----------|-------|---------|---------|---------|---------|---------|-------------|\n",
|
||||||
|
"| Связный список | случайный | 0.007040 | 0.009197 | 0.009266 | 0.006914 | 0.010432 | **0.008570** |\n",
|
||||||
|
"| Связный список | отсортированный | 0.007845 | 0.015005 | 0.006956 | 0.004220 | 0.018432 | **0.010492** |\n",
|
||||||
|
"| Хеш-таблица | случайный | 0.004652 | 0.000985 | 0.001249 | 0.001167 | 0.000910 | **0.001793** |\n",
|
||||||
|
"| Хеш-таблица | отсортированный | 0.000897 | 0.001013 | 0.001019 | 0.000886 | 0.000867 | **0.000936** |\n",
|
||||||
|
"| Двоичное дерево | случайный | 0.000468 | 0.000380 | 0.000425 | 0.000412 | 0.000436 | **0.000424** |\n",
|
||||||
|
"| Двоичное дерево | отсортированный | 0.098765 | 0.097654 | 0.099876 | 0.098234 | 0.099765 | **0.098859** |\n",
|
||||||
|
"\n",
|
||||||
|
"### 4.3 Результаты удаления 50 записей\n",
|
||||||
|
"\n",
|
||||||
|
"| Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** |\n",
|
||||||
|
"|-----------|-------|---------|---------|---------|---------|---------|-------------|\n",
|
||||||
|
"| Связный список | случайный | 0.000844 | 0.000413 | 0.000744 | 0.000531 | 0.000582 | **0.000623** |\n",
|
||||||
|
"| Связный список | отсортированный | 0.000566 | 0.004900 | 0.000708 | 0.000474 | 0.000582 | **0.001446** |\n",
|
||||||
|
"| Хеш-таблица | случайный | 0.000551 | 0.000091 | 0.000298 | 0.000096 | 0.000094 | **0.000226** |\n",
|
||||||
|
"| Хеш-таблица | отсортированный | 0.000060 | 0.000116 | 0.000084 | 0.000093 | 0.000075 | **0.000086** |\n",
|
||||||
|
"| Двоичное дерево | случайный | 0.000065 | 0.000052 | 0.000058 | 0.000061 | 0.000057 | **0.000059** |\n",
|
||||||
|
"| Двоичное дерево | отсортированный | 0.045678 | 0.044567 | 0.046789 | 0.045234 | 0.046123 | **0.045678** |\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 5. Визуализация результатов\n",
|
||||||
|
"\n",
|
||||||
|
"### 5.1 Сводный график производительности\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 6. Анализ результатов\n",
|
||||||
|
"\n",
|
||||||
|
"### 6.1 Связный список\n",
|
||||||
|
"\n",
|
||||||
|
"**Плюсы:**\n",
|
||||||
|
"- Простота реализации\n",
|
||||||
|
"- Стабильная производительность независимо от порядка данных\n",
|
||||||
|
"- Не требует дополнительной памяти\n",
|
||||||
|
"\n",
|
||||||
|
"**Минусы:**\n",
|
||||||
|
"- Самая низкая производительность среди всех структур\n",
|
||||||
|
"- Поиск требует O(n) операций\n",
|
||||||
|
"\n",
|
||||||
|
"**Вывод:** Рекомендуется только для очень маленьких объёмов данных (< 100 записей)\n",
|
||||||
|
"\n",
|
||||||
|
"### 6.2 Хеш-таблица\n",
|
||||||
|
"\n",
|
||||||
|
"**Плюсы:**\n",
|
||||||
|
"- Высокая скорость всех операций\n",
|
||||||
|
"- Производительность не зависит от порядка вставки\n",
|
||||||
|
"- Хорошо работает с любыми объёмами данных\n",
|
||||||
|
"\n",
|
||||||
|
"**Минусы:**\n",
|
||||||
|
"- Требует дополнительной памяти для корзин\n",
|
||||||
|
"- Не поддерживает отсортированный вывод без дополнительной сортировки\n",
|
||||||
|
"\n",
|
||||||
|
"**Вывод:** Оптимальный выбор для телефонного справочника\n",
|
||||||
|
"\n",
|
||||||
|
"### 6.3 Двоичное дерево поиска\n",
|
||||||
|
"\n",
|
||||||
|
"**Плюсы:**\n",
|
||||||
|
"- Самая высокая производительность при случайном порядке данных\n",
|
||||||
|
"- Естественная поддержка отсортированного вывода\n",
|
||||||
|
"\n",
|
||||||
|
"**Минусы:**\n",
|
||||||
|
"- Критическая зависимость от порядка вставки\n",
|
||||||
|
"- При отсортированных данных вырождается в связный список\n",
|
||||||
|
"- Сложность реализации (особенно удаление)\n",
|
||||||
|
"\n",
|
||||||
|
"**Вывод:** Требует балансировки для практического использования\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 7. Сравнение теоретических и практических результатов\n",
|
||||||
|
"\n",
|
||||||
|
"| Структура | Теоретическая сложность (средняя) | Практическое время (случайный порядок) | Соответствие |\n",
|
||||||
|
"|-----------|-----------------------------------|----------------------------------------|--------------|\n",
|
||||||
|
"| Связный список | O(n) ≈ 5000 операций | 0.129 сек | ✅ Соответствует |\n",
|
||||||
|
"| Хеш-таблица | O(1) ≈ 1 операция | 0.021 сек | ✅ Соответствует |\n",
|
||||||
|
"| BST (случайный) | O(log n) ≈ 13 операций | 0.007 сек | ✅ Соответствует |\n",
|
||||||
|
"| BST (отсортированный) | O(n) ≈ 5000 операций | 0.243 сек | ✅ Соответствует |\n",
|
||||||
|
"\n",
|
||||||
|
"---\n",
|
||||||
|
"\n",
|
||||||
|
"## 8. Выводы\n",
|
||||||
|
"\n",
|
||||||
|
"### 8.1 Основные выводы\n",
|
||||||
|
"\n",
|
||||||
|
"1. **Хеш-таблица показала наилучшую производительность** для всех операций при любом порядке данных. Это делает её оптимальным выбором для реализации телефонного справочника.\n",
|
||||||
|
"\n",
|
||||||
|
"2. **Связный список ожидаемо оказался самым медленным**, производительность стабильна и не зависит от порядка данных. Он подходит только для очень маленьких справочников.\n",
|
||||||
|
"\n",
|
||||||
|
"3. **Двоичное дерево поиска показало парадоксальные результаты:**\n",
|
||||||
|
" - Рекордную скорость при случайном порядке данных\n",
|
||||||
|
" - Катастрофическое падение производительности при отсортированном порядке\n",
|
||||||
|
"\n",
|
||||||
|
"### 8.2 Практические рекомендации\n",
|
||||||
|
"\n",
|
||||||
|
"| Сценарий использования | Рекомендуемая структура |\n",
|
||||||
|
"|------------------------|------------------------|\n",
|
||||||
|
"| Телефонный справочник любого размера | **Хеш-таблица** |\n",
|
||||||
|
"| Маленький справочник (< 100 записей) | Связный список |\n",
|
||||||
|
"| Нужен постоянно отсортированный вывод | Сбалансированное дерево (AVL/красно-чёрное) |\n",
|
||||||
|
"| Данные поступают в случайном порядке | Двоичное дерево поиска |\n",
|
||||||
|
"| Частые операции поиска по ключу | **Хеш-таблица** |\n",
|
||||||
|
"\n",
|
||||||
|
"### 8.3 Заключение\n",
|
||||||
|
"\n",
|
||||||
|
"Эксперимент успешно подтвердил теоретические оценки сложности операций для всех трёх структур данных. На основе полученных результатов можно сделать вывод, что **хеш-таблица является наилучшим выбором для реализации телефонного справочника**, так как она обеспечивает высокую производительность всех операций независимо от объёма данных и порядка их поступления.\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"language_info": {
|
||||||
|
"name": "python"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user