[2] добавлен скрипт для сравнения алгоритмов
This commit is contained in:
parent
d59cd16706
commit
4ce7536bbf
240
pogodinda/lab2/src/experiments.py
Normal file
240
pogodinda/lab2/src/experiments.py
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
import csv
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from maze import Maze, Cell
|
||||||
|
from maze_builder import RandomMazeBuilder
|
||||||
|
from pathfinding import BFSStrategy, DFSStrategy, AStarStrategy, DijkstraStrategy
|
||||||
|
from maze_solver import MazeSolver, SearchStats
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_mazes() -> Dict[str, Maze]:
|
||||||
|
"""Создаёт тестовые лабиринты разной сложности."""
|
||||||
|
mazes = {}
|
||||||
|
|
||||||
|
# 1. Маленький 10×10 с простым путём
|
||||||
|
small = Maze(10, 10)
|
||||||
|
for y in range(10):
|
||||||
|
for x in range(10):
|
||||||
|
is_wall = (x == 0 or x == 9 or y == 0 or y == 9 or
|
||||||
|
(x == 3 and y < 7) or (x == 6 and y > 2))
|
||||||
|
is_start = (x == 1 and y == 1)
|
||||||
|
is_exit = (x == 8 and y == 8)
|
||||||
|
small.set_cell(x, y, Cell(x, y, is_wall=is_wall,
|
||||||
|
is_start=is_start, is_exit=is_exit))
|
||||||
|
mazes['small_10x10'] = small
|
||||||
|
|
||||||
|
# 2. Средний 50×50 — случайный
|
||||||
|
mazes['medium_50x50'] = RandomMazeBuilder(51, 51).build_from_file()
|
||||||
|
|
||||||
|
# 3. Большой 100×100 — случайный
|
||||||
|
mazes['large_100x100'] = RandomMazeBuilder(101, 101).build_from_file()
|
||||||
|
|
||||||
|
# 4. Пустой 20×20 — без стен
|
||||||
|
empty = Maze(20, 20)
|
||||||
|
for y in range(20):
|
||||||
|
for x in range(20):
|
||||||
|
empty.set_cell(x, y, Cell(x, y, is_wall=False,
|
||||||
|
is_start=(x==1 and y==1),
|
||||||
|
is_exit=(x==18 and y==18)))
|
||||||
|
mazes['empty_20x20'] = empty
|
||||||
|
|
||||||
|
# 5. Без выхода — проверка обработки
|
||||||
|
no_exit = Maze(10, 10)
|
||||||
|
for y in range(10):
|
||||||
|
for x in range(10):
|
||||||
|
is_wall = (x == 0 or x == 9 or y == 0 or y == 9 or x == 5)
|
||||||
|
no_exit.set_cell(x, y, Cell(x, y, is_wall=is_wall,
|
||||||
|
is_start=(x==1 and y==1)))
|
||||||
|
mazes['no_exit_10x10'] = no_exit
|
||||||
|
|
||||||
|
# 6. Взвешенный 15×15
|
||||||
|
weighted = Maze(15, 15)
|
||||||
|
for y in range(15):
|
||||||
|
for x in range(15):
|
||||||
|
is_wall = (x == 0 or x == 14 or y == 0 or y == 14 or
|
||||||
|
(x == 5 and y != 7) or (x == 10 and y != 3))
|
||||||
|
weight = 1
|
||||||
|
if 3 <= x <= 7 and 3 <= y <= 7:
|
||||||
|
weight = 2 # песок
|
||||||
|
elif 8 <= x <= 12 and 8 <= y <= 12:
|
||||||
|
weight = 3 # болото
|
||||||
|
|
||||||
|
weighted.set_cell(x, y, Cell(x, y,
|
||||||
|
is_wall=is_wall,
|
||||||
|
is_start=(x==1 and y==1),
|
||||||
|
is_exit=(x==13 and y==13),
|
||||||
|
weight=weight))
|
||||||
|
mazes['weighted_15x15'] = weighted
|
||||||
|
|
||||||
|
return mazes
|
||||||
|
|
||||||
|
|
||||||
|
def run_experiments(mazes: Dict[str, Maze],
|
||||||
|
strategies: List,
|
||||||
|
runs_per_test: int = 5) -> List[SearchStats]:
|
||||||
|
"""Запускает эксперименты, возвращает список статистик."""
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for maze_name, maze in mazes.items():
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f"Лабиринт: {maze_name} ({maze.width}x{maze.height})")
|
||||||
|
print(f"{'='*60}")
|
||||||
|
|
||||||
|
for strategy in strategies:
|
||||||
|
print(f"\nАлгоритм: {strategy.get_name()}")
|
||||||
|
|
||||||
|
times = []
|
||||||
|
visited_list = []
|
||||||
|
path_lens = []
|
||||||
|
|
||||||
|
solver = MazeSolver(maze, strategy)
|
||||||
|
|
||||||
|
for run in range(runs_per_test):
|
||||||
|
stats = solver.solve(maze_name)
|
||||||
|
times.append(stats.time_ms)
|
||||||
|
visited_list.append(stats.visited_cells)
|
||||||
|
path_lens.append(stats.path_length)
|
||||||
|
|
||||||
|
print(f" Запуск {run+1}: {stats.time_ms:.4f} мс, "
|
||||||
|
f"посещено: {stats.visited_cells}, "
|
||||||
|
f"длина: {stats.path_length}")
|
||||||
|
|
||||||
|
# Средние значения
|
||||||
|
avg = SearchStats(
|
||||||
|
time_ms=np.mean(times),
|
||||||
|
visited_cells=int(np.mean(visited_list)),
|
||||||
|
path_length=int(np.mean(path_lens)),
|
||||||
|
algorithm_name=strategy.get_name(),
|
||||||
|
maze_name=maze_name
|
||||||
|
)
|
||||||
|
results.append(avg)
|
||||||
|
|
||||||
|
print(f" СРЕДНЕЕ: {avg.time_ms:.4f} мс, "
|
||||||
|
f"посещено: {avg.visited_cells}, "
|
||||||
|
f"длина: {avg.path_length}")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def save_csv(results: List[SearchStats], filename: str):
|
||||||
|
"""Сохраняет результаты в CSV."""
|
||||||
|
with open(filename, 'w', newline='', encoding='utf-8') as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerow(['Лабиринт', 'Алгоритм', 'Время_мс',
|
||||||
|
'Посещено_клеток', 'Длина_пути'])
|
||||||
|
for r in results:
|
||||||
|
writer.writerow([
|
||||||
|
r.maze_name, r.algorithm_name,
|
||||||
|
f"{r.time_ms:.6f}", r.visited_cells, r.path_length
|
||||||
|
])
|
||||||
|
print(f"\nCSV сохранён: {filename}")
|
||||||
|
|
||||||
|
|
||||||
|
def create_plots(results: List[SearchStats], output_dir: str = "."):
|
||||||
|
"""Создаёт графики сравнения."""
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
maze_names = sorted(set(r.maze_name for r in results))
|
||||||
|
algorithms = sorted(set(r.algorithm_name for r in results))
|
||||||
|
|
||||||
|
# 1. Общее сравнение по лабиринтам
|
||||||
|
fig, axes = plt.subplots(2, 3, figsize=(18, 12))
|
||||||
|
fig.suptitle('Сравнение алгоритмов поиска пути', fontsize=16)
|
||||||
|
|
||||||
|
for idx, maze_name in enumerate(maze_names):
|
||||||
|
ax = axes[idx // 3, idx % 3]
|
||||||
|
maze_res = [r for r in results if r.maze_name == maze_name]
|
||||||
|
|
||||||
|
names = [r.algorithm_name for r in maze_res]
|
||||||
|
times = [r.time_ms for r in maze_res]
|
||||||
|
visited = [r.visited_cells for r in maze_res]
|
||||||
|
paths = [r.path_length for r in maze_res]
|
||||||
|
|
||||||
|
x = np.arange(len(names))
|
||||||
|
w = 0.25
|
||||||
|
|
||||||
|
ax.bar(x - w, times, w, label='Время (мс)', color='skyblue')
|
||||||
|
ax.bar(x, [v/10 for v in visited], w,
|
||||||
|
label='Посещено (÷10)', color='lightcoral')
|
||||||
|
ax.bar(x + w, paths, w, label='Длина пути', color='lightgreen')
|
||||||
|
|
||||||
|
ax.set_title(maze_name)
|
||||||
|
ax.set_xticks(x)
|
||||||
|
ax.set_xticklabels(names, rotation=45)
|
||||||
|
ax.legend(fontsize=8)
|
||||||
|
ax.grid(True, alpha=0.3)
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(f'{output_dir}/maze_comparison.png', dpi=150)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
# 2. Посещённые клетки
|
||||||
|
fig, ax = plt.subplots(figsize=(14, 8))
|
||||||
|
for algo in algorithms:
|
||||||
|
algo_res = [r for r in results if r.algorithm_name == algo]
|
||||||
|
names = [r.maze_name for r in algo_res]
|
||||||
|
vals = [r.visited_cells for r in algo_res]
|
||||||
|
ax.plot(names, vals, marker='o', label=algo, linewidth=2)
|
||||||
|
|
||||||
|
ax.set_title('Посещённые клетки по лабиринтам', fontsize=14)
|
||||||
|
ax.set_xlabel('Лабиринт')
|
||||||
|
ax.set_ylabel('Клетки')
|
||||||
|
ax.legend()
|
||||||
|
ax.grid(True, alpha=0.3)
|
||||||
|
plt.xticks(rotation=45)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(f'{output_dir}/visited_cells.png', dpi=150)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
# 3. Время выполнения (логарифмическая шкала)
|
||||||
|
fig, ax = plt.subplots(figsize=(14, 8))
|
||||||
|
for algo in algorithms:
|
||||||
|
algo_res = [r for r in results if r.algorithm_name == algo]
|
||||||
|
names = [r.maze_name for r in algo_res]
|
||||||
|
vals = [max(r.time_ms, 0.001) for r in algo_res]
|
||||||
|
ax.plot(names, vals, marker='s', label=algo, linewidth=2)
|
||||||
|
|
||||||
|
ax.set_title('Время выполнения (лог. шкала)', fontsize=14)
|
||||||
|
ax.set_xlabel('Лабиринт')
|
||||||
|
ax.set_ylabel('Время (мс)')
|
||||||
|
ax.set_yscale('log')
|
||||||
|
ax.legend()
|
||||||
|
ax.grid(True, alpha=0.3, which='both')
|
||||||
|
plt.xticks(rotation=45)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(f'{output_dir}/time_comparison.png', dpi=150)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
print(f"Графики сохранены в {output_dir}/")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("=" * 70)
|
||||||
|
print("ЭКСПЕРИМЕНТАЛЬНАЯ ЧАСТЬ")
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
# Создаём лабиринты
|
||||||
|
mazes = create_test_mazes()
|
||||||
|
|
||||||
|
# Алгоритмы для сравнения
|
||||||
|
strategies = [BFSStrategy(), DFSStrategy(),
|
||||||
|
AStarStrategy(), DijkstraStrategy()]
|
||||||
|
|
||||||
|
# Запускаем эксперименты
|
||||||
|
results = run_experiments(mazes, strategies, runs_per_test=5)
|
||||||
|
|
||||||
|
# Сохраняем CSV
|
||||||
|
save_csv(results, "experiment_results.csv")
|
||||||
|
|
||||||
|
# Строим графики
|
||||||
|
create_plots(results)
|
||||||
|
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("ГОТОВО!")
|
||||||
|
print("=" * 70)
|
||||||
Loading…
Reference in New Issue
Block a user