тест полной программы (Успех)
This commit is contained in:
parent
6c9a56505a
commit
303da5bdc5
19
nikolaevda/experiment_results.csv
Normal file
19
nikolaevda/experiment_results.csv
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Структура;Режим;Операция;Замер1(с);Замер2(с);Замер3(с);Замер4(с);Замер5(с);Среднее(с)
|
||||
linked_list;случайный;вставка;5.503300700000182;5.004083100000571;4.882084700000632;4.881827499999417;4.890130499999941;5.032285300000149
|
||||
linked_list;случайный;поиск;0.05034929999965243;0.04795749999993859;0.046004300000277;0.04418989999976475;0.04287860000022192;0.04627591999997094
|
||||
linked_list;случайный;удаление;0.03702340000018012;0.03709479999997711;0.03829469999982393;0.03841730000021926;0.03741160000026866;0.03764836000009382
|
||||
hash_table;случайный;вставка;0.03724729999976262;0.036700800000289746;0.03645409999990079;0.036475899999459216;0.035486800000398944;0.03647297999996226
|
||||
hash_table;случайный;поиск;0.00042500000017753337;0.00033910000001924345;0.00033829999938461697;0.0003378999999767984;0.00033820000044215703;0.00035570000000006985
|
||||
hash_table;случайный;удаление;0.00018709999949351186;0.00017469999966124306;0.0001795999996829778;0.00018339999951422215;0.00017609999940759735;0.00018017999955191044
|
||||
bst;случайный;вставка;0.04527770000004239;0.042043200000080105;0.04279800000040268;0.038970799999333394;0.03744349999942642;0.041306639999856995
|
||||
bst;случайный;поиск;0.0005276000001686043;0.0004296000006434042;0.00043430000005173497;0.00041569999939383706;0.0004365999993751757;0.00044875999992655125
|
||||
bst;случайный;удаление;0.09453469999971276;0.09244760000001406;0.10106580000046961;0.09086349999961385;0.09310050000021874;0.0944024200000058
|
||||
linked_list;отсортированный;вставка;6.237431600000491;6.1635778999998365;6.183921300000293;6.150560400000359;6.1811854999996285;6.183335340000122
|
||||
linked_list;отсортированный;поиск;0.05593550000048708;0.053237000000081025;0.05208490000040911;0.05022400000052585;0.049882199999956356;0.052272720000291886
|
||||
linked_list;отсортированный;удаление;0.04117889999997715;0.037663099999917904;0.03757399999994959;0.04252919999998994;0.03767599999991944;0.03932423999995081
|
||||
hash_table;отсортированный;вставка;0.04085130000021309;0.03589560000000347;0.03588300000046729;0.035286500000438537;0.03309669999998732;0.03620262000022194
|
||||
hash_table;отсортированный;поиск;0.00044720000005327165;0.00040470000021741726;0.0003451000002314686;0.00034250000044266926;0.0003420000002734014;0.00037630000024364564
|
||||
hash_table;отсортированный;удаление;0.0001863999996203347;0.00017409999964002054;0.00017500000012660166;0.00017459999980928842;0.00017379999917466193;0.00017677999967418144
|
||||
bst;отсортированный;вставка;18.630878699999812;18.350701800000024;18.322985300001164;18.381200199999512;18.44051959999888;18.425257119999877
|
||||
bst;отсортированный;поиск;0.16251639999973122;0.15183020000040415;0.1572033000011288;0.16128759999992326;0.16190610000012384;0.15894872000026225
|
||||
bst;отсортированный;удаление;0.09546029999910388;0.09252720000040426;0.09302340000067488;0.09245310000005702;0.09305589999894437;0.09330397999983689
|
||||
|
BIN
nikolaevda/graphs.png
Normal file
BIN
nikolaevda/graphs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
|
|
@ -1,3 +1,12 @@
|
|||
import random
|
||||
import time
|
||||
import csv
|
||||
import os
|
||||
import traceback
|
||||
import sys
|
||||
|
||||
sys.setrecursionlimit(30000)
|
||||
|
||||
def ll_insert(head, name, phone):
|
||||
|
||||
"""
|
||||
|
|
@ -293,4 +302,348 @@ def bst_list_all(root, result=None):
|
|||
# затем правое поддерево (все большие ключи)
|
||||
bst_list_all(root['right'], result)
|
||||
|
||||
return result
|
||||
return result
|
||||
|
||||
def generate_records(count=10000):
|
||||
"""
|
||||
генерация тестовых данных
|
||||
70% уникальных имён, 30% повторяющихся (для коллизий)
|
||||
"""
|
||||
records = []
|
||||
base_names = ["Алексей", "Борис", "Владимир", "Дмитрий", "Елена",
|
||||
"Иван", "Мария", "Николай", "Ольга", "Павел"]
|
||||
|
||||
for i in range(count):
|
||||
if random.random() < 0.7:
|
||||
name = f"User_{i:05d}"
|
||||
else:
|
||||
name = random.choice(base_names) + f"_{random.randint(1, 100)}"
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 = hash_table(2003)
|
||||
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 = hash_table(2003)
|
||||
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
|
||||
|
||||
print(f"Текущая рабочая директория: {os.getcwd()}")
|
||||
print(f"Путь к файлу: {os.path.abspath(__file__)}")
|
||||
|
||||
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")
|
||||
|
||||
|
||||
|
||||
os.makedirs(docs_dir, exist_ok=True)
|
||||
|
||||
print("ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ СТРУКТУР ДАННЫХ")
|
||||
print("Телефонный справочник - 10000 записей")
|
||||
print(f"\nРезультаты будут сохранены в: {csv_file}")
|
||||
|
||||
# генерация данных
|
||||
print("\n1. Генерация тестовых данных...")
|
||||
shuffled_records, sorted_records = generate_records(10000)
|
||||
print(f"Сгенерировано 10000 записей")
|
||||
print(f"Уникальных имён: {len(set([r[0] for r in shuffled_records]))}")
|
||||
|
||||
# подготовка имён для поиска и удаления
|
||||
random.seed(42)
|
||||
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}")
|
||||
|
||||
|
||||
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 записей (100 существующих + 10 которых нет)...")
|
||||
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])
|
||||
|
||||
# сохранение CSV
|
||||
print("\n3. Сохранение результатов...")
|
||||
try:
|
||||
with open(csv_file, "w", newline="", encoding="utf-8-sig") as f:
|
||||
writer = csv.writer(f, delimiter=';')
|
||||
writer.writerows(results)
|
||||
print(f"Результаты сохранены в: {csv_file}")
|
||||
except Exception as e:
|
||||
print(f"Ошибка сохранения: {e}")
|
||||
|
||||
# вывод табл.
|
||||
print("СВОДНАЯ ТАБЛИЦА РЕЗУЛЬТАТОВ")
|
||||
print(f"{'Структура':<15} {'Режим':<12} {'Операция':<10} {'Среднее время (сек)':<20}")
|
||||
|
||||
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
|
||||
|
||||
def create_report_table(results):
|
||||
"""Создание сводной таблицы"""
|
||||
|
||||
print("СВОДНАЯ ТАБЛИЦА (среднее время в секундах)")
|
||||
|
||||
print(f"{'Структура':<12} {'Режим':<12} {'Вставка':<12} {'Поиск':<12} {'Удаление':<12}")
|
||||
|
||||
summary = {}
|
||||
for row in results[1:]:
|
||||
struct, mode, op, _, _, _, _, _, avg = row
|
||||
key = (struct, mode)
|
||||
if key not in summary:
|
||||
summary[key] = {}
|
||||
summary[key][op] = avg
|
||||
|
||||
names = {'linked_list': 'LinkedList', 'hash_table': 'HashTable', 'bst': 'BST'}
|
||||
for (struct, mode), ops in summary.items():
|
||||
print(f"{names[struct]:<12} {mode:<12} {ops.get('вставка', 0):<12.6f} {ops.get('поиск', 0):<12.6f} {ops.get('удаление', 0):<12.6f}")
|
||||
|
||||
|
||||
def print_analysis():
|
||||
"""вывод краткого анализа"""
|
||||
|
||||
print("АНАЛИЗ РЕЗУЛЬТАТОВ")
|
||||
|
||||
print("""
|
||||
1. Влияние порядка данных на BST:
|
||||
- На случайных данных: быстро O(log n)
|
||||
- На отсортированных: деградация до O(n) (дерево вырождается в список)
|
||||
|
||||
2. Хеш-таблица не чувствительна к порядку:
|
||||
- Хеш-функция случайно распределяет данные по bucket'ам
|
||||
- Порядок вставки не влияет на время операций
|
||||
|
||||
3. Связный список всегда медленен при поиске:
|
||||
- Поиск требует последовательного прохода O(n)
|
||||
- Нет индексов или сортировки для ускорения
|
||||
|
||||
4. Сравнение удаления:
|
||||
- Связный список: O(n) — нужен поиск элемента
|
||||
- Хеш-таблица: O(1) — прямой доступ по индексу
|
||||
- BST: O(log n) в среднем, O(n) на отсортированных
|
||||
|
||||
5. Рекомендация для реальных задач:
|
||||
- Хеш-таблица: частый поиск, словари, кэши
|
||||
- BST (сбалансированный): нужны отсортированные данные
|
||||
- Связный список: маленькие объёмы, очереди/стеки
|
||||
- Для телефонного справочника ЛУЧШЕ: ХЕШ-ТАБЛИЦА
|
||||
""")
|
||||
|
||||
def create_graphs(results):
|
||||
"""Построение столбчатых диаграмм"""
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
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_names = {'linked_list': 'LinkedList', 'hash_table': 'HashTable', 'bst': 'BST'}
|
||||
colors = {'linked_list': '#3498db', 'hash_table': '#2ecc71', 'bst': '#e74c3c'}
|
||||
modes = ['случайный', 'отсортированный']
|
||||
operations = ['вставка', 'поиск', 'удаление']
|
||||
op_titles = ['Вставка (10000 записей)', 'Поиск (110 запросов)', 'Удаление (50 записей)']
|
||||
|
||||
fig, axes = plt.subplots(1, 3, figsize=(14, 5))
|
||||
fig.suptitle('Сравнение производительности структур данных', fontsize=14, fontweight='bold')
|
||||
|
||||
for idx, (op, title) in enumerate(zip(operations, op_titles)):
|
||||
ax = axes[idx]
|
||||
x = np.arange(len(modes))
|
||||
width = 0.25
|
||||
multiplier = 0
|
||||
|
||||
for struct in ['linked_list', 'hash_table', 'bst']:
|
||||
values = [data[struct][mode][op] for mode in modes]
|
||||
bars = ax.bar(x + multiplier * width, values, width,
|
||||
label=struct_names[struct], color=colors[struct])
|
||||
|
||||
for bar, val in zip(bars, values):
|
||||
if val < 0.001:
|
||||
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height(),
|
||||
f'{val:.6f}', ha='center', va='bottom', fontsize=7)
|
||||
elif val < 0.01:
|
||||
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height(),
|
||||
f'{val:.5f}', ha='center', va='bottom', fontsize=7)
|
||||
else:
|
||||
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height(),
|
||||
f'{val:.3f}', ha='center', va='bottom', fontsize=8)
|
||||
multiplier += 1
|
||||
|
||||
ax.set_title(title)
|
||||
ax.set_ylabel('Время (сек)')
|
||||
ax.set_yscale('log')
|
||||
ax.set_ylim(1e-5, 10)
|
||||
ax.set_xticks(x + width)
|
||||
ax.set_xticklabels(['Случайный', 'Отсортированный'])
|
||||
ax.legend()
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
current_dir = os.path.dirname(__file__)
|
||||
docs_dir = os.path.dirname(current_dir)
|
||||
path = os.path.join(docs_dir, 'graphs.png')
|
||||
plt.savefig(path, dpi=150)
|
||||
plt.close()
|
||||
print(f"\nГрафики сохранены: {path}")
|
||||
return path
|
||||
|
||||
if __name__ == "__main__":
|
||||
results = run_experiment()
|
||||
create_report_table(results)
|
||||
create_graphs(results)
|
||||
print_analysis()
|
||||
|
||||
print("ЭКСПЕРИМЕНТ ВЫПОЛНЕН ПОЛНОСТЬЮ!")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user