add experiment

This commit is contained in:
SobolevNS 2026-05-22 13:46:32 +03:00
parent 313fe75c36
commit 0f03b4edd4
7 changed files with 409 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -0,0 +1,228 @@
лабиринт,стратегия,trial,время_мс,посещено_клеток,длина_пути,стоимость_пути
small_10x10,BFS,1,0.0454,34,16,16
small_10x10,BFS,2,0.0314,34,16,16
small_10x10,BFS,3,0.0286,34,16,16
small_10x10,BFS,4,0.0275,34,16,16
small_10x10,BFS,5,0.0270,34,16,16
small_10x10,BFS,6,0.0264,34,16,16
small_10x10,BFS,7,0.0279,34,16,16
small_10x10,DFS,1,0.0167,18,16,16
small_10x10,DFS,2,0.0146,18,16,16
small_10x10,DFS,3,0.0138,18,16,16
small_10x10,DFS,4,0.0132,18,16,16
small_10x10,DFS,5,0.0138,18,16,16
small_10x10,DFS,6,0.0133,18,16,16
small_10x10,DFS,7,0.0138,18,16,16
small_10x10,A*,1,0.0585,27,16,16
small_10x10,A*,2,0.0514,27,16,16
small_10x10,A*,3,0.0386,27,16,16
small_10x10,A*,4,0.0366,27,16,16
small_10x10,A*,5,0.0367,27,16,16
small_10x10,A*,6,0.0367,27,16,16
small_10x10,A*,7,0.0356,27,16,16
small_10x10,Dijkstra,1,0.0467,33,16,16
small_10x10,Dijkstra,2,0.0409,33,16,16
small_10x10,Dijkstra,3,0.0395,33,16,16
small_10x10,Dijkstra,4,0.0396,33,16,16
small_10x10,Dijkstra,5,0.0642,33,16,16
small_10x10,Dijkstra,6,0.0404,33,16,16
small_10x10,Dijkstra,7,0.0392,33,16,16
medium_51x51,BFS,1,0.5159,524,353,353
medium_51x51,BFS,2,0.5299,524,353,353
medium_51x51,BFS,3,0.5232,524,353,353
medium_51x51,BFS,4,0.4525,524,353,353
medium_51x51,BFS,5,0.4667,524,353,353
medium_51x51,BFS,6,0.4594,524,353,353
medium_51x51,BFS,7,0.4886,524,353,353
medium_51x51,DFS,1,0.3356,379,353,353
medium_51x51,DFS,2,0.3270,379,353,353
medium_51x51,DFS,3,0.3471,379,353,353
medium_51x51,DFS,4,0.3235,379,353,353
medium_51x51,DFS,5,0.3309,379,353,353
medium_51x51,DFS,6,0.3856,379,353,353
medium_51x51,DFS,7,0.3248,379,353,353
medium_51x51,A*,1,0.8707,421,353,353
medium_51x51,A*,2,0.6813,421,353,353
medium_51x51,A*,3,0.6357,421,353,353
medium_51x51,A*,4,0.6464,421,353,353
medium_51x51,A*,5,0.6520,421,353,353
medium_51x51,A*,6,0.6231,421,353,353
medium_51x51,A*,7,0.6365,421,353,353
medium_51x51,Dijkstra,1,0.7634,523,353,353
medium_51x51,Dijkstra,2,0.6893,523,353,353
medium_51x51,Dijkstra,3,0.6817,523,353,353
medium_51x51,Dijkstra,4,0.6965,523,353,353
medium_51x51,Dijkstra,5,0.6920,523,353,353
medium_51x51,Dijkstra,6,0.6702,523,353,353
medium_51x51,Dijkstra,7,0.7281,523,353,353
large_101x101,BFS,1,3.2679,2143,1265,1265
large_101x101,BFS,2,1.9302,2143,1265,1265
large_101x101,BFS,3,1.9559,2143,1265,1265
large_101x101,BFS,4,1.9057,2143,1265,1265
large_101x101,BFS,5,1.8770,2143,1265,1265
large_101x101,BFS,6,1.8828,2143,1265,1265
large_101x101,BFS,7,1.9345,2143,1265,1265
large_101x101,DFS,1,1.2758,1443,1265,1265
large_101x101,DFS,2,1.3043,1443,1265,1265
large_101x101,DFS,3,1.2613,1443,1265,1265
large_101x101,DFS,4,1.2846,1443,1265,1265
large_101x101,DFS,5,1.3566,1443,1265,1265
large_101x101,DFS,6,1.3296,1443,1265,1265
large_101x101,DFS,7,1.2501,1443,1265,1265
large_101x101,A*,1,3.2760,1831,1265,1265
large_101x101,A*,2,3.3353,1831,1265,1265
large_101x101,A*,3,4.1894,1831,1265,1265
large_101x101,A*,4,4.6809,1831,1265,1265
large_101x101,A*,5,3.4026,1831,1265,1265
large_101x101,A*,6,3.1036,1831,1265,1265
large_101x101,A*,7,3.2912,1831,1265,1265
large_101x101,Dijkstra,1,3.4403,2139,1265,1265
large_101x101,Dijkstra,2,3.3500,2139,1265,1265
large_101x101,Dijkstra,3,3.4201,2139,1265,1265
large_101x101,Dijkstra,4,3.2253,2139,1265,1265
large_101x101,Dijkstra,5,5.0122,2139,1265,1265
large_101x101,Dijkstra,6,3.3146,2139,1265,1265
large_101x101,Dijkstra,7,3.2323,2139,1265,1265
empty_30x30,BFS,1,0.8417,784,55,55
empty_30x30,BFS,2,0.8160,784,55,55
empty_30x30,BFS,3,0.7701,784,55,55
empty_30x30,BFS,4,0.7609,784,55,55
empty_30x30,BFS,5,0.7931,784,55,55
empty_30x30,BFS,6,0.7647,784,55,55
empty_30x30,BFS,7,0.8047,784,55,55
empty_30x30,DFS,1,0.5067,784,379,379
empty_30x30,DFS,2,0.6133,784,379,379
empty_30x30,DFS,3,0.8051,784,379,379
empty_30x30,DFS,4,0.4703,784,379,379
empty_30x30,DFS,5,0.8029,784,379,379
empty_30x30,DFS,6,0.5463,784,379,379
empty_30x30,DFS,7,0.4602,784,379,379
empty_30x30,A*,1,1.5117,784,55,55
empty_30x30,A*,2,1.4866,784,55,55
empty_30x30,A*,3,1.5878,784,55,55
empty_30x30,A*,4,1.8756,784,55,55
empty_30x30,A*,5,1.4943,784,55,55
empty_30x30,A*,6,2.0146,784,55,55
empty_30x30,A*,7,1.5262,784,55,55
empty_30x30,Dijkstra,1,1.2824,784,55,55
empty_30x30,Dijkstra,2,1.2897,784,55,55
empty_30x30,Dijkstra,3,1.3428,784,55,55
empty_30x30,Dijkstra,4,1.3181,784,55,55
empty_30x30,Dijkstra,5,1.2785,784,55,55
empty_30x30,Dijkstra,6,1.3634,784,55,55
empty_30x30,Dijkstra,7,1.2709,784,55,55
nopath_15x15,BFS,1,0.1595,165,0,0
nopath_15x15,BFS,2,0.1705,165,0,0
nopath_15x15,BFS,3,0.1489,165,0,0
nopath_15x15,BFS,4,0.1461,165,0,0
nopath_15x15,BFS,5,0.1972,165,0,0
nopath_15x15,BFS,6,0.1461,165,0,0
nopath_15x15,BFS,7,0.1436,165,0,0
nopath_15x15,DFS,1,0.2023,165,0,0
nopath_15x15,DFS,2,0.1506,165,0,0
nopath_15x15,DFS,3,0.1511,165,0,0
nopath_15x15,DFS,4,0.1477,165,0,0
nopath_15x15,DFS,5,0.1513,165,0,0
nopath_15x15,DFS,6,0.1455,165,0,0
nopath_15x15,DFS,7,0.1654,165,0,0
nopath_15x15,A*,1,0.2915,165,0,0
nopath_15x15,A*,2,0.3024,165,0,0
nopath_15x15,A*,3,0.2743,165,0,0
nopath_15x15,A*,4,0.2980,165,0,0
nopath_15x15,A*,5,0.2807,165,0,0
nopath_15x15,A*,6,0.2838,165,0,0
nopath_15x15,A*,7,0.3015,165,0,0
nopath_15x15,Dijkstra,1,0.2476,165,0,0
nopath_15x15,Dijkstra,2,0.2492,165,0,0
nopath_15x15,Dijkstra,3,0.2435,165,0,0
nopath_15x15,Dijkstra,4,0.2869,165,0,0
nopath_15x15,Dijkstra,5,0.2466,165,0,0
nopath_15x15,Dijkstra,6,0.2480,165,0,0
nopath_15x15,Dijkstra,7,0.2445,165,0,0
weighted_31x31,BFS,1,0.4261,433,265,391
weighted_31x31,BFS,2,0.3905,433,265,391
weighted_31x31,BFS,3,0.3713,433,265,391
weighted_31x31,BFS,4,0.3713,433,265,391
weighted_31x31,BFS,5,0.3672,433,265,391
weighted_31x31,BFS,6,0.3788,433,265,391
weighted_31x31,BFS,7,0.4045,433,265,391
weighted_31x31,DFS,1,0.2646,318,265,391
weighted_31x31,DFS,2,0.2761,318,265,391
weighted_31x31,DFS,3,0.2978,318,265,391
weighted_31x31,DFS,4,0.2618,318,265,391
weighted_31x31,DFS,5,0.2717,318,265,391
weighted_31x31,DFS,6,0.2581,318,265,391
weighted_31x31,DFS,7,0.2787,318,265,391
weighted_31x31,A*,1,0.6283,405,265,391
weighted_31x31,A*,2,0.6319,405,265,391
weighted_31x31,A*,3,0.7192,405,265,391
weighted_31x31,A*,4,0.6285,405,265,391
weighted_31x31,A*,5,0.6179,405,265,391
weighted_31x31,A*,6,0.6571,405,265,391
weighted_31x31,A*,7,1.0022,405,265,391
weighted_31x31,Dijkstra,1,0.8638,431,265,391
weighted_31x31,Dijkstra,2,0.8008,431,265,391
weighted_31x31,Dijkstra,3,0.6000,431,265,391
weighted_31x31,Dijkstra,4,0.6262,431,265,391
weighted_31x31,Dijkstra,5,0.5502,431,265,391
weighted_31x31,Dijkstra,6,0.5523,431,265,391
weighted_31x31,Dijkstra,7,0.5431,431,265,391
weighted_choice,BFS,1,0.1839,189,19,29
weighted_choice,BFS,2,0.1642,189,19,29
weighted_choice,BFS,3,0.1718,189,19,29
weighted_choice,BFS,4,0.2025,189,19,29
weighted_choice,BFS,5,0.1855,189,19,29
weighted_choice,BFS,6,0.1656,189,19,29
weighted_choice,BFS,7,0.1674,189,19,29
weighted_choice,DFS,1,0.0238,55,19,29
weighted_choice,DFS,2,0.0204,55,19,29
weighted_choice,DFS,3,0.0196,55,19,29
weighted_choice,DFS,4,0.0201,55,19,29
weighted_choice,DFS,5,0.0372,55,19,29
weighted_choice,DFS,6,0.0198,55,19,29
weighted_choice,DFS,7,0.0198,55,19,29
weighted_choice,A*,1,0.2451,117,25,25
weighted_choice,A*,2,0.2572,117,25,25
weighted_choice,A*,3,0.2276,117,25,25
weighted_choice,A*,4,0.2337,117,25,25
weighted_choice,A*,5,0.2305,117,25,25
weighted_choice,A*,6,0.2742,117,25,25
weighted_choice,A*,7,0.2275,117,25,25
weighted_choice,Dijkstra,1,0.3360,209,25,25
weighted_choice,Dijkstra,2,0.4054,209,25,25
weighted_choice,Dijkstra,3,0.3169,209,25,25
weighted_choice,Dijkstra,4,0.3882,209,25,25
weighted_choice,Dijkstra,5,0.3406,209,25,25
weighted_choice,Dijkstra,6,0.3182,209,25,25
weighted_choice,Dijkstra,7,0.3200,209,25,25
--- СРЕДНИЕ ---
лабиринт,стратегия,среднееремя_мс,посещено_клеток,длина_пути,стоимость_пути
small_10x10,BFS,0.0306,34,16,16
small_10x10,DFS,0.0142,18,16,16
small_10x10,A*,0.0420,27,16,16
small_10x10,Dijkstra,0.0444,33,16,16
medium_51x51,BFS,0.4909,524,353,353
medium_51x51,DFS,0.3392,379,353,353
medium_51x51,A*,0.6780,421,353,353
medium_51x51,Dijkstra,0.7030,523,353,353
large_101x101,BFS,2.1077,2143,1265,1265
large_101x101,DFS,1.2946,1443,1265,1265
large_101x101,A*,3.6113,1831,1265,1265
large_101x101,Dijkstra,3.5707,2139,1265,1265
empty_30x30,BFS,0.7930,784,55,55
empty_30x30,DFS,0.6007,784,379,379
empty_30x30,A*,1.6424,784,55,55
empty_30x30,Dijkstra,1.3066,784,55,55
nopath_15x15,BFS,0.1588,165,0,0
nopath_15x15,DFS,0.1591,165,0,0
nopath_15x15,A*,0.2903,165,0,0
nopath_15x15,Dijkstra,0.2523,165,0,0
weighted_31x31,BFS,0.3871,433,265,391
weighted_31x31,DFS,0.2727,318,265,391
weighted_31x31,A*,0.6979,405,265,391
weighted_31x31,Dijkstra,0.6481,431,265,391
weighted_choice,BFS,0.1773,189,19,29
weighted_choice,DFS,0.0230,55,19,29
weighted_choice,A*,0.2423,117,25,25
weighted_choice,Dijkstra,0.3465,209,25,25
Can't render this file because it has a wrong number of fields in line 199.

View File

@ -0,0 +1,82 @@
"""
experiment.py - экспериментальное сравнение стратегий поиска пути.
Для каждого лабиринта × стратегии:
- запускаем solve() TRIALS раз
- усредняем время в мс, фиксируем число посещённых клеток и длину пути
- сохраняем в docs/data/results.csv
"""
import csv
import os
from maze_solver import (
TextFileMazeBuilder, MazeSolver,
BFSStrategy, DFSStrategy, AStarStrategy, DijkstraStrategy,
)
TRIALS = 7
MAZES = [
("small_10x10", "mazes/small_10x10.txt"),
("medium_51x51", "mazes/medium_51x51.txt"),
("large_101x101", "mazes/large_101x101.txt"),
("empty_30x30", "mazes/empty_30x30.txt"),
("nopath_15x15", "mazes/nopath_15x15.txt"),
("weighted_31x31", "mazes/weighted_31x31.txt"),
("weighted_choice","mazes/weighted_choice.txt"),
]
STRATEGY_CLASSES = [BFSStrategy, DFSStrategy, AStarStrategy, DijkstraStrategy]
OUT_CSV = "docs/data/results.csv"
def main():
os.makedirs(os.path.dirname(OUT_CSV), exist_ok=True)
builder = TextFileMazeBuilder()
rows = [["лабиринт", "стратегия", "trial",
"время_мс", "посещено_клеток", "длина_пути", "стоимость_пути"]]
summary = []
for maze_name, maze_path in MAZES:
maze = builder.build_from_file(maze_path)
print(f"\n## {maze_name} ({maze.width}x{maze.height})")
for cls in STRATEGY_CLASSES:
times, visited_vals, path_vals, cost_vals = [], [], [], []
for trial in range(TRIALS):
solver = MazeSolver(maze, cls())
stats = solver.solve()
cost = sum(c.weight for c in stats["path"])
times.append(stats["elapsed_ms"])
visited_vals.append(stats["visited"])
path_vals.append(stats["path_length"])
cost_vals.append(cost)
rows.append([maze_name, stats["strategy"], trial + 1,
f"{stats['elapsed_ms']:.4f}",
stats["visited"], stats["path_length"], cost])
mean_t = sum(times) / TRIALS
print(f" {cls.name:9s} t_avg={mean_t:7.3f} ms "
f"visited={visited_vals[0]:5d} "
f"path={path_vals[0]:5d} cost={cost_vals[0]:5d}")
summary.append((maze_name, cls.name, mean_t,
visited_vals[0], path_vals[0], cost_vals[0]))
rows.append([])
rows.append(["--- СРЕДНИЕ ---"])
rows.append(["лабиринт", "стратегия", "среднееремя_мс",
"посещено_клеток", "длина_пути", "стоимость_пути"])
for r in summary:
rows.append([r[0], r[1], f"{r[2]:.4f}", r[3], r[4], r[5]])
with open(OUT_CSV, "w", newline="", encoding="utf-8") as f:
csv.writer(f).writerows(rows)
print(f"\nГотово. Результаты записаны в {OUT_CSV}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,99 @@
"""plot_results.py - графики для эксперимента с лабиринтами."""
import csv
import os
import matplotlib.pyplot as plt
import numpy as np
CSV = "docs/data/results.csv"
PLOTS = "docs/data/plots"
os.makedirs(PLOTS, exist_ok=True)
def load_means():
"""Возвращает dict[(maze, strategy)] = (time_ms, visited, path_len, cost)."""
out = {}
with open(CSV, encoding="utf-8") as f:
rows = list(csv.reader(f))
start = next(i for i, r in enumerate(rows) if r and r[0] == "--- СРЕДНИЕ ---") + 2
for r in rows[start:]:
if not r:
continue
maze, strat, t, vis, plen, cost = r
out[(maze, strat)] = (float(t), int(vis), int(plen), int(cost))
return out
MAZES = ["small_10x10", "medium_51x51", "large_101x101",
"empty_30x30", "nopath_15x15",
"weighted_31x31", "weighted_choice"]
STRATEGIES = ["BFS", "DFS", "A*", "Dijkstra"]
COLORS = {"BFS": "#3498db", "DFS": "#e67e22", "A*": "#2ecc71", "Dijkstra": "#9b59b6"}
def grouped_bar(means, idx, ylabel, title, fname, log=True):
x = np.arange(len(MAZES))
w = 0.2
fig, ax = plt.subplots(figsize=(11, 5))
for i, s in enumerate(STRATEGIES):
vals = [means[(m, s)][idx] for m in MAZES]
bars = ax.bar(x + (i - 1.5) * w, vals, w, label=s, color=COLORS[s], alpha=0.9)
for b, v in zip(bars, vals):
ax.text(b.get_x() + b.get_width() / 2, b.get_height(),
f"{v:g}", ha="center", va="bottom", fontsize=7, rotation=0)
ax.set_xticks(x)
ax.set_xticklabels(MAZES, rotation=20, ha="right")
ax.set_ylabel(ylabel)
ax.set_title(title)
if log:
ax.set_yscale("log")
ax.legend()
ax.grid(axis="y", linestyle="--", alpha=0.4)
plt.tight_layout()
p = os.path.join(PLOTS, fname)
plt.savefig(p, dpi=130)
plt.close()
print("saved:", p)
def weighted_choice_chart(means):
"""Отдельный график для weighted_choice: путь vs стоимость."""
strategies = STRATEGIES
lengths = [means[("weighted_choice", s)][2] for s in strategies]
costs = [means[("weighted_choice", s)][3] for s in strategies]
x = np.arange(len(strategies))
w = 0.35
fig, ax = plt.subplots(figsize=(7.5, 4.5))
b1 = ax.bar(x - w/2, lengths, w, label="длина пути (клеток)",
color="#3498db", alpha=0.9)
b2 = ax.bar(x + w/2, costs, w, label="стоимость пути (сумма весов)",
color="#e74c3c", alpha=0.9)
for bars in (b1, b2):
for bar in bars:
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height(),
f"{bar.get_height():.0f}", ha="center", va="bottom", fontsize=9)
ax.set_xticks(x); ax.set_xticklabels(strategies)
ax.set_title("weighted_choice: BFS/DFS режут через болото,\n"
"Dijkstra/A* находят более дешёвый обход")
ax.set_ylabel("значение")
ax.legend()
ax.grid(axis="y", linestyle="--", alpha=0.4)
plt.tight_layout()
p = os.path.join(PLOTS, "weighted_choice_compare.png")
plt.savefig(p, dpi=130)
plt.close()
print("saved:", p)
def main():
means = load_means()
grouped_bar(means, 0, "Время, мс (среднее по 7 запускам, лог. шкала)",
"Время поиска пути", "time_compare.png", log=True)
grouped_bar(means, 1, "Число посещённых клеток (лог. шкала)",
"Сколько клеток посетил алгоритм", "visited_compare.png", log=True)
grouped_bar(means, 2, "Длина пути (клеток)",
"Длина найденного пути", "path_compare.png", log=False)
weighted_choice_chart(means)
if __name__ == "__main__":
main()