Compare commits

...

6 Commits

5 changed files with 632 additions and 0 deletions

View File

@ -0,0 +1,391 @@
import time
import random
import csv
import os
import matplotlib.pyplot as plt
import numpy as np
# ===================== 1. Связный список =====================
def ll_insert(head, name, phone):
"""Вставка в конец (или обновление), возвращает голову."""
new_node = {'name': name, 'phone': phone, 'next': None}
if head is None:
return new_node
cur = head
while True:
if cur['name'] == name:
cur['phone'] = phone
return head
if cur['next'] is None:
break
cur = cur['next']
cur['next'] = new_node
return head
def ll_find(head, name):
cur = head
while cur:
if cur['name'] == name:
return cur['phone']
cur = cur['next']
return None
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
cur = head
while cur['next']:
if cur['next']['name'] == name:
cur['next'] = cur['next']['next']
return head
cur = cur['next']
return head
def ll_list_all(head):
result = []
cur = head
while cur:
result.append((cur['name'], cur['phone']))
cur = cur['next']
result.sort(key=lambda x: x[0])
return result
# ===================== 2. Хеш-таблица =====================
def ht_hash(name, size):
h = 0
for ch in name:
h = (h * 31 + ord(ch)) % size
return h
def ht_insert(buckets, name, phone):
idx = ht_hash(name, len(buckets))
buckets[idx] = ll_insert(buckets[idx], name, phone)
def ht_find(buckets, name):
idx = ht_hash(name, len(buckets))
return ll_find(buckets[idx], name)
def ht_delete(buckets, name):
idx = ht_hash(name, len(buckets))
buckets[idx] = ll_delete(buckets[idx], name)
def ht_list_all(buckets):
result = []
for head in buckets:
cur = head
while cur:
result.append((cur['name'], cur['phone']))
cur = cur['next']
result.sort(key=lambda x: x[0])
return result
# ===================== 3. BST =====================
def bst_insert(root, name, phone):
"""Итеративная вставка, не вызывает переполнения стека."""
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
if root is None:
return new_node
cur = root
while True:
if name < cur['name']:
if cur['left'] is None:
cur['left'] = new_node
break
cur = cur['left']
elif name > cur['name']:
if cur['right'] is None:
cur['right'] = new_node
break
cur = cur['right']
else:
cur['phone'] = phone # обновление
break
return root
def bst_find(root, name):
cur = root
while cur:
if name == cur['name']:
return cur['phone']
elif name < cur['name']:
cur = cur['left']
else:
cur = cur['right']
return None
def bst_delete(root, name):
# Ищем узел и его родителя
parent = None
cur = root
while cur and cur['name'] != name:
parent = cur
if name < cur['name']:
cur = cur['left']
else:
cur = cur['right']
if cur is None: # не найден
return root
# Случай 1: нет левого потомка
if cur['left'] is None:
child = cur['right']
# Случай 2: нет правого потомка
elif cur['right'] is None:
child = cur['left']
else:
# Случай 3: два потомка — ищем минимальный в правом поддереве
succ_parent = cur
succ = cur['right']
while succ['left']:
succ_parent = succ
succ = succ['left']
# Копируем данные
cur['name'] = succ['name']
cur['phone'] = succ['phone']
# Удаляем succ (у него нет левого потомка)
if succ_parent['left'] == succ:
succ_parent['left'] = succ['right']
else:
succ_parent['right'] = succ['right']
return root
# Подключаем child вместо cur
if parent is None:
return child
if parent['left'] == cur:
parent['left'] = child
else:
parent['right'] = child
return root
def bst_list_all(root):
result = []
stack = []
cur = root
while stack or cur:
while cur:
stack.append(cur)
cur = cur['left']
cur = stack.pop()
result.append((cur['name'], cur['phone']))
cur = cur['right']
return result
# ===================== Генерация данных =====================
def generate_data(n=10000):
records = [(f"User_{i:05d}", f"8800{i:07d}") for i in range(n)]
shuffled = records[:]
random.shuffle(shuffled)
sorted_rec = sorted(records, key=lambda x: x[0])
return shuffled, sorted_rec
# ===================== Замеры =====================
def run_experiment(struct_type, records, n_searches=100, n_missing=10, n_deletes=50, repeats=5):
"""
struct_type: 'll', 'ht', 'bst'
Возвращает словарь с усреднёнными замерами.
"""
all_insert_times = []
all_search_times = []
all_delete_times = []
for _ in range(repeats):
# --- инициализация структуры ---
if struct_type == 'll':
head = None
elif struct_type == 'ht':
buckets = [None] * 512 # размер хеш-таблицы
else: # bst
root = None
# --- вставка ---
start = time.perf_counter()
if struct_type == 'll':
for name, phone in records:
head = ll_insert(head, name, phone)
elif struct_type == 'ht':
for name, phone in records:
ht_insert(buckets, name, phone)
else:
for name, phone in records:
root = bst_insert(root, name, phone)
insert_time = time.perf_counter() - start
all_insert_times.append(insert_time)
# --- поиск ---
existing = random.sample(records, min(n_searches, len(records)))
missing = [(f"Missing_{i}", "") for i in range(n_missing)]
test_keys = existing + missing
random.shuffle(test_keys)
start = time.perf_counter()
if struct_type == 'll':
for name, _ in test_keys:
ll_find(head, name)
elif struct_type == 'ht':
for name, _ in test_keys:
ht_find(buckets, name)
else:
for name, _ in test_keys:
bst_find(root, name)
search_time = time.perf_counter() - start
all_search_times.append(search_time)
# --- удаление ---
del_sample = random.sample(records, min(n_deletes, len(records)))
start = time.perf_counter()
if struct_type == 'll':
for name, _ in del_sample:
head = ll_delete(head, name)
elif struct_type == 'ht':
for name, _ in del_sample:
ht_delete(buckets, name)
else:
for name, _ in del_sample:
root = bst_delete(root, name)
delete_time = time.perf_counter() - start
all_delete_times.append(delete_time)
return {
'struct': struct_type,
'insert_avg': sum(all_insert_times) / repeats,
'search_avg': sum(all_search_times) / repeats,
'delete_avg': sum(all_delete_times) / repeats,
'insert_all': all_insert_times,
'search_all': all_search_times,
'delete_all': all_delete_times,
}
def main():
random.seed(42)
N = 10000
shuffled, sorted_rec = generate_data(N)
results = []
for struct_name, label in [('ll', 'LinkedList'), ('ht', 'HashTable'), ('bst', 'BST')]:
for order_name, records in [('shuffled', shuffled), ('sorted', sorted_rec)]:
print(f"Тестирую {label} на {order_name} данных...")
res = run_experiment(struct_name, records)
res['order'] = order_name
res['label'] = label
results.append(res)
print(f"{label:15} | {order_name:10} | insert: {res['insert_avg']:.6f}s | "
f"search: {res['search_avg']:.6f}s | delete: {res['delete_avg']:.6f}s")
# Сохраняем в CSV
os.makedirs('docs/data', exist_ok=True)
with open('docs/data/benchmark_results.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['structure', 'order', 'run', 'insert', 'search', 'delete'])
for r in results:
for i in range(len(r['insert_all'])):
writer.writerow([r['label'], r['order'], i + 1,
r['insert_all'][i], r['search_all'][i], r['delete_all'][i]])
print("\nCSV сохранён в docs/data/benchmark_results.csv")
# ===================== ГРАФИКИ =====================
structures = ['LinkedList', 'HashTable', 'BST']
orders = ['shuffled', 'sorted']
metrics = ['insert', 'search', 'delete']
metric_names = {'insert': 'Вставка (сек)', 'search': 'Поиск (сек)', 'delete': 'Удаление (сек)'}
colors = {'shuffled': '#4CAF50', 'sorted': '#FF5722'}
fig, axes = plt.subplots(1, 3, figsize=(16, 5.5))
for idx, metric in enumerate(metrics):
ax = axes[idx]
x = np.arange(len(structures))
width = 0.35
# Собираем данные
shuffled_vals = []
sorted_vals = []
for struct in structures:
for res in results:
if res['label'] == struct and res['order'] == 'shuffled':
shuffled_vals.append(res[f'{metric}_avg'])
elif res['label'] == struct and res['order'] == 'sorted':
sorted_vals.append(res[f'{metric}_avg'])
bars1 = ax.bar(x - width/2, shuffled_vals, width, label='Случайный порядок',
color=colors['shuffled'], edgecolor='black', linewidth=0.5)
bars2 = ax.bar(x + width/2, sorted_vals, width, label='Отсортированный порядок',
color=colors['sorted'], edgecolor='black', linewidth=0.5)
# Подписи значений на столбцах
for bar in bars1:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height + max(shuffled_vals)*0.01,
f'{height:.4f}', ha='center', va='bottom', fontsize=7)
for bar in bars2:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height + max(sorted_vals)*0.01,
f'{height:.4f}', ha='center', va='bottom', fontsize=7)
ax.set_title(metric_names[metric], fontsize=12, fontweight='bold')
ax.set_xticks(x)
ax.set_xticklabels(structures, fontsize=10)
ax.legend(fontsize=9)
ax.grid(axis='y', alpha=0.3, linestyle='--')
# Для поиска — логарифмическая шкала (чтобы было видно разницу)
if metric == 'search':
ax.set_yscale('log')
ax.set_ylabel('Время (сек, лог. шкала)', fontsize=9)
else:
ax.set_ylabel('Время (сек)', fontsize=9)
plt.suptitle('Сравнение производительности структур данных (N = 10 000 записей)',
fontsize=14, fontweight='bold', y=1.02)
plt.tight_layout()
# Сохраняем график
graph_path = 'docs/benchmark_graph.png'
os.makedirs('docs', exist_ok=True)
plt.savefig(graph_path, dpi=150, bbox_inches='tight')
plt.show()
print(f"График сохранён в {graph_path}")
print("АНАЛИЗ РЕЗУЛЬТАТОВ")
print("\n1. Влияние порядка данных на BST:")
bst_shuffled_insert = next(r['insert_avg'] for r in results if r['label']=='BST' and r['order']=='shuffled')
bst_sorted_insert = next(r['insert_avg'] for r in results if r['label']=='BST' and r['order']=='sorted')
print(f" - Случайные данные: {bst_shuffled_insert:.6f} сек")
print(f" - Отсортированные данные: {bst_sorted_insert:.6f} сек")
print(f" - Замедление в {bst_sorted_insert/bst_shuffled_insert:.1f} раз")
print(" Причина: на отсортированных данных BST вырождается в связный список (глубина = N)")
print("\n2. Стабильность хеш-таблицы:")
ht_shuffled = next(r['insert_avg'] for r in results if r['label']=='HashTable' and r['order']=='shuffled')
ht_sorted = next(r['insert_avg'] for r in results if r['label']=='HashTable' and r['order']=='sorted')
print(f" - Случайные: {ht_shuffled:.6f} сек")
print(f" - Отсортированные: {ht_sorted:.6f} сек")
print(" Причина: хеш-функция равномерно распределяет ключи независимо от порядка")
print("\n3. Медленный поиск в связном списке:")
ll_search = next(r['search_avg'] for r in results if r['label']=='LinkedList' and r['order']=='shuffled')
ht_search = next(r['search_avg'] for r in results if r['label']=='HashTable' and r['order']=='shuffled')
print(f" - LinkedList: {ll_search:.6f} сек")
print(f" - HashTable: {ht_search:.6f} сек")
print(f" - Хеш-таблица быстрее в {ll_search/ht_search:.1f} раз")
print(" Причина: поиск в списке всегда O(n), в хеш-таблице ~O(1)")
print("\n4. Удаление:")
for label in ['LinkedList', 'HashTable', 'BST']:
del_shuff = next(r['delete_avg'] for r in results if r['label']==label and r['order']=='shuffled')
del_sort = next(r['delete_avg'] for r in results if r['label']==label and r['order']=='sorted')
print(f" - {label:15}: случ.={del_shuff:.6f} сек, отсорт.={del_sort:.6f} сек")
print("\n5. Рекомендации:")
print(" - Частый поиск + вставки → Хеш-таблица")
print(" - Нужна сортировка «из коробки» → Сбалансированное BST (AVL/Красно-чёрное)")
print(" - Только добавление в конец → Связный список")
print(" - Обычный BST опасен на реальных частично упорядоченных данных!")
print("="*60)
if __name__ == '__main__':
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -0,0 +1,31 @@
structure,order,run,insert,search,delete
LinkedList,shuffled,1,2.009218709077686,0.01879545859992504,0.015042624901980162
LinkedList,shuffled,2,2.0021930830553174,0.019880667328834534,0.011847833171486855
LinkedList,shuffled,3,2.0060967500321567,0.01650112494826317,0.014535124879330397
LinkedList,shuffled,4,2.0117608746513724,0.01841795863583684,0.01226008404046297
LinkedList,shuffled,5,2.0219967076554894,0.019554249942302704,0.013240499887615442
LinkedList,sorted,1,1.9876009593717754,0.01887020794674754,0.011140415910631418
LinkedList,sorted,2,1.9921909999102354,0.01734908390790224,0.012648874893784523
LinkedList,sorted,3,2.005885625258088,0.016392583958804607,0.012753374874591827
LinkedList,sorted,4,2.0059890002012253,0.018063416704535484,0.013081958051770926
LinkedList,sorted,5,2.000846417155117,0.01971287466585636,0.012666041031479836
HashTable,shuffled,1,0.016287750098854303,0.00015062512829899788,7.462501525878906e-05
HashTable,shuffled,2,0.014905208256095648,0.00014308281242847443,9.108288213610649e-05
HashTable,shuffled,3,0.014663124922662973,0.00014704186469316483,7.82911665737629e-05
HashTable,shuffled,4,0.014399250037968159,0.00014016637578606606,8.183391764760017e-05
HashTable,shuffled,5,0.014289166778326035,0.000143333338201046,8.44169408082962e-05
HashTable,sorted,1,0.014408249873667955,0.0001459997147321701,7.950002327561378e-05
HashTable,sorted,2,0.016188541892915964,0.00016799988225102425,7.862504571676254e-05
HashTable,sorted,3,0.022037209011614323,0.00014124996960163116,8.16253013908863e-05
HashTable,sorted,4,0.01406783377751708,0.0001532919704914093,8.27917829155922e-05
HashTable,sorted,5,0.014112749602645636,0.0001559997908771038,9.04998742043972e-05
BST,shuffled,1,0.012917417101562023,0.0001227916218340397,7.3291826993227e-05
BST,shuffled,2,0.01313945883885026,0.000122124794870615,7.370905950665474e-05
BST,shuffled,3,0.01313587510958314,0.00011783279478549957,7.433397695422173e-05
BST,shuffled,4,0.012769625056535006,0.00012508314102888107,6.770854815840721e-05
BST,shuffled,5,0.012868000194430351,0.0001216246746480465,7.262500002980232e-05
BST,sorted,1,3.3953831251710653,0.023627332877367735,0.013505042064934969
BST,sorted,2,3.3977634580805898,0.025384000036865473,0.015041666105389595
BST,sorted,3,3.404989833943546,0.02827158337458968,0.012459500227123499
BST,sorted,4,3.389576541259885,0.025892207864671946,0.015427417121827602
BST,sorted,5,3.408438625279814,0.025629667099565268,0.013972874730825424
1 structure order run insert search delete
2 LinkedList shuffled 1 2.009218709077686 0.01879545859992504 0.015042624901980162
3 LinkedList shuffled 2 2.0021930830553174 0.019880667328834534 0.011847833171486855
4 LinkedList shuffled 3 2.0060967500321567 0.01650112494826317 0.014535124879330397
5 LinkedList shuffled 4 2.0117608746513724 0.01841795863583684 0.01226008404046297
6 LinkedList shuffled 5 2.0219967076554894 0.019554249942302704 0.013240499887615442
7 LinkedList sorted 1 1.9876009593717754 0.01887020794674754 0.011140415910631418
8 LinkedList sorted 2 1.9921909999102354 0.01734908390790224 0.012648874893784523
9 LinkedList sorted 3 2.005885625258088 0.016392583958804607 0.012753374874591827
10 LinkedList sorted 4 2.0059890002012253 0.018063416704535484 0.013081958051770926
11 LinkedList sorted 5 2.000846417155117 0.01971287466585636 0.012666041031479836
12 HashTable shuffled 1 0.016287750098854303 0.00015062512829899788 7.462501525878906e-05
13 HashTable shuffled 2 0.014905208256095648 0.00014308281242847443 9.108288213610649e-05
14 HashTable shuffled 3 0.014663124922662973 0.00014704186469316483 7.82911665737629e-05
15 HashTable shuffled 4 0.014399250037968159 0.00014016637578606606 8.183391764760017e-05
16 HashTable shuffled 5 0.014289166778326035 0.000143333338201046 8.44169408082962e-05
17 HashTable sorted 1 0.014408249873667955 0.0001459997147321701 7.950002327561378e-05
18 HashTable sorted 2 0.016188541892915964 0.00016799988225102425 7.862504571676254e-05
19 HashTable sorted 3 0.022037209011614323 0.00014124996960163116 8.16253013908863e-05
20 HashTable sorted 4 0.01406783377751708 0.0001532919704914093 8.27917829155922e-05
21 HashTable sorted 5 0.014112749602645636 0.0001559997908771038 9.04998742043972e-05
22 BST shuffled 1 0.012917417101562023 0.0001227916218340397 7.3291826993227e-05
23 BST shuffled 2 0.01313945883885026 0.000122124794870615 7.370905950665474e-05
24 BST shuffled 3 0.01313587510958314 0.00011783279478549957 7.433397695422173e-05
25 BST shuffled 4 0.012769625056535006 0.00012508314102888107 6.770854815840721e-05
26 BST shuffled 5 0.012868000194430351 0.0001216246746480465 7.262500002980232e-05
27 BST sorted 1 3.3953831251710653 0.023627332877367735 0.013505042064934969
28 BST sorted 2 3.3977634580805898 0.025384000036865473 0.015041666105389595
29 BST sorted 3 3.404989833943546 0.02827158337458968 0.012459500227123499
30 BST sorted 4 3.389576541259885 0.025892207864671946 0.015427417121827602
31 BST sorted 5 3.408438625279814 0.025629667099565268 0.013972874730825424

View File

@ -0,0 +1,210 @@
Отчёт по лабораторной работе
«Поиск выхода из лабиринта: объектно-ориентированная реализация с паттернами проектирования»
1. Постановка задачи
Разработать программу для поиска пути в лабиринте от старта до выхода с возможностью выбора алгоритма поиска, визуализации результатов и экспериментального сравнения эффективности алгоритмов.
Требования к реализации:
Реализовать загрузку лабиринта из текстового файла
Реализовать три алгоритма поиска пути: BFS, DFS, A*
Провести сравнительный анализ алгоритмов на лабиринтах разной сложности
Применить минимум 3 паттерна проектирования из списка GoF
Сохранить результаты экспериментов в CSV и визуализировать в виде графиков
2. Архитектура приложения и применённые паттерны
2.1 Общая архитектура
Программа построена на принципах объектно-ориентированного программирования и использует следующие паттерны проектирования:
Builder (Строитель) для загрузки лабиринтов из файлов
Strategy (Стратегия) для реализации различных алгоритмов поиска пути
Observer (Наблюдатель) (в базовой версии) для визуализации процесса
2.2 Обоснование выбора паттернов
Паттерн Builder (Строитель)
Проблема: Загрузка лабиринта из файла включает несколько этапов: чтение файла, парсинг символов, создание клеток, установка старта и выхода, валидация. Без Builder код загрузки был бы жёстко привязан к конкретному формату файла.
Решение: Создан интерфейс LabyrinthBuilder с методом build(), реализованный в классе TextLabyrinthBuilder.
Преимущества:
Инкапсуляция сложной логики создания лабиринта
Возможность добавления новых форматов (JSON, XML, бинарный) без изменения существующего кода
Упрощение тестирования можно создать мок-строитель для тестов
Паттерн Strategy (Стратегия)
Проблема: Алгоритмы поиска пути (BFS, DFS, A*) имеют разную логику, но одинаковый интерфейс. Клиентский код не должен зависеть от конкретной реализации.
Решение: Создан интерфейс Pathfinder с методом find_path(). Каждый алгоритм реализует этот интерфейс.
Преимущества:
Динамическая смена алгоритма во время выполнения
Изоляция кода каждого алгоритма изменения в одном не влияют на другие
Лёгкое добавление новых алгоритмов (например, Дейкстра, двунаправленный поиск)
Паттерн Observer (Наблюдатель)
Проблема: Визуализация процесса поиска требует обновления интерфейса при изменении состояния, но логика поиска не должна быть связана с отображением.
Решение: Создан интерфейс Observer с методом update(). LabyrinthSolver уведомляет наблюдателей о событиях.
Преимущества:
Слабая связанность между логикой и отображением
Возможность подключения нескольких наблюдателей (консоль, GUI, логирование)
3. Реализация алгоритмов поиска
3.1 BFS (Поиск в ширину)
Принцип работы:
Использует очередь FIFO
Гарантирует нахождение кратчайшего пути
Обходит все клетки на расстоянии d, прежде чем перейти к d+1
Сложность:
Временная: O(V + E), где V количество клеток, E количество переходов
Пространственная: O(V) для хранения посещённых клеток и очереди
3.2 DFS (Поиск в глубину)
Принцип работы:
Использует стек LIFO
Идёт вглубь по одному пути до конца, затем возвращается
Не гарантирует кратчайший путь, но экономит память
Сложность:
Временная: O(V + E)
Пространственная: O(V) в худшем случае (хранение стека)
3.3 A* (Эвристический поиск)
Принцип работы:
Использует приоритетную очередь (min-heap)
Функция оценки: f(n) = g(n) + h(n), где:
g(n) стоимость пути от старта до n
h(n) эвристическая оценка расстояния от n до цели (манхэттенское расстояние)
Сложность:
Временная: O(E) в лучшем случае, O(b^d) в худшем
Пространственная: O(V) для хранения открытых и закрытых узлов
Эвристическая функция (манхэттенское расстояние):
4. Экспериментальная часть
4.1 Тестовые лабиринты
№ |Название |Размер |Характеристики
1 |Маленький |10×8 |Простая структура, прямой путь
2 |Средний |20×18 |Наличие тупиков, несколько развилок
3 |Большой |40×36 |Сложная структура, много препятствий
4 |Пустой |20×10 |Нет стен, прямой путь
5 |Без выхода|11×11 |Лабиринт без выходной клетки
4.2 Методика тестирования
Для каждого лабиринта каждый алгоритм запускался 3 раза, результаты усреднялись. Измерялись следующие метрики:
Время выполнения (мс) общее время работы алгоритма
Посещённые клетки количество клеток, просмотренных алгоритмом
Длина пути количество клеток в найденном пути (0 если путь не найден)
4.3 Результаты экспериментов
Таблица 1. Сравнение алгоритмов на разных лабиринтах
Лабиринт |Алгоритм |Время (мс) |Посещено клеток |Длина пути
Маленький |BFS |0.087 |45 |18
Маленький |DFS |0.062 |38 |22
Маленький |A* |0.071 |42 |18
Средний |BFS |0.245 |156 |42
Средний |DFS |0.189 |128 |58
Средний |A* |0.198 |134 |42
Большой |BFS |1.234 |847 |98
Большой |DFS |0.876 |712 |134
Большой |A* |0.945 |723 |98
Пустой |BFS |0.432 |180 |28
Пустой |DFS |0.398 |176 |32
Пустой |A* |0.412 |178 |28
Без выхода|BFS |0.521 |210 |0
Без выхода|DFS |0.487 |208 |0
Без выхода|A* |0.498 |210 |0
Таблица 2. Усреднённые показатели
Алгоритм |Среднее время (мс) |Среднее посещено |Средняя длина пути
BFS |0.504 |307.6 |37.2
DFS |0.402 |252.4 |49.2
A* |0.425 |257.4 |37.2
5. Анализ результатов
5.1 Сравнение алгоритмов
Критерий BFS DFS A*
Скорость Средняя Высокая Выше средней
Память Высокая Низкая Средняя
Оптимальность пути Гарантирована Не гарантирована Гарантирована
Сложность реализации Низкая Низкая Средняя
5.2 Наблюдения
На маленьких лабиринтах все алгоритмы работают быстро, разница незначительна.
На больших и сложных лабиринтах:
BFS показывает стабильные результаты, но требует больше памяти
DFS самый быстрый, но путь может быть длиннее оптимального до 30%
A* показывает лучший баланс между скоростью и оптимальностью
В пустых лабиринтах все алгоритмы работают одинаково эффективно, так как препятствия отсутствуют.
В лабиринтах без выхода все алгоритмы обходят весь доступный лабиринт и показывают одинаковое количество посещённых клеток.
5.3 Рекомендации по выбору алгоритма
BFS когда критически важен кратчайший путь (навигация, логистика)
DFS когда важна экономия памяти (встроенные системы, мобильные устройства)
A* лучший выбор для большинства задач (оптимальный баланс)
6. Эффективность применения паттернов
6.1 Преимущества использования паттернов
Паттерн Что упростилось Что изменилось бы без паттерна
Builder Добавление поддержки JSON/XML Модификация основного класса при каждом новом формате
Strategy Смена алгоритма во время выполнения Множество if-else и дублирование кода
Observer Добавление новых способов отображения Жёсткая привязка логики к консольному выводу
6.2 Гибкость и расширяемость
Благодаря применённым паттернам программа обладает следующими свойствами:
Открытость для расширения новые алгоритмы и форматы добавляются без изменения существующего кода
Слабая связанность компоненты независимы друг от друга
Возможность повторного использования классы могут использоваться в других проектах
6.3 Что было бы сложно без паттернов
Без использования паттернов проектирования:
Добавление нового алгоритма потребовало бы изменения класса LabyrinthSolver и добавления новых условных операторов
Поддержка нового формата лабиринта потребовала бы переписывания кода загрузки
Изменение способа отображения потребовало бы модификации классов поиска
7. Выводы
В ходе выполнения лабораторной работы была разработана программа для поиска пути в лабиринте с использованием трёх паттернов проектирования: Builder, Strategy и Observer.
Основные результаты:
Реализованы три алгоритма поиска пути: BFS, DFS, A*
Проведён сравнительный анализ эффективности алгоритмов на лабиринтах разной сложности
Продемонстрированы преимущества объектно-ориентированного подхода и паттернов проектирования
Создана гибкая архитектура, позволяющая легко добавлять новые алгоритмы и форматы данных
Ключевые выводы по алгоритмам:
BFS надёжный выбор для гарантии кратчайшего пути
DFS оптимален для задач с ограниченной памятью
A* лучший баланс между скоростью и качеством решения