Compare commits

...

7 Commits

22 changed files with 1468 additions and 0 deletions

0
groshevava/426.md.txt Normal file
View File

153
groshevava/docs/data/bst.py Normal file
View File

@ -0,0 +1,153 @@
#реализация справочника на основе бинарного дерева поиска (BST).
#создаём узел
def bst_create_node(name, phone):
return {
'name': name,
'phone': phone,
'left': None,
'right': None
}
#Вставляет запись в BST или обновляет.Возвращает корень дерева
def bst_insert(root, name, phone):
new_node = bst_create_node(name, phone)
if root is None:
return new_node
current = root
parent = None
while current is not None:
parent = current
if name < current['name']:
current = current['left']
elif name > current['name']:
current = current['right']
else:
current['phone'] = phone
return root
#вставляем новый узел
if name < parent['name']:
parent['left'] = new_node
else:
parent['right'] = new_node
return root
#ищет запись в BST по имени. Возвращает телефон или None
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
#удаляет запись из BST по имени. Возвращает новый корень дерева
def bst_delete(root, name):
if root is None:
return None
parent = None
current = root
while current is not None and current['name'] != name:
parent = current
if name < current['name']:
current = current['left']
else:
current = current['right']
if current is None:
return root
if current['left'] is None and current['right'] is None:
if parent is None:
return None
elif parent['left'] == current:
parent['left'] = None
else:
parent['right'] = None
return root
if current['left'] is None:
if parent is None:
return current['right']
elif parent['left'] == current:
parent['left'] = current['right']
else:
parent['right'] = current['right']
return root
if current['right'] is None:
if parent is None:
return current['left']
elif parent['left'] == current:
parent['left'] = current['left']
else:
parent['right'] = current['left']
return root
successor_parent = current
successor = current['right']
while successor['left'] is not None:
successor_parent = successor
successor = successor['left']
current['name'] = successor['name']
current['phone'] = successor['phone']
if successor_parent == current:
successor_parent['right'] = successor['right']
else:
successor_parent['left'] = successor['right']
return root
#рекурсивно собирает записи дерева по возрастанию имён
def _bst_in_order_collect(node, records):
if node is not None:
_bst_in_order_collect(node['left'], records)
records.append((node['name'], node['phone']))
_bst_in_order_collect(node['right'], records)
#отсортированный список всех записей
def bst_list_all(root):
records = []
# Для очень глубоких деревьев лучше использовать итеративный обход
_bst_in_order_iterative(root, records)
return records
#центрированный обход дерева.
def _bst_in_order_iterative(root, records):
stack = []
current = root
while current is not None or len(stack) > 0:
# доходим до самого левого узла
while current is not None:
stack.append(current)
current = current['left']
# обрабатываем узел
current = stack.pop()
records.append((current['name'], current['phone']))
#переходим к правому поддереву
current = current['right']

View File

@ -0,0 +1,75 @@
# maze_solver/builders.py
from abc import ABC, abstractmethod
from models import Cell, Maze
class MazeBuilder(ABC):
"""Интерфейс строителя лабиринта."""
@abstractmethod
def buildFromFile(self, filename: str) -> Maze:
"""Строит объект Maze из файла."""
pass
class TextFileMazeBuilder(MazeBuilder):
"""Строитель для текстового формата: ■ стена, ' ' проход, S старт, E выход."""
# Поддерживаемые символы стен
WALL_SYMBOLS = {'#', '', '', '', '', ''}
def __init__(self, require_exit: bool = True):
"""
Args:
require_exit: Если False, позволяет создавать лабиринты без выхода
"""
self.require_exit = require_exit
def buildFromFile(self, filename: str) -> Maze:
with open(filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
# Убираем символы новой строки и пустые строки в конце файла
cleaned_lines = [line.rstrip('\n') for line in lines if line.strip() != '']
if not cleaned_lines:
raise ValueError("Файл лабиринта пуст")
height = len(cleaned_lines)
width = len(cleaned_lines[0])
grid = []
start_cell = None
exit_cell = None
for y, line in enumerate(cleaned_lines):
row = []
if len(line) != width:
raise ValueError(
f"Строка {y} имеет длину {len(line)}, ожидалось {width}. "
f"Лабиринт должен быть прямоугольным."
)
for x, char in enumerate(line):
is_wall = char in self.WALL_SYMBOLS
cell = Cell(x, y, is_wall)
if char == 'S':
cell.isStart = True
start_cell = cell
elif char == 'E':
cell.isExit = True
exit_cell = cell
row.append(cell)
grid.append(row)
if not start_cell:
raise ValueError("В лабиринте не найдена стартовая позиция (S)")
if self.require_exit and not exit_cell:
raise ValueError("В лабиринте не найдена выходная позиция (E)")
maze = Maze(width, height, grid)
maze.start = start_cell
maze.exit = exit_cell
return maze

View File

@ -0,0 +1,10 @@
■■■■■■■■■■
■S ■
■ ■
■ ■
■ ■
■ ■
■ ■
■ ■
■ E■
■■■■■■■■■■

View File

@ -0,0 +1,198 @@
#проведение экспериментов и замер производительности
import time
import random
import csv
from linked_list import ll_insert, ll_find, ll_delete, ll_list_all
from hash_table import ht_create, ht_insert, ht_find, ht_delete, ht_list_all
from bst import bst_insert, bst_find, bst_delete, bst_list_all
#генерирует записи справочника. shuffled- случайный порядок, sorted-отсортированный по имени
def generate_test_data(n=10000, seed=42):
random.seed(seed)
names = [f"User_{i:05d}" for i in range(n)]
phones = [f"+7-999-{i:07d}" for i in range(n)]
records = list(zip(names, phones))
records_shuffled = records.copy()
random.shuffle(records_shuffled)
records_sorted = sorted(records, key=lambda x: x[0])
return records_shuffled, records_sorted
#замеряем время
def measure_insert(ll_structure, ht_structure, bst_structure, records, mode_name):
results = []
# Связный список
start = time.perf_counter()
head = None
for name, phone in records:
head = ll_insert(head, name, phone)
end = time.perf_counter()
results.append(["LinkedList", mode_name, "вставка", end - start])
# Хеш-таблица
start = time.perf_counter()
buckets = ht_create(256)
for name, phone in records:
ht_insert(buckets, name, phone)
end = time.perf_counter()
results.append(["HashTable", mode_name, "вставка", end - start])
# BST
start = time.perf_counter()
root = None
for name, phone in records:
root = bst_insert(root, name, phone)
end = time.perf_counter()
results.append(["BST", mode_name, "вставка", end - start])
return results, head, buckets, root
def measure_find(head, buckets, root, all_names, mode_name):
results = []
# Выбираем 100 случайных существующих и 10 несуществующих имён
existing_names = random.sample(all_names, 100)
non_existing_names = [f"None_{i}" for i in range(10)]
search_names = existing_names + non_existing_names
# Связный список
start = time.perf_counter()
for name in search_names:
ll_find(head, name)
end = time.perf_counter()
results.append(["LinkedList", mode_name, "поиск", end - start])
# Хеш-таблица
start = time.perf_counter()
for name in search_names:
ht_find(buckets, name)
end = time.perf_counter()
results.append(["HashTable", mode_name, "поиск", end - start])
# BST
start = time.perf_counter()
for name in search_names:
bst_find(root, name)
end = time.perf_counter()
results.append(["BST", mode_name, "поиск", end - start])
return results
def measure_delete(head, buckets, root, all_names, mode_name):
results = []
# Выбираем 50 случайных имён для удаления
delete_names = random.sample(all_names, 50)
# Связный список
start = time.perf_counter()
for name in delete_names:
head = ll_delete(head, name)
end = time.perf_counter()
results.append(["LinkedList", mode_name, "удаление", end - start])
# Хеш-таблица
start = time.perf_counter()
for name in delete_names:
ht_delete(buckets, name)
end = time.perf_counter()
results.append(["HashTable", mode_name, "удаление", end - start])
# BST
start = time.perf_counter()
for name in delete_names:
root = bst_delete(root, name)
end = time.perf_counter()
results.append(["BST", mode_name, "удаление", end - start])
return results
#проводим эксперименты
def run_experiments(records_shuffled, records_sorted, repetitions=5):
all_results = [
["Структура", "Режим", "Операция", "Время (сек)"]
]
all_names = [record[0] for record in records_shuffled]
for rep in range(repetitions):
print(f"Повторение {rep + 1}/{repetitions}")
# Шаффлированные данные
results, head, buckets, root = measure_insert(
None, None, None, records_shuffled, "случайный"
)
all_results.extend(results)
results = measure_find(head, buckets, root, all_names, "случайный")
all_results.extend(results)
results = measure_delete(head, buckets, root, all_names, "случайный")
all_results.extend(results)
# Отсортированные данные
results, head, buckets, root = measure_insert(
None, None, None, records_sorted, "отсортированный"
)
all_results.extend(results)
results = measure_find(head, buckets, root, all_names, "отсортированный")
all_results.extend(results)
results = measure_delete(head, buckets, root, all_names, "отсортированный")
all_results.extend(results)
return all_results
def save_results(results, filename="results.csv"):
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerows(results)
print(f"Результаты сохранены в {filename}")
def analyze_results(filename="results.csv"):
from collections import defaultdict
with open(filename, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
next(reader) # пропускаем заголовок
data = list(reader)
# Группируем для вычисления средних
stats = defaultdict(list)
for row in data:
structure, mode, operation, time_str = row
key = (structure, mode, operation)
stats[key].append(float(time_str))
print("\nСредние времена выполнения (сек):")
print("-" * 60)
print(f"{'Структура':<15} {'Режим':<20} {'Операция':<10} {'Время':<10}")
print("-" * 60)
for (structure, mode, operation), times in sorted(stats.items()):
avg_time = sum(times) / len(times)
print(f"{structure:<15} {mode:<20} {operation:<10} {avg_time:<10.6f}")
if __name__ == "__main__":
print("Генерация тестовых данных...")
records_shuffled, records_sorted = generate_test_data(10000)
print("Запуск экспериментов...")
results = run_experiments(records_shuffled, records_sorted, repetitions=5)
save_results(results)
analyze_results()

View File

@ -0,0 +1,43 @@
#Телефонного справочник на основе хеш-таблицы.
#Использует метод цепочек
from linked_list import ll_insert, ll_find, ll_delete, ll_list_all
#Хеш-функция для строки имени.
#Использует полиномиальное хеширование.
def ht_hash(name, bucket_count):
hash_value = 0
p = 31
for char in name:
hash_value = (hash_value * p + ord(char)) % bucket_count
return hash_value
#Создаёт пустую таблицу
def ht_create(bucket_count=128):
return [None] * bucket_count
#Добавляет запись в таблицу.
#Вычисляет хэш, затем вставляет в нужный бакет.
def ht_insert(buckets, name, phone):
index = ht_hash(name, len(buckets))
buckets[index] = ll_insert(buckets[index], name, phone)
#ищет запись в таблице
def ht_find(buckets, name):
index = ht_hash(name, len(buckets))
return ll_find(buckets[index], name)
#удаляет запись
def ht_delete(buckets, name):
index = ht_hash(name, len(buckets))
buckets[index] = ll_delete(buckets[index], name)
#сортировка по имени
def ht_list_all(buckets):
all_records = []
for bucket_head in buckets:
records = ll_list_all(bucket_head)
all_records.extend(records)
all_records.sort(key=lambda x: x[0])
return all_records

View File

@ -0,0 +1,100 @@
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■S ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■ ■■■■■■■■■■■■■■■ ■ ■ ■■■■■ ■■■■■■■ ■■■■■ ■ ■■■ ■ ■■■ ■■■■■ ■■■■■ ■■■ ■■■■■ ■■■■■ ■ ■■■■■ ■ ■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■ ■ ■■■■■ ■ ■ ■ ■■■■■■■ ■■■ ■■■■■ ■■■ ■ ■■■ ■■■■■■■ ■ ■ ■ ■ ■■■ ■ ■■■■■ ■■■ ■ ■ ■■■ ■ ■■■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■■■ ■■■■■ ■■■ ■ ■■■ ■■■ ■■■ ■■■ ■ ■■■■■■■■■ ■ ■ ■■■■■ ■■■■■ ■ ■ ■■■■■ ■■■ ■■■■■■■■■■■■■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■■■■■ ■ ■ ■■■■■■■■■ ■■■ ■■■ ■■■ ■ ■ ■ ■■■ ■ ■■■ ■■■■■■■ ■■■ ■■■■■ ■■■■■■■■■■■ ■■■ ■ ■ ■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■ ■ ■ ■■■■■ ■■■■■■■■■ ■■■■■■■ ■ ■■■■■ ■■■■■■■ ■■■ ■ ■■■■■ ■■■ ■■■ ■ ■■■ ■ ■ ■ ■ ■ ■■■ ■ ■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■■■ ■■■ ■■■■■ ■ ■ ■ ■ ■■■ ■■■■■■■■■ ■ ■ ■■■ ■ ■ ■■■■■ ■ ■ ■■■■■■■■■ ■ ■■■ ■■■ ■■■ ■■■■■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■■■■■■■■■ ■■■ ■ ■■■■■ ■■■■■■■■■■■ ■ ■ ■■■ ■■■■■ ■■■ ■■■■■ ■ ■ ■ ■ ■■■■■ ■■■ ■ ■■■ ■ ■ ■■■■■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■ ■ ■■■ ■■■ ■■■ ■ ■■■■■ ■■■■■ ■■■ ■■■■■ ■■■ ■ ■■■■■ ■ ■ ■■■ ■■■■■■■ ■ ■ ■ ■ ■ ■■■ ■■■■■ ■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■■■ ■ ■ ■■■ ■ ■■■ ■■■ ■ ■■■■■ ■ ■ ■ ■ ■■■ ■ ■ ■ ■■■ ■■■ ■■■ ■■■ ■ ■■■■■■■■■■■■■ ■ ■ ■ ■■■ ■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■■■■■ ■ ■■■ ■ ■ ■■■■■■■ ■■■ ■■■■■ ■■■■■ ■ ■ ■ ■ ■ ■ ■■■■■ ■■■ ■■■■■■■ ■■■■■ ■■■■■■■ ■■■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■ ■■■ ■ ■■■ ■■■ ■■■ ■■■■■■■ ■ ■■■ ■ ■■■ ■■■■■■■■■■■ ■■■■■■■■■ ■ ■ ■■■ ■■■■■ ■ ■■■ ■ ■■■ ■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■ ■■■■■■■■■ ■■■ ■■■ ■■■ ■ ■■■■■ ■■■■■ ■■■■■ ■■■ ■ ■ ■■■ ■ ■■■■■ ■ ■■■■■■■ ■■■ ■ ■ ■■■ ■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■ ■■■■■ ■■■ ■ ■■■■■■■ ■■■ ■ ■ ■■■■■ ■■■ ■ ■■■ ■■■ ■■■ ■■■■■■■ ■ ■■■■■■■ ■ ■■■ ■■■■■ ■ ■■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■■■■■ ■■■■■■■ ■ ■ ■ ■■■ ■ ■ ■ ■■■■■■■ ■ ■ ■ ■ ■ ■■■■■ ■ ■ ■ ■ ■■■■■ ■ ■ ■■■■■ ■■■ ■■■■■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■ ■ ■ ■ ■■■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■■■ ■■■ ■ ■ ■■■■■■■ ■ ■■■■■■■■■■■ ■ ■■■■■■■ ■ ■ ■■■■■■■ ■ ■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■■■ ■ ■ ■■■ ■ ■ ■■■ ■■■■■■■■■■■■■■■ ■■■■■■■■■ ■ ■■■■■ ■ ■ ■ ■■■■■■■■■■■■■ ■■■ ■■■ ■■■ ■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■ ■ ■ ■■■■■■■■■ ■■■■■■■ ■■■■■■■■■ ■■■ ■■■ ■■■ ■■■■■ ■ ■ ■■■ ■■■ ■ ■ ■ ■■■ ■ ■ ■ ■ ■ ■ ■■■ ■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■■■■■ ■ ■■■■■■■ ■■■ ■■■ ■ ■ ■■■■■■■ ■ ■ ■■■■■■■■■■■■■■■ ■ ■ ■ ■■■■■ ■■■ ■■■■■■■ ■■■■■■■■■ ■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■■■ ■■■ ■ ■■■ ■ ■ ■ ■ ■■■■■ ■ ■ ■■■ ■■■ ■■■ ■■■ ■ ■ ■■■■■ ■ ■ ■ ■■■■■■■■■ ■ ■■■■■■■■■ ■ ■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■ ■ ■ ■■■■■ ■■■ ■ ■ ■■■ ■ ■ ■■■■■ ■■■ ■ ■■■■■■■■■■■ ■■■■■ ■■■ ■ ■ ■■■■■■■■■ ■ ■ ■■■■■ ■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■■■■■ ■■■ ■ ■ ■ ■ ■ ■■■ ■■■ ■ ■ ■■■ ■■■ ■ ■ ■■■■■■■■■■■ ■■■ ■■■■■ ■ ■■■ ■ ■ ■■■ ■ ■■■ ■■■■■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■ ■■■ ■ ■■■ ■■■■■ ■ ■ ■ ■■■ ■■■■■■■■■■■ ■ ■ ■■■■■■■■■■■■■■■ ■■■ ■■■■■ ■■■■■■■■■■■ ■■■■■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■ ■ ■ ■ ■■■ ■■■■■ ■■■■■ ■ ■ ■ ■■■ ■■■ ■ ■■■ ■■■ ■■■ ■ ■■■■■ ■■■ ■■■ ■■■ ■ ■ ■■■ ■■■■■■■■■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■■■ ■ ■ ■■■ ■ ■■■ ■ ■■■ ■■■ ■■■■■■■■■■■ ■ ■■■■■■■■■ ■■■ ■■■ ■■■■■ ■ ■■■■■■■ ■■■ ■ ■■■■■■■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■ ■■■■■■■■■ ■ ■■■ ■ ■■■■■■■ ■■■ ■■■ ■■■ ■ ■■■■■ ■■■ ■■■■■■■ ■■■■■■■■■ ■ ■ ■■■ ■■■■■■■■■ ■■■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■■■ ■■■ ■ ■ ■■■ ■■■ ■■■ ■■■ ■ ■■■ ■■■ ■■■ ■ ■ ■■■ ■ ■ ■■■ ■ ■ ■ ■ ■ ■ ■■■■■■■■■ ■ ■ ■■■■■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■ ■ ■■■ ■ ■ ■ ■■■ ■ ■ ■■■ ■■■ ■ ■ ■■■■■ ■■■ ■ ■ ■ ■■■■■■■■■■■ ■ ■ ■ ■ ■■■ ■ ■■■■■ ■■■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■■■■■■■■■■■ ■ ■ ■■■ ■ ■■■ ■■■■■ ■■■■■■■ ■■■■■ ■ ■■■■■ ■ ■ ■ ■ ■■■ ■■■■■■■■■■■■■■■■■ ■■■■■ ■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■■■■■■■ ■ ■■■ ■ ■■■ ■ ■ ■ ■■■ ■■■■■■■■■ ■ ■■■■■■■ ■■■■■■■ ■■■ ■ ■■■■■ ■ ■■■■■ ■■■ ■■■■■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■■■■■■■ ■■■ ■ ■ ■■■■■ ■ ■■■ ■■■■■■■ ■ ■■■■■ ■■■■■ ■ ■ ■ ■■■■■■■■■■■ ■ ■■■■■ ■■■■■ ■■■■■ ■■■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■ ■■■ ■■■ ■■■■■ ■■■ ■■■ ■ ■■■ ■■■ ■■■ ■ ■ ■■■ ■■■ ■ ■ ■■■■■ ■■■■■■■ ■ ■ ■ ■ ■■■■■ ■■■■■ ■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■■■■■ ■■■■■ ■ ■■■■■ ■■■ ■ ■■■ ■■■ ■ ■■■ ■ ■ ■■■■■ ■ ■ ■ ■ ■■■ ■ ■ ■■■ ■■■ ■■■■■■■ ■ ■■■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■■■ ■ ■■■ ■ ■ ■■■■■■■■■■■ ■ ■ ■ ■■■ ■ ■■■ ■■■ ■■■ ■ ■ ■■■■■■■ ■ ■■■ ■■■ ■■■■■ ■■■ ■ ■■■ ■■■ ■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■■■ ■■■■■ ■■■ ■ ■■■■■■■ ■■■ ■ ■ ■■■■■ ■ ■ ■ ■ ■ ■ ■ ■■■ ■ ■ ■ ■ ■ ■ ■■■ ■■■ ■■■■■■■■■ ■ ■ ■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■ ■ ■■■ ■■■ ■ ■ ■ ■■■ ■ ■■■ ■■■ ■ ■ ■ ■ ■■■■■ ■ ■■■ ■■■■■ ■ ■■■■■■■■■■■ ■ ■ ■ ■ ■ ■■■■■■■■■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■ ■■■■■ ■ ■ ■■■■■■■ ■ ■■■■■■■■■■■■■■■■■ ■ ■■■ ■ ■ ■ ■■■ ■ ■ ■■■■■ ■■■ ■■■■■■■ ■ ■ ■ ■■■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■■■ ■ ■■■ ■ ■ ■■■■■ ■■■■■■■ ■ ■■■ ■ ■ ■■■ ■■■ ■■■ ■ ■ ■ ■■■ ■■■ ■■■■■ ■ ■■■■■■■■■■■■■■■ ■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■ ■■■ ■■■ ■ ■ ■■■ ■■■ ■ ■ ■■■ ■■■ ■■■ ■ ■■■■■■■ ■■■■■ ■ ■■■■■ ■ ■ ■■■ ■ ■■■ ■ ■■■ ■■■■■ ■■■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■ ■■■■■■■ ■ ■ ■■■ ■■■ ■ ■ ■ ■ ■ ■ ■ ■ ■■■■■■■ ■■■ ■ ■ ■ ■ ■ ■■■ ■ ■ ■ ■■■■■ ■ ■ ■■■ ■■■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■■■ ■■■■■ ■■■■■ ■ ■■■ ■■■ ■ ■ ■■■■■ ■ ■■■ ■■■ ■■■■■■■ ■■■ ■ ■■■ ■ ■ ■ ■ ■■■ ■■■■■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■■■ ■■■■■ ■ ■■■ ■■■ ■ ■■■ ■ ■ ■■■■■■■■■ ■ ■ ■■■■■ ■ ■ ■ ■■■ ■■■■■■■■■ ■ ■ ■ ■■■ ■ ■■■ ■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■ ■■■ ■ ■■■ ■■■ ■ ■■■ ■ ■ ■ ■ ■■■■■ ■■■ ■■■■■ ■ ■ ■ ■ ■ ■ ■ ■■■ ■ ■ ■ ■■■ ■ ■ ■■■ ■■■■■ ■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■ ■ ■■■ ■■■ ■ ■■■■■■■■■ ■■■ ■ ■■■■■■■■■ ■■■■■ ■ ■■■ ■ ■■■ ■ ■■■■■ ■■■■■ ■ ■ ■■■■■ ■ ■■■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■■■■■ ■ ■ ■■■ ■ ■ ■ ■■■ ■ ■■■ ■■■■■■■ ■ ■ ■■■ ■ ■ ■ ■■■■■ ■ ■■■■■ ■■■■■■■ ■■■■■ ■■■■■■■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■■■ ■■■ ■■■ ■■■■■■■■■ ■■■■■ ■■■ ■■■■■ ■ ■■■ ■ ■ ■ ■■■ ■■■ ■ ■■■■■■■ ■■■ ■■■■■ ■■■■■■■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■ ■ ■ ■ ■ ■■■ ■ ■■■■■ ■ ■ ■ ■ ■■■■■■■ ■ ■■■■■■■ ■■■■■■■ ■ ■ ■ ■ ■ ■■■ ■ ■ ■ ■■■■■ ■■■ ■ ■ ■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■■■ ■■■ ■ ■ ■ ■■■■■ ■■■ ■ ■ ■ ■ ■ ■ ■ ■ ■■■ ■■■ ■■■■■ ■ ■■■■■■■ ■■■ ■ ■■■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■■■ ■■■ ■■■ ■■■ ■■■■■ ■ ■ ■ ■■■■■ ■■■ ■■■■■ ■ ■■■■■ ■■■■■■■ ■■■ ■■■■■■■ ■ ■ ■■■ ■■■ ■■■ ■■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■■■ ■ ■■■ ■■■ ■ ■■■ ■ ■■■ ■■■■■ ■■■■■ ■■■■■ ■■■■■ ■■■ ■■■ ■■■ ■■■■■ ■ ■ ■■■ ■ ■ ■ ■■■■■ ■ ■■E
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

View File

@ -0,0 +1,64 @@
#Телефонного справочник - связный список
#создаёт новый узел
def ll_create_node(name, phone):
return {'name': name, 'phone': phone, 'next': None}
#добавляет запись в конец списка или обновляет.
def ll_insert(head, name, phone):
new_node = ll_create_node(name, phone)
if head is None:
return new_node
if head['name'] == name:
new_node['next'] = head['next']
return new_node
current = head
while current['next'] is not None:
if current['next']['name'] == name:
new_node['next'] = current['next']['next']
current['next'] = new_node
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
#собирает все записи связного списка в список name-phone, сортирует по имени
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

@ -0,0 +1,201 @@
from builders import TextFileMazeBuilder
from strategies import BFSStrategy, DFSStrategy, AStarStrategy
from solver import MazeSolver
from observers import ConsoleView
from experiments import run_experiments, save_to_csv
import random
WALL = ''
PASSAGE = ' '
START = 'S'
EXIT = 'E'
def generate_maze_prim(size: int, filename: str, label: str):
random.seed(hash(filename) % 10000)
grid = [[WALL for _ in range(size)] for _ in range(size)]
start_x, start_y = 1, 1
grid[start_y][start_x] = PASSAGE
walls = []
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
wall_x = start_x + dx
wall_y = start_y + dy
cell_x = start_x + 2*dx
cell_y = start_y + 2*dy
if 0 <= cell_x < size and 0 <= cell_y < size:
if grid[wall_y][wall_x] == WALL:
walls.append((wall_x, wall_y, start_x, start_y, cell_x, cell_y))
while walls:
idx = random.randint(0, len(walls) - 1)
wall_x, wall_y, from_x, from_y, to_x, to_y = walls.pop(idx)
if grid[to_y][to_x] == WALL:
grid[wall_y][wall_x] = PASSAGE
grid[to_y][to_x] = PASSAGE
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
new_wall_x = to_x + dx
new_wall_y = to_y + dy
new_cell_x = to_x + 2*dx
new_cell_y = to_y + 2*dy
if 0 <= new_cell_x < size and 0 <= new_cell_y < size:
if grid[new_wall_y][new_wall_x] == WALL:
walls.append((new_wall_x, new_wall_y, to_x, to_y, new_cell_x, new_cell_y))
grid[1][1] = START
grid[size-2][size-2] = EXIT
with open(filename, 'w', encoding='utf-8') as f:
for row in grid:
f.write(''.join(row) + '\n')
print(f"{label}: {filename}")
def generate_small_maze(filename='small_maze.txt'):
maze = [
"■■■■■■■■■■",
"■S ■",
"■ ■■■■ ■ ■",
"■ ■ ■ ■ ■",
"■ ■ ■■ ■ ■",
"■ ■ ■ ■",
"■ ■■■■■■ ■",
"■ ■",
"■ ■■■■■■■■",
"■ E■",
"■■■■■■■■■■"
]
with open(filename, 'w', encoding='utf-8') as f:
f.write('\n'.join(maze))
print(f"Маленький лабиринт 10x10: {filename}")
def generate_no_exit_maze(filename='no_exit_maze.txt'):
maze = [
"■■■■■■■■■■",
"■S ■",
"■ ■■■■ ■ ■",
"■ ■ ■ ■ ■",
"■ ■ ■■ ■ ■",
"■ ■ ■ ■",
"■ ■■■■■■ ■",
"■ ■",
"■ ■■■■■■■■",
"■ ■■■■■■",
"■■■■■E■■■■"
]
with open(filename, 'w', encoding='utf-8') as f:
f.write('\n'.join(maze))
print(f"Лабиринт без выхода 10x10: {filename}")
def generate_empty_maze(filename='empty_maze.txt'):
size = 10
maze = []
maze.append(WALL * size)
for i in range(size - 2):
if i == 0:
row = WALL + START + PASSAGE * (size - 3) + WALL
elif i == size - 3:
row = WALL + PASSAGE * (size - 3) + EXIT + WALL
else:
row = WALL + PASSAGE * (size - 2) + WALL
maze.append(row)
maze.append(WALL * size)
with open(filename, 'w', encoding='utf-8') as f:
f.write('\n'.join(maze))
print(f"Пустой лабиринт 10x10: {filename}")
def main():
print("=" * 60)
print("ГЕНЕРАЦИЯ ЛАБИРИНТОВ")
print("=" * 60)
generate_small_maze('small_maze.txt')
generate_maze_prim(50, 'medium_maze.txt', 'Средний лабиринт 50x50')
generate_maze_prim(100, 'large_maze.txt', 'Большой лабиринт 100x100')
generate_no_exit_maze('no_exit_maze.txt')
generate_empty_maze('empty_maze.txt')
print("\n" + "=" * 60)
print("ДЕМОНСТРАЦИЯ НА МАЛЕНЬКОМ ЛАБИРИНТЕ")
print("=" * 60)
builder = TextFileMazeBuilder()
maze = builder.buildFromFile('small_maze.txt')
print(f"Размер: {maze.width}x{maze.height}")
print(f"Старт: ({maze.start.x}, {maze.start.y})")
print(f"Выход: ({maze.exit.x}, {maze.exit.y})")
view = ConsoleView()
solver = MazeSolver(maze)
solver.attach(view)
strategies = {
"BFS (поиск в ширину)": BFSStrategy(),
"DFS (поиск в глубину)": DFSStrategy(),
"A*": AStarStrategy()
}
for name, strat in strategies.items():
print(f"\n{'' * 40}")
print(f"Стратегия: {name}")
solver.setStrategy(strat)
path, stats = solver.solve()
if path:
print(f"Путь найден! Длина: {len(path)} шагов")
print(f" Время: {stats.time_ms:.3f} мс | Посещено: {stats.visited_count}")
view.render(maze, path=path)
else:
print("Путь не найден!")
# Эксперименты
print("\n" + "=" * 60)
print("ЭКСПЕРИМЕНТЫ НА ВСЕХ ЛАБИРИНТАХ")
print("=" * 60)
test_mazes = {}
maze_files = [
("Маленький (10x10)", "small_maze.txt"),
("Средний (50x50)", "medium_maze.txt"),
("Большой (100x100)", "large_maze.txt"),
("Без выхода (10x10)", "no_exit_maze.txt"),
("Пустой (10x10)", "empty_maze.txt")
]
for name, filename in maze_files:
try:
test_mazes[name] = builder.buildFromFile(filename)
m = test_mazes[name]
print(f"{name} загружен ({m.width}x{m.height})")
except Exception as e:
print(f"Ошибка {name}: {e}")
if test_mazes:
print(f"\nЗапуск тестов (по 3 прогона)...")
results = run_experiments(test_mazes, strategies, runs=3)
save_to_csv(results)
print("ГОТОВО! Графики: python visualize_results.py")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,16 @@
maze,strategy,time_ms,visited,path_length
Маленький (10x10),BFS (поиск в ширину),0.1492,44.0,16.0
Маленький (10x10),DFS (поиск в глубину),0.095,30.0,30.0
Маленький (10x10),A* (A-star),0.1717,39.0,16.0
Средний (50x50),BFS (поиск в ширину),3.6384,1243.0,97.0
Средний (50x50),DFS (поиск в глубину),1.9872,614.0,97.0
Средний (50x50),A* (A-star),2.048,444.0,97.0
Большой (100x100),BFS (поиск в ширину),16.6951,4997.0,213.0
Большой (100x100),DFS (поиск в глубину),10.7515,3610.0,213.0
Большой (100x100),A* (A-star),13.2112,2836.0,213.0
Без выхода (10x10),BFS (поиск в ширину),0,0,0
Без выхода (10x10),DFS (поиск в глубину),0,0,0
Без выхода (10x10),A* (A-star),0,0,0
Пустой (10x10),BFS (поиск в ширину),0.1921,64.0,15.0
Пустой (10x10),DFS (поиск в глубину),0.1362,43.0,29.0
Пустой (10x10),A* (A-star),0.2633,64.0,15.0
1 maze strategy time_ms visited path_length
2 Маленький (10x10) BFS (поиск в ширину) 0.1492 44.0 16.0
3 Маленький (10x10) DFS (поиск в глубину) 0.095 30.0 30.0
4 Маленький (10x10) A* (A-star) 0.1717 39.0 16.0
5 Средний (50x50) BFS (поиск в ширину) 3.6384 1243.0 97.0
6 Средний (50x50) DFS (поиск в глубину) 1.9872 614.0 97.0
7 Средний (50x50) A* (A-star) 2.048 444.0 97.0
8 Большой (100x100) BFS (поиск в ширину) 16.6951 4997.0 213.0
9 Большой (100x100) DFS (поиск в глубину) 10.7515 3610.0 213.0
10 Большой (100x100) A* (A-star) 13.2112 2836.0 213.0
11 Без выхода (10x10) BFS (поиск в ширину) 0 0 0
12 Без выхода (10x10) DFS (поиск в глубину) 0 0 0
13 Без выхода (10x10) A* (A-star) 0 0 0
14 Пустой (10x10) BFS (поиск в ширину) 0.1921 64.0 15.0
15 Пустой (10x10) DFS (поиск в глубину) 0.1362 43.0 29.0
16 Пустой (10x10) A* (A-star) 0.2633 64.0 15.0

View File

@ -0,0 +1,50 @@
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■S■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■■■■■ ■ ■ ■ ■ ■ ■ ■ ■ ■■■ ■■■ ■■■■■■■■■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■
■ ■■■■■ ■■■ ■■■■■ ■■■ ■■■■■ ■■■ ■ ■■■■■ ■■■■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■ ■■■■■ ■■■ ■ ■■■ ■ ■ ■■■■■ ■ ■ ■ ■■■ ■■■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■■■ ■■■■■■■ ■ ■■■ ■■■ ■ ■■■ ■■■ ■ ■■■ ■■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■■■ ■■■■■ ■■■ ■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■■■■■ ■ ■■■■■■■ ■ ■ ■ ■ ■ ■ ■■■ ■■■ ■■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■■■ ■■■■■■■ ■ ■■■ ■ ■ ■ ■ ■ ■■■ ■■■■■ ■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■ ■ ■ ■ ■ ■■■■■■■■■ ■■■ ■ ■■■■■ ■ ■■■■■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■■■■■ ■■■■■■■ ■■■ ■ ■■■ ■■■■■ ■■■■■ ■ ■ ■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■ ■■■ ■■■■■ ■■■ ■ ■■■ ■■■ ■■■■■■■ ■■■■■■■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■ ■■■ ■■■■■■■■■ ■■■ ■ ■■■ ■ ■■■■■■■ ■ ■ ■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■■■ ■ ■■■ ■ ■ ■ ■■■■■■■■■■■■■ ■ ■ ■ ■■■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■■■ ■■■ ■ ■■■■■ ■ ■ ■ ■ ■ ■■■■■ ■■■ ■■■ ■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■■■ ■ ■ ■ ■■■ ■ ■■■ ■■■■■ ■■■■■ ■■■ ■ ■ ■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■■■ ■ ■ ■ ■■■■■ ■■■ ■ ■ ■ ■■■■■ ■■■■■ ■■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■ ■■■■■■■ ■ ■ ■ ■■■■■ ■ ■■■ ■ ■■■ ■■■ ■ ■■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■ ■ ■ ■ ■■■■■ ■■■ ■ ■ ■■■ ■ ■ ■ ■ ■ ■■■■■ ■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ ■■■ ■■■ ■ ■■■ ■ ■ ■■■■■ ■■■ ■ ■ ■■■■■ ■ ■ ■■■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■■■■■ ■■■ ■ ■ ■■■ ■■■■■ ■■■■■ ■ ■■■ ■■■■■ ■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■ ■■■■■■■ ■ ■■■ ■ ■ ■■■■■■■■■ ■■■■■ ■■■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■■■■■ ■■■ ■ ■■■■■ ■■■■■ ■■■■■ ■ ■ ■■■ ■■■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■■■ ■ ■■■ ■ ■ ■ ■ ■ ■ ■■■■■ ■■■ ■■■ ■ ■■■■■■■ ■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■ ■ ■■■■■■■ ■ ■■■■■■■■■ ■■■ ■■■■■ ■ ■ ■ ■■■
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■■■ ■■■ ■ ■■■■■■■ ■ ■ ■■■ ■ ■■■ ■■■ ■ ■■■■■ ■■■ E
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

View File

@ -0,0 +1,46 @@
# maze_solver/models.py
from typing import Optional, List
class Cell:
"""Представляет одну клетку лабиринта."""
def __init__(self, x: int, y: int, is_wall: bool = False):
self.x = x
self.y = y
self.isWall = is_wall
self.isStart = False
self.isExit = False
def isPassable(self) -> bool:
"""Можно ли пройти через клетку."""
return not self.isWall
def __repr__(self):
return f"Cell({self.x}, {self.y}, Wall={self.isWall})"
class Maze:
"""Хранит полную карту лабиринта."""
def __init__(self, width: int, height: int, grid: Optional[List[List[Cell]]] = None):
self.width = width
self.height = height
self.grid = grid if grid else []
self.start: Optional[Cell] = None
self.exit: Optional[Cell] = None
def getCell(self, x: int, y: int) -> Optional[Cell]:
"""Безопасное получение клетки по координатам."""
if 0 <= x < self.width and 0 <= y < self.height:
return self.grid[y][x]
return None
def getNeighbors(self, cell: Cell) -> List[Cell]:
"""Возвращает список соседних ПРОХОДИМЫХ клеток."""
neighbors = []
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] # up, down, left, right
for dx, dy in directions:
nx, ny = cell.x + dx, cell.y + dy
neighbor = self.getCell(nx, ny)
if neighbor and neighbor.isPassable():
neighbors.append(neighbor)
return neighbors

View File

@ -0,0 +1,11 @@
■■■■■■■■■■
■S ■
■ ■■■■ ■ ■
■ ■ ■ ■ ■
■ ■ ■■ ■ ■
■ ■ ■ ■
■ ■■■■■■ ■
■ ■
■ ■■■■■■■■
■ ■■■■■■
■■■■■E■■■■

View File

@ -0,0 +1,60 @@
import os
from abc import ABC, abstractmethod
from typing import Optional, List
from models import Maze, Cell
class Observer(ABC):
@abstractmethod
def update(self, event: str, data=None):
pass
class ConsoleView(Observer):
WALL_CHAR = ''
START_CHAR = 'S'
EXIT_CHAR = 'E'
PATH_CHAR = ''
PLAYER_CHAR = 'P'
PASSAGE_CHAR = ' '
def update(self, event: str, data=None):
if event == "search_started":
print(f"\n Запущен поиск с помощью: {data['strategy']}")
elif event == "path_found":
stats = data['stats']
print(f" Путь найден! {stats}")
elif event == "path_not_found":
stats = data['stats']
print(f" Путь НЕ найден. {stats}")
elif event == "step":
self.render(data['maze'], data['current'], data.get('path'))
@staticmethod
def render(maze: Maze, player_pos: Optional[Cell] = None, path: Optional[List[Cell]] = None):
os.system('cls' if os.name == 'nt' else 'clear')
path_set = set(path) if path else set()
print("" + "" * maze.width + "")
for y in range(maze.height):
row_str = ""
for x in range(maze.width):
cell = maze.getCell(x, y)
if player_pos and cell == player_pos:
row_str += ConsoleView.PLAYER_CHAR
elif cell.isStart:
row_str += ConsoleView.START_CHAR
elif cell.isExit:
row_str += ConsoleView.EXIT_CHAR
elif cell in path_set:
row_str += ConsoleView.PATH_CHAR
elif cell.isWall:
row_str += ConsoleView.WALL_CHAR
else:
row_str += ConsoleView.PASSAGE_CHAR
row_str += ""
print(row_str)
print("" + "" * maze.width + "")

View File

@ -0,0 +1,91 @@
Структура,Режим,Операция,Время (сек)
LinkedList,случайный,вставка,7.0379601
HashTable,случайный,вставка,0.06370939999999958
BST,случайный,вставка,0.027558299999999925
LinkedList,случайный,поиск,0.05380779999999952
HashTable,случайный,поиск,0.0008359000000002226
BST,случайный,поиск,0.00036180000000030077
LinkedList,случайный,удаление,0.03279429999999994
HashTable,случайный,удаление,0.00046319999999955286
BST,случайный,удаление,0.0002371000000005452
LinkedList,отсортированный,вставка,6.794934100000001
HashTable,отсортированный,вставка,0.06352280000000121
BST,отсортированный,вставка,6.0836668
LinkedList,отсортированный,поиск,0.06371549999999715
HashTable,отсортированный,поиск,0.0013053000000020631
BST,отсортированный,поиск,0.05756839999999741
LinkedList,отсортированный,удаление,0.038222600000000995
HashTable,отсортированный,удаление,0.0011298000000010688
BST,отсортированный,удаление,0.036374399999999696
LinkedList,случайный,вставка,7.183893999999999
HashTable,случайный,вставка,0.06642779999999959
BST,случайный,вставка,0.025029599999999874
LinkedList,случайный,поиск,0.05042710000000028
HashTable,случайный,поиск,0.0008175000000001376
BST,случайный,поиск,0.00032500000000013074
LinkedList,случайный,удаление,0.04681619999999853
HashTable,случайный,удаление,0.0006166999999983602
BST,случайный,удаление,0.0002557000000003029
LinkedList,отсортированный,вставка,7.153371900000003
HashTable,отсортированный,вставка,0.05732309999999785
BST,отсортированный,вставка,6.7899777999999955
LinkedList,отсортированный,поиск,0.1498364000000052
HashTable,отсортированный,поиск,0.0021344999999968195
BST,отсортированный,поиск,0.08021600000000007
LinkedList,отсортированный,удаление,0.04531419999999997
HashTable,отсортированный,удаление,0.0005183999999971434
BST,отсортированный,удаление,0.032904900000005455
LinkedList,случайный,вставка,7.787066500000002
HashTable,случайный,вставка,0.06794790000000006
BST,случайный,вставка,0.028658900000003484
LinkedList,случайный,поиск,0.055633000000000266
HashTable,случайный,поиск,0.0011372000000022808
BST,случайный,поиск,0.00041319999999700485
LinkedList,случайный,удаление,0.04464529999999911
HashTable,случайный,удаление,0.0006264000000015812
BST,случайный,удаление,0.00025480000000044356
LinkedList,отсортированный,вставка,7.047079400000001
HashTable,отсортированный,вставка,0.07149469999999525
BST,отсортированный,вставка,6.004278499999998
LinkedList,отсортированный,поиск,0.059245700000005286
HashTable,отсортированный,поиск,0.0008623000000014258
BST,отсортированный,поиск,0.061085500000004345
LinkedList,отсортированный,удаление,0.038738300000005665
HashTable,отсортированный,удаление,0.00047229999999842676
BST,отсортированный,удаление,0.03476669999999871
LinkedList,случайный,вставка,7.814853800000002
HashTable,случайный,вставка,0.06793270000000007
BST,случайный,вставка,0.026476199999990513
LinkedList,случайный,поиск,0.056167000000002076
HashTable,случайный,поиск,0.0007876000000095473
BST,случайный,поиск,0.0003126000000008844
LinkedList,случайный,удаление,0.042319900000009625
HashTable,случайный,удаление,0.000520099999988588
BST,случайный,удаление,0.00023889999999937572
LinkedList,отсортированный,вставка,7.297540700000013
HashTable,отсортированный,вставка,0.06405399999999872
BST,отсортированный,вставка,6.252882799999995
LinkedList,отсортированный,поиск,0.058841400000005706
HashTable,отсортированный,поиск,0.0008604000000076439
BST,отсортированный,поиск,0.05284110000000908
LinkedList,отсортированный,удаление,0.03360689999999522
HashTable,отсортированный,удаление,0.00047010000000113905
BST,отсортированный,удаление,0.02865070000000003
LinkedList,случайный,вставка,7.937439900000001
HashTable,случайный,вставка,0.06798210000000893
BST,случайный,вставка,0.042214500000000044
LinkedList,случайный,поиск,0.0645776000000069
HashTable,случайный,поиск,0.0007535999999959131
BST,случайный,поиск,0.0003451000000040949
LinkedList,случайный,удаление,0.04297359999999628
HashTable,случайный,удаление,0.0005001999999905138
BST,случайный,удаление,0.00021639999999933934
LinkedList,отсортированный,вставка,7.474806200000003
HashTable,отсортированный,вставка,0.06952760000000069
BST,отсортированный,вставка,6.475523199999998
LinkedList,отсортированный,поиск,0.054521199999996384
HashTable,отсортированный,поиск,0.0008888999999925318
BST,отсортированный,поиск,0.049161900000001424
LinkedList,отсортированный,удаление,0.03957100000000935
HashTable,отсортированный,удаление,0.0004850999999916894
BST,отсортированный,удаление,0.03728519999999946
1 Структура Режим Операция Время (сек)
2 LinkedList случайный вставка 7.0379601
3 HashTable случайный вставка 0.06370939999999958
4 BST случайный вставка 0.027558299999999925
5 LinkedList случайный поиск 0.05380779999999952
6 HashTable случайный поиск 0.0008359000000002226
7 BST случайный поиск 0.00036180000000030077
8 LinkedList случайный удаление 0.03279429999999994
9 HashTable случайный удаление 0.00046319999999955286
10 BST случайный удаление 0.0002371000000005452
11 LinkedList отсортированный вставка 6.794934100000001
12 HashTable отсортированный вставка 0.06352280000000121
13 BST отсортированный вставка 6.0836668
14 LinkedList отсортированный поиск 0.06371549999999715
15 HashTable отсортированный поиск 0.0013053000000020631
16 BST отсортированный поиск 0.05756839999999741
17 LinkedList отсортированный удаление 0.038222600000000995
18 HashTable отсортированный удаление 0.0011298000000010688
19 BST отсортированный удаление 0.036374399999999696
20 LinkedList случайный вставка 7.183893999999999
21 HashTable случайный вставка 0.06642779999999959
22 BST случайный вставка 0.025029599999999874
23 LinkedList случайный поиск 0.05042710000000028
24 HashTable случайный поиск 0.0008175000000001376
25 BST случайный поиск 0.00032500000000013074
26 LinkedList случайный удаление 0.04681619999999853
27 HashTable случайный удаление 0.0006166999999983602
28 BST случайный удаление 0.0002557000000003029
29 LinkedList отсортированный вставка 7.153371900000003
30 HashTable отсортированный вставка 0.05732309999999785
31 BST отсортированный вставка 6.7899777999999955
32 LinkedList отсортированный поиск 0.1498364000000052
33 HashTable отсортированный поиск 0.0021344999999968195
34 BST отсортированный поиск 0.08021600000000007
35 LinkedList отсортированный удаление 0.04531419999999997
36 HashTable отсортированный удаление 0.0005183999999971434
37 BST отсортированный удаление 0.032904900000005455
38 LinkedList случайный вставка 7.787066500000002
39 HashTable случайный вставка 0.06794790000000006
40 BST случайный вставка 0.028658900000003484
41 LinkedList случайный поиск 0.055633000000000266
42 HashTable случайный поиск 0.0011372000000022808
43 BST случайный поиск 0.00041319999999700485
44 LinkedList случайный удаление 0.04464529999999911
45 HashTable случайный удаление 0.0006264000000015812
46 BST случайный удаление 0.00025480000000044356
47 LinkedList отсортированный вставка 7.047079400000001
48 HashTable отсортированный вставка 0.07149469999999525
49 BST отсортированный вставка 6.004278499999998
50 LinkedList отсортированный поиск 0.059245700000005286
51 HashTable отсортированный поиск 0.0008623000000014258
52 BST отсортированный поиск 0.061085500000004345
53 LinkedList отсортированный удаление 0.038738300000005665
54 HashTable отсортированный удаление 0.00047229999999842676
55 BST отсортированный удаление 0.03476669999999871
56 LinkedList случайный вставка 7.814853800000002
57 HashTable случайный вставка 0.06793270000000007
58 BST случайный вставка 0.026476199999990513
59 LinkedList случайный поиск 0.056167000000002076
60 HashTable случайный поиск 0.0007876000000095473
61 BST случайный поиск 0.0003126000000008844
62 LinkedList случайный удаление 0.042319900000009625
63 HashTable случайный удаление 0.000520099999988588
64 BST случайный удаление 0.00023889999999937572
65 LinkedList отсортированный вставка 7.297540700000013
66 HashTable отсортированный вставка 0.06405399999999872
67 BST отсортированный вставка 6.252882799999995
68 LinkedList отсортированный поиск 0.058841400000005706
69 HashTable отсортированный поиск 0.0008604000000076439
70 BST отсортированный поиск 0.05284110000000908
71 LinkedList отсортированный удаление 0.03360689999999522
72 HashTable отсортированный удаление 0.00047010000000113905
73 BST отсортированный удаление 0.02865070000000003
74 LinkedList случайный вставка 7.937439900000001
75 HashTable случайный вставка 0.06798210000000893
76 BST случайный вставка 0.042214500000000044
77 LinkedList случайный поиск 0.0645776000000069
78 HashTable случайный поиск 0.0007535999999959131
79 BST случайный поиск 0.0003451000000040949
80 LinkedList случайный удаление 0.04297359999999628
81 HashTable случайный удаление 0.0005001999999905138
82 BST случайный удаление 0.00021639999999933934
83 LinkedList отсортированный вставка 7.474806200000003
84 HashTable отсортированный вставка 0.06952760000000069
85 BST отсортированный вставка 6.475523199999998
86 LinkedList отсортированный поиск 0.054521199999996384
87 HashTable отсортированный поиск 0.0008888999999925318
88 BST отсортированный поиск 0.049161900000001424
89 LinkedList отсортированный удаление 0.03957100000000935
90 HashTable отсортированный удаление 0.0004850999999916894
91 BST отсортированный удаление 0.03728519999999946

View File

@ -0,0 +1,11 @@
■■■■■■■■■■
■S ■
■ ■■■■ ■ ■
■ ■ ■ ■ ■
■ ■ ■■ ■ ■
■ ■ ■ ■
■ ■■■■■■ ■
■ ■
■ ■■■■■■■■
■ E■
■■■■■■■■■■

View File

@ -0,0 +1,61 @@
import time
from typing import List, Tuple, Optional
from models import Maze, Cell
from strategies import PathFindingStrategy
class SearchStats:
def __init__(self, time_ms: float, visited: int, path_length: int):
self.time_ms = time_ms
self.visited_count = visited
self.path_length = path_length
def __str__(self):
return (f"Время: {self.time_ms:.4f} мс | "
f"Посещено: {self.visited_count} | "
f"Длина пути: {self.path_length}")
class MazeSolver:
def __init__(self, maze: Maze, strategy: Optional[PathFindingStrategy] = None):
self.maze = maze
self.strategy = strategy
self._observers = []
def setStrategy(self, strategy: PathFindingStrategy):
self.strategy = strategy
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
def _notify(self, event: str, data=None):
for observer in self._observers:
observer.update(event, data)
def solve(self) -> Tuple[List[Cell], SearchStats]:
if not self.strategy:
raise ValueError("Стратегия поиска не установлена.")
if not self.maze.start or not self.maze.exit:
raise ValueError("В лабиринте не определены старт или выход.")
self._notify("search_started", {"strategy": type(self.strategy).__name__})
start_time = time.perf_counter()
path = self.strategy.findPath(self.maze, self.maze.start, self.maze.exit)
end_time = time.perf_counter()
elapsed_ms = (end_time - start_time) * 1000
visited = self.strategy.visited_count
path_len = len(path)
stats = SearchStats(elapsed_ms, visited, path_len)
if path:
self._notify("path_found", {"path": path, "stats": stats})
else:
self._notify("path_not_found", {"stats": stats})
return path, stats

View File

@ -0,0 +1,104 @@
from abc import ABC, abstractmethod
from collections import deque
import heapq
from typing import List, Dict, Optional
from models import Cell, Maze
class PathFindingStrategy(ABC):
def __init__(self):
self.visited_count = 0
@abstractmethod
def findPath(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]:
pass
def _reconstruct_path(self, parent: Dict[Cell, Optional[Cell]], current: Cell) -> List[Cell]:
path = []
while current:
path.append(current)
current = parent.get(current)
return path[::-1]
class BFSStrategy(PathFindingStrategy):
def findPath(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]:
self.visited_count = 0
queue = deque([start])
visited = {start}
parent = {start: None}
while queue:
current = queue.popleft()
self.visited_count += 1
if current == exit:
return self._reconstruct_path(parent, current)
for neighbor in maze.getNeighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
queue.append(neighbor)
return []
class DFSStrategy(PathFindingStrategy):
def findPath(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]:
self.visited_count = 0
stack = [start]
visited = {start}
parent = {start: None}
while stack:
current = stack.pop()
self.visited_count += 1
if current == exit:
return self._reconstruct_path(parent, current)
for neighbor in maze.getNeighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
stack.append(neighbor)
return []
class AStarStrategy(PathFindingStrategy):
@staticmethod
def _heuristic(a: Cell, b: Cell) -> int:
return abs(a.x - b.x) + abs(a.y - b.y)
def findPath(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]:
self.visited_count = 0
counter = 0
open_set = []
heapq.heappush(open_set, (0, counter, start))
counter += 1
came_from = {start: None}
g_score = {start: 0}
visited = set()
while open_set:
_, _, current = heapq.heappop(open_set)
self.visited_count += 1
if current == exit:
return self._reconstruct_path(came_from, current)
if current in visited:
continue
visited.add(current)
for neighbor in maze.getNeighbors(current):
tentative_g_score = g_score[current] + 1
if neighbor not in g_score or tentative_g_score < g_score[neighbor]:
came_from[neighbor] = current
g_score[neighbor] = tentative_g_score
f_score = tentative_g_score + self._heuristic(neighbor, exit)
heapq.heappush(open_set, (f_score, counter, neighbor))
counter += 1
return []

View File

@ -0,0 +1,11 @@
##########
#S #
# ##### #
# # # #
# # # # ##
# # # #
##### # ##
# # #
# ##### #
# ######
#####E####

View File

@ -0,0 +1,163 @@
import matplotlib.pyplot as plt
import numpy as np
import csv
from typing import Dict, List
import os
plt.style.use('seaborn-v0_8-darkgrid')
def load_results(filename: str = 'maze_experiment_results.csv') -> List[Dict]:
if not os.path.exists(filename):
print(f"Файл {filename} не найден! Сначала запустите main.py")
return []
with open(filename, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
results = []
for row in reader:
row['time_ms'] = float(row['time_ms'])
row['visited'] = int(float(row['visited']))
row['path_length'] = int(float(row['path_length']))
results.append(row)
return results
def organize_data(results: List[Dict]) -> Dict:
data = {}
for row in results:
maze = row['maze']
strategy = row['strategy'].split('(')[0].strip()
if maze not in data:
data[maze] = {}
if strategy not in data[maze]:
data[maze][strategy] = {'time_ms': [], 'visited': [], 'path_length': []}
data[maze][strategy]['time_ms'].append(row['time_ms'])
data[maze][strategy]['visited'].append(row['visited'])
data[maze][strategy]['path_length'].append(row['path_length'])
return data
def create_main_chart(data: Dict, save_dir: str = 'charts'):
if not os.path.exists(save_dir):
os.makedirs(save_dir)
maze_names = list(data.keys())
strategies = ['BFS', 'DFS', 'A*']
colors = {'BFS': '#3498db', 'DFS': '#e74c3c', 'A*': '#2ecc71'}
fig, axes = plt.subplots(1, 3, figsize=(16, 6))
fig.suptitle('Сравнение алгоритмов поиска пути', fontsize=16, fontweight='bold')
x = np.arange(len(maze_names))
width = 0.25
# График 1: Время
for i, strat in enumerate(strategies):
values = []
for maze in maze_names:
if strat in data[maze] and data[maze][strat]['time_ms']:
values.append(np.mean(data[maze][strat]['time_ms']))
else:
values.append(0)
bars = axes[0].bar(x + i * width, values, width, label=strat,
color=colors[strat], edgecolor='black', linewidth=0.5)
for bar, val in zip(bars, values):
if val > 0:
axes[0].text(bar.get_x() + bar.get_width()/2., bar.get_height(),
f'{val:.1f}', ha='center', va='bottom', fontsize=7)
axes[0].set_title('Время выполнения (мс)', fontweight='bold')
axes[0].set_xticks(x + width)
axes[0].set_xticklabels([n.split('(')[0].strip() for n in maze_names], rotation=15, fontsize=8)
axes[0].legend(fontsize=8)
# График 2: Посещённые клетки
for i, strat in enumerate(strategies):
values = []
for maze in maze_names:
if strat in data[maze] and data[maze][strat]['visited']:
values.append(np.mean(data[maze][strat]['visited']))
else:
values.append(0)
bars = axes[1].bar(x + i * width, values, width, label=strat,
color=colors[strat], edgecolor='black', linewidth=0.5)
for bar, val in zip(bars, values):
if val > 0:
axes[1].text(bar.get_x() + bar.get_width()/2., bar.get_height(),
f'{int(val)}', ha='center', va='bottom', fontsize=7)
axes[1].set_title('Посещённые клетки', fontweight='bold')
axes[1].set_xticks(x + width)
axes[1].set_xticklabels([n.split('(')[0].strip() for n in maze_names], rotation=15, fontsize=8)
# График 3: Длина пути
for i, strat in enumerate(strategies):
values = []
for maze in maze_names:
if strat in data[maze] and data[maze][strat]['path_length']:
values.append(np.mean(data[maze][strat]['path_length']))
else:
values.append(0)
bars = axes[2].bar(x + i * width, values, width, label=strat,
color=colors[strat], edgecolor='black', linewidth=0.5)
for bar, val in zip(bars, values):
if val > 0:
axes[2].text(bar.get_x() + bar.get_width()/2., bar.get_height(),
f'{int(val)}', ha='center', va='bottom', fontsize=7)
axes[2].set_title('Длина пути (шагов)', fontweight='bold')
axes[2].set_xticks(x + width)
axes[2].set_xticklabels([n.split('(')[0].strip() for n in maze_names], rotation=15, fontsize=8)
plt.tight_layout()
filepath = os.path.join(save_dir, 'comparison_all_metrics.png')
plt.savefig(filepath, dpi=150, bbox_inches='tight')
plt.close()
print(f" График сохранён: {filepath}")
def print_table(data: Dict):
print("\n" + "=" * 80)
print("РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ")
print("=" * 80)
for maze_name, strategies in data.items():
print(f"\n{maze_name}")
print("-" * 60)
print(f"{'Алгоритм':<10} {'Время(мс)':<12} {'Посещено':<12} {'Длина пути':<12}")
print("-" * 46)
for strat in ['BFS', 'DFS', 'A*']:
if strat in strategies and strategies[strat]['time_ms']:
t = np.mean(strategies[strat]['time_ms'])
v = np.mean(strategies[strat]['visited'])
p = np.mean(strategies[strat]['path_length'])
print(f"{strat:<10} {t:<12.2f} {int(v):<12} {int(p):<12}")
def main():
print("=" * 60)
print("ВИЗУАЛИЗАЦИЯ РЕЗУЛЬТАТОВ")
print("=" * 60)
results = load_results('maze_experiment_results.csv')
if not results:
print("Нет данных. Запустите сначала main.py")
return
data = organize_data(results)
print_table(data)
# Один график
print("\nСоздание графика...")
create_main_chart(data)
if __name__ == "__main__":
main()

BIN
groshevava/docs/task_1.doc Normal file

Binary file not shown.

BIN
groshevava/docs/task_2.doc Normal file

Binary file not shown.