Загрузить файлы в «SmirnovaVYu/docs/data»

This commit is contained in:
smirnovavyu 2026-09-04 15:02:24 +00:00
parent c07a4807bf
commit 3b08582540
5 changed files with 516 additions and 138 deletions

View File

@ -0,0 +1,94 @@
import time
import numpy as np
from linkedlist import ll_insert, ll_find, ll_delete
from hashtable import ht_create, ht_insert, ht_find, ht_delete
from bst import bst_insert, bst_find, bst_delete
def measure_insert(records, struct_type, params=None): #Замер времени вставки всех записей
start = time.perf_counter()
if struct_type == 'linkedlist':
head = None
for name, phone in records:
head = ll_insert(head, name, phone)
result = head
elif struct_type == 'hashtable':
size = params.get('size', 1000) if params else 1000
buckets = ht_create(size)
for name, phone in records:
ht_insert(buckets, name, phone)
result = buckets
elif struct_type == 'bst':
root = None
for name, phone in records:
root = bst_insert(root, name, phone)
result = root
end = time.perf_counter()
return end - start, result
def measure_find(structure, names_to_find, struct_type): #Замер времени поиска записей
start = time.perf_counter()
for name in names_to_find:
if struct_type == 'linkedlist':
ll_find(structure, name)
elif struct_type == 'hashtable':
ht_find(structure, name)
elif struct_type == 'bst':
bst_find(structure, name)
end = time.perf_counter()
return end - start
def measure_delete(structure, names_to_delete, struct_type): #Замер времени удаления записей
start = time.perf_counter()
for name in names_to_delete:
if struct_type == 'linkedlist':
structure = ll_delete(structure, name)
elif struct_type == 'hashtable':
ht_delete(structure, name)
elif struct_type == 'bst':
structure = bst_delete(structure, name)
end = time.perf_counter()
return end - start, structure
def run_single_experiment(struct_type, mode, data_records, names_to_find, names_to_delete, repeats, params=None): #Запуск одного эксперимента
insert_times = []
find_times = []
delete_times = []
for i in range(repeats):
if struct_type == 'hashtable':
insert_time, structure = measure_insert(data_records, struct_type, params)
else:
insert_time, structure = measure_insert(data_records, struct_type)
insert_times.append(insert_time)
find_time = measure_find(structure, names_to_find, struct_type)
find_times.append(find_time)
delete_time, structure = measure_delete(structure, names_to_delete, struct_type)
delete_times.append(delete_time)
return {
'structure': struct_type,
'mode': mode,
'insert_mean': np.mean(insert_times),
'insert_std': np.std(insert_times),
'insert_all': insert_times,
'find_mean': np.mean(find_times),
'find_std': np.std(find_times),
'find_all': find_times,
'delete_mean': np.mean(delete_times),
'delete_std': np.std(delete_times),
'delete_all': delete_times
}

View File

@ -0,0 +1,30 @@
from linkedlist import ll_insert, ll_find, ll_delete, ll_list_all
def hash_function(name, size):
return sum(ord(c) for c in name) % size
def ht_create(size):
return [None] * size
def ht_insert(buckets, name, phone):
index = hash_function(name, len(buckets))
buckets[index] = ll_insert(buckets[index], name, phone)
def ht_find(buckets, name):
index = hash_function(name, len(buckets))
return ll_find(buckets[index], name)
def ht_delete(buckets, name):
index = hash_function(name, len(buckets))
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

View File

@ -0,0 +1,50 @@
def ll_insert(head, name, phone): #Oбновление записи в связном списке
if head is None:
return {'name': name, 'phone': phone, 'next': None}
current = head
while current is not None:
if current['name'] == name:
current['phone'] = phone
return head
current = current['next']
new_node = {'name': name, 'phone': phone, 'next': None}
current = head
while current['next'] is not None:
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

View File

@ -1,145 +1,52 @@
import os
from builders import TextFileMazeBuilder
from strategies import BFSStrategy, DFSStrategy, AStarStrategy
from solver import MazeSolver
from observers import ConsoleView
from commands import Player
from experiments import run_all_experiments, save_results_to_csv, print_results_table
def create_test_mazes():
os.makedirs("mazes", exist_ok=True)
small = """##########
#S #
# ### ## #
# # #
### # ####
# # #
# ### # #
# # #
# # E#
##########"""
medium = """####################
#S #
# # # # # # # # # #
# #
# # # # # # # # # #
# #
# # # # # # # # # #
# #
# # # # # # # # # #
# E#
####################"""
large = """##############################
#S #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# E#
##############################"""
empty = "S" + " " * 28 + "E"
no_exit = """#######
#S #
# ### #
# # #
#######"""
with open("mazes/small.txt", "w") as f:
f.write(small)
with open("mazes/medium.txt", "w") as f:
f.write(medium)
with open("mazes/large.txt", "w") as f:
f.write(large)
with open("mazes/empty.txt", "w") as f:
f.write(empty)
with open("mazes/no_exit.txt", "w") as f:
f.write(no_exit)
def demo_maze_solver():
print("\n" + "=" * 60)
print("ДЕМОНСТРАЦИЯ РАБОТЫ MAZE SOLVER")
print("=" * 60)
builder = TextFileMazeBuilder()
view = ConsoleView()
maze = builder.build_from_file("mazes/small.txt")
view.update("maze_loaded", {"maze": maze})
strategies = [
("BFS", BFSStrategy(), "BFS"),
("DFS", DFSStrategy(), "DFSs"),
("A*", AStarStrategy(), "A*")
]
for name, strategy, description in strategies:
solver = MazeSolver(maze, strategy)
view.update("search_start", {"algorithm": description})
path, stats = solver.solve()
if stats.path_found:
view.update("path_found", {"maze": maze, "path": path, "stats": stats})
else:
view.update("no_path", {"stats": stats})
def demo_player_controls():
print("\n" + "=" * 60)
print("Command + Observer")
print("=" * 60)
builder = TextFileMazeBuilder()
view = ConsoleView()
maze = builder.build_from_file("mazes/small.txt")
player = Player(maze.start)
view.update("maze_loaded", {"maze": maze})
view.render(maze, player_position=player.current_cell)
def run_experiments():
print("\n" + "=" * 60)
print("ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ АЛГОРИТМОВ")
print("=" * 60)
maze_files = [
"mazes/small.txt",
"mazes/medium.txt",
"mazes/large.txt",
"mazes/empty.txt",
"mazes/no_exit.txt"
]
results = run_all_experiments(maze_files, repeats=5)
save_results_to_csv(results)
print_results_table(results)
from config import N, REPEATS, HASH_TABLE_SIZE
from data_generator import generate_test_data, get_names_for_operations
from experiment import run_single_experiment
from results_analyzer import save_to_csv, plot_results, print_analysis, save_report_md
def main():
print("Объектно-ориентированная реализация с паттернами")
print("Паттерны: Builder, Strategy, Observer, Command")
print(f"Количество записей: {N}")
print(f"Количество повторов: {REPEATS}")
print(f"Размер хеш-таблицы: {HASH_TABLE_SIZE}")
print()
records, records_shuffled, records_sorted = generate_test_data(N)
names_to_find, names_to_delete = get_names_for_operations(records)
experiments = [
('linkedlist', 'случайный', records_shuffled),
('linkedlist', 'отсортированный', records_sorted),
('hashtable', 'случайный', records_shuffled),
('hashtable', 'отсортированный', records_sorted),
('bst', 'случайный', records_shuffled),
('bst', 'отсортированный', records_sorted),
]
results = []
for struct_type, mode, data_records in experiments:
print(f"Тестирование: {struct_type} - {mode}")
params = {'size': HASH_TABLE_SIZE} if struct_type == 'hashtable' else None
result = run_single_experiment(
struct_type, mode, data_records,
names_to_find, names_to_delete,
REPEATS, params
)
results.append(result)
print(f" Insert: {result['insert_mean']:.4f} ± {result['insert_std']:.4f} sec")
print(f" Find: {result['find_mean']:.4f} ± {result['find_std']:.4f} sec")
print(f" Delete: {result['delete_mean']:.4f} ± {result['delete_std']:.4f} sec")
print()
save_to_csv(results) # docs/data/results.csv
plot_results(results) # docs/performance_chart.png
save_report_md(results) # docs/report.md
print_analysis(results)
create_test_mazes()
demo_maze_solver()
demo_player_controls()
run_experiments()
if __name__ == "__main__":

View File

@ -0,0 +1,297 @@
import csv
import os
import numpy as np
from matplotlib import pyplot as plt
def ensure_directories():
os.makedirs('docs/data', exist_ok=True)
def save_to_csv(results, filename="docs/data/results.csv"):
ensure_directories()
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Структура', 'Режим', 'Операция',
'Повтор1', 'Повтор2', 'Повтор3', 'Повтор4', 'Повтор5',
'Среднее', 'Стд_откл'])
for res in results:
struct_name = res['structure']
mode = res['mode']
for op, times, mean, std in [
('вставка', res['insert_all'], res['insert_mean'], res['insert_std']),
('поиск', res['find_all'], res['find_mean'], res['find_std']),
('удаление', res['delete_all'], res['delete_mean'], res['delete_std'])
]:
row = [struct_name, mode, op] + times + [mean, std]
writer.writerow(row)
def plot_results(results, filename="docs/performance_chart.png"):
ensure_directories()
struct_names = {
'linkedlist': 'LinkedList',
'hashtable': 'HashTable',
'bst': 'BST'
}
operations = ['insert', 'find', 'delete']
op_names = {'insert': 'Вставка', 'find': 'Поиск', 'delete': 'Удаление'}
random_data = {}
sorted_data = {}
for res in results:
struct_name = struct_names.get(res['structure'], res['structure'])
mode = res['mode']
if mode == 'случайный':
random_data[struct_name] = {
'insert': res['insert_mean'],
'find': res['find_mean'],
'delete': res['delete_mean']
}
else:
sorted_data[struct_name] = {
'insert': res['insert_mean'],
'find': res['find_mean'],
'delete': res['delete_mean']
}
structure_order = ['LinkedList', 'HashTable', 'BST']
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
for idx, op in enumerate(operations):
ax = axes[idx]
x = np.arange(len(structure_order))
width = 0.35
random_means = []
sorted_means = []
for struct in structure_order:
if struct in random_data:
random_means.append(random_data[struct][op])
else:
random_means.append(0)
if struct in sorted_data:
sorted_means.append(sorted_data[struct][op])
else:
sorted_means.append(0)
if not random_means and not sorted_means:
print(f" Нет данных для операции {op}")
continue
bars1 = ax.bar(x - width/2, random_means, width,
label='Случайный порядок', color='skyblue')
bars2 = ax.bar(x + width/2, sorted_means, width,
label='Отсортированный порядок', color='salmon')
ax.set_xlabel('Структура данных')
ax.set_ylabel('Время (секунды)')
ax.set_title(f'{op_names.get(op, op)}')
ax.set_xticks(x)
ax.set_xticklabels(structure_order)
ax.legend()
for bar in bars1 + bars2:
height = bar.get_height()
if height > 0:
ax.annotate(f'{height:.3f}',
xy=(bar.get_x() + bar.get_width() / 2, height),
xytext=(0, 3), textcoords="offset points",
ha='center', va='bottom', fontsize=8)
plt.tight_layout()
plt.savefig(filename, dpi=150)
plt.show()
def save_report_md(results, filename="docs/report.md"):
ensure_directories()
results_dict = {}
for res in results:
key = (res['structure'], res['mode'])
results_dict[key] = res
def get_val(struct, mode, field):
key = (struct, mode)
if key in results_dict:
return results_dict[key][field]
return 0.0
ll_random_insert = get_val('linkedlist', 'случайный', 'insert_mean')
ll_random_find = get_val('linkedlist', 'случайный', 'find_mean')
ll_random_delete = get_val('linkedlist', 'случайный', 'delete_mean')
ll_sorted_insert = get_val('linkedlist', 'отсортированный', 'insert_mean')
ll_sorted_find = get_val('linkedlist', 'отсортированный', 'find_mean')
ll_sorted_delete = get_val('linkedlist', 'отсортированный', 'delete_mean')
ht_random_insert = get_val('hashtable', 'случайный', 'insert_mean')
ht_random_find = get_val('hashtable', 'случайный', 'find_mean')
ht_random_delete = get_val('hashtable', 'случайный', 'delete_mean')
ht_sorted_insert = get_val('hashtable', 'отсортированный', 'insert_mean')
ht_sorted_find = get_val('hashtable', 'отсортированный', 'find_mean')
ht_sorted_delete = get_val('hashtable', 'отсортированный', 'delete_mean')
bst_random_insert = get_val('bst', 'случайный', 'insert_mean')
bst_random_find = get_val('bst', 'случайный', 'find_mean')
bst_random_delete = get_val('bst', 'случайный', 'delete_mean')
bst_sorted_insert = get_val('bst', 'отсортированный', 'insert_mean')
bst_sorted_find = get_val('bst', 'отсортированный', 'find_mean')
bst_sorted_delete = get_val('bst', 'отсортированный', 'delete_mean')
from datetime import datetime
report_content = f"""# Отчёт по лабораторной работе
## Цель работы
Реализовать три структуры данных «с нуля» (связный список, хеш-таблица, двоичное дерево поиска), применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций.
## Параметры эксперимента
- Количество записей: 10000
- Количество повторов каждого теста: 5
- Размер хеш-таблицы: 1000 корзин
## Результаты экспериментов
### 1. Связный список
| Режим | Вставка (сек) | Поиск (сек) | Удаление (сек) |
|-------|---------------|-------------|----------------|
| Случайный | {ll_random_insert:.4f} | {ll_random_find:.4f} | {ll_random_delete:.4f} |
| Отсортированный | {ll_sorted_insert:.4f} | {ll_sorted_find:.4f} | {ll_sorted_delete:.4f} |
### 2. Хеш-таблица
| Режим | Вставка (сек) | Поиск (сек) | Удаление (сек) |
|-------|---------------|-------------|----------------|
| Случайный | {ht_random_insert:.4f} | {ht_random_find:.4f} | {ht_random_delete:.4f} |
| Отсортированный | {ht_sorted_insert:.4f} | {ht_sorted_find:.4f} | {ht_sorted_delete:.4f} |
### 3. Двоичное дерево поиска (BST)
| Режим | Вставка (сек) | Поиск (сек) | Удаление (сек) |
|-------|---------------|-------------|----------------|
| Случайный | {bst_random_insert:.4f} | {bst_random_find:.4f} | {bst_random_delete:.4f} |
| Отсортированный | {bst_sorted_insert:.4f} | {bst_sorted_find:.4f} | {bst_sorted_delete:.4f} |
## Анализ результатов
### 1. Влияние порядка данных на BST
На отсортированных данных BST деградирует с O(log n) до O(n).
Время вставки увеличилось с {bst_random_insert:.4f} до {bst_sorted_insert:.4f} секунд в {bst_sorted_insert/bst_random_insert:.1f} раз.
### 2. Почему хеш-таблица не чувствительна к порядку
Хеш-функция распределяет элементы случайно, порядок ввода не влияет на позицию элемента.
Разница между случайным и отсортированным порядком:
- Вставка: {ht_random_insert:.4f} vs {ht_sorted_insert:.4f}
- Отношение: {ht_sorted_insert/ht_random_insert:.2f}x (почти не чувствительна)
### 3. Почему связный список медленный при поиске
Поиск требует последовательного прохода O(n) без возможности индексации.
Поэтому связный список хорош только когда записей мало.
Для больших телефонных справочников он не подходит.
Сравнение скорости поиска (случайные данные):
- LinkedList: {ll_random_find:.4f} сек
- HashTable: {ht_random_find:.4f} сек (в {ll_random_find/ht_random_find:.1f} раз быстрее)
- BST: {bst_random_find:.4f} сек
### 4. Сравнение удаления
| Структура | Сложность | Время на 50 удалений (случайные данные) |
|-----------|-----------|------------------------------------------|
| Связный список | O(n) | {ll_random_delete:.4f} сек |
| Хеш-таблица | O(1) в среднем | {ht_random_delete:.4f} сек |
| BST | O(log n) в среднем | {bst_random_delete:.4f} сек |
## Вывод:
| Задача | Рекомендация | Почему |
|--------|-------------|--------|
| Частый поиск | Хеш-таблица | O(1) в среднем, не зависит от порядка |
| Частые вставки/удаления | Хеш-таблица | Амортизированное O(1) |
| Нужен отсортированный вывод | Сбалансированное дерево (AVL/Red-Black) | In-order обход даёт сортировку |
| Мало данных (<100 элементов) | Связный список или массив | Простота, накладные расходы не оправданы |
| Последовательный доступ (очередь/стек) | Связный список | Вставка/удаление в начало/конец за O(1) |
## Заключение
Эксперимент наглядно демонстрирует:
1. **BST без балансировки опасен** на отсортированных данных он деградирует до O(n)
2. **Хеш-таблица стабильна** её производительность не зависит от порядка входных данных
3. **Связный список** подходит только для специфических задач с малым объёмом данных
## Дата выполнения
{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
with open(filename, 'w', encoding='utf-8') as f:
f.write(report_content)
def print_analysis(results):
print("\n" + "="*60)
print("Анализ резов")
print("="*60)
best_insert = min(results, key=lambda x: x['insert_mean'])
best_find = min(results, key=lambda x: x['find_mean'])
best_delete = min(results, key=lambda x: x['delete_mean'])
print(f"\n Лучшая для вставки: {best_insert['structure']} ({best_insert['mode']}) - {best_insert['insert_mean']:.4f} сек")
print(f" Лучшая для поиска: {best_find['structure']} ({best_find['mode']}) - {best_find['find_mean']:.4f} сек")
print(f" Лучшая для удаления: {best_delete['structure']} ({best_delete['mode']}) - {best_delete['delete_mean']:.4f} сек")
bst_random = None
bst_sorted = None
for res in results:
if res['structure'] == 'bst' and res['mode'] == 'случайный':
bst_random = res
elif res['structure'] == 'bst' and res['mode'] == 'отсортированный':
bst_sorted = res
if bst_random and bst_sorted:
print("\n Влияние порядка данных на BST:")
print(f" Вставка: случайный {bst_random['insert_mean']:.4f} сек vs отсортированный {bst_sorted['insert_mean']:.4f} сек")
print(f" Деградация в {bst_sorted['insert_mean']/bst_random['insert_mean']:.1f}x")
ht_random = None
ht_sorted = None
for res in results:
if res['structure'] == 'hashtable' and res['mode'] == 'случайный':
ht_random = res
elif res['structure'] == 'hashtable' and res['mode'] == 'отсортированный':
ht_sorted = res
if ht_random and ht_sorted:
print("\n Чувствительность хеш-таблицы к порядку:")
print(f" Вставка: случайный {ht_random['insert_mean']:.4f} сек vs отсортированный {ht_sorted['insert_mean']:.4f} сек")
print(f" Отношение: {ht_sorted['insert_mean']/ht_random['insert_mean']:.2f}x (почти не чувствительна)")
ll_random = None
for res in results:
if res['structure'] == 'linkedlist' and res['mode'] == 'случайный':
ll_random = res
elif res['structure'] == 'hashtable' and res['mode'] == 'случайный':
ht_random = res
if ll_random and ht_random:
print("\n Сравнение скорости поиска:")
print(f" LinkedList: {ll_random['find_mean']:.4f} сек")
print(f" HashTable: {ht_random['find_mean']:.4f} сек")
print(f" HashTable быстрее в {ll_random['find_mean']/ht_random['find_mean']:.1f} раз")