diff --git a/agafonovdm/docs/data/1zad/1-st_ex.py b/agafonovdm/docs/data/1zad/1-st_ex.py new file mode 100644 index 0000000..24c59b7 --- /dev/null +++ b/agafonovdm/docs/data/1zad/1-st_ex.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import time +import random +import csv +import sys +sys.setrecursionlimit(30000) + +def ll_create_node(name, phone): + return {'name': name, 'phone': phone, 'next': None} + +def ll_insert(head, name, phone): + if head is None: + return ll_create_node(name, phone) + + if head['name'] == name: + head['phone'] = phone + return head + + current = head + while current['next'] is not None: + if current['next']['name'] == name: + current['next']['phone'] = phone + return head + current = current['next'] + + current['next'] = ll_create_node(name, phone) + return head + +def ll_find(head, name): + current = head + while current is not None: + if current['name'] == name: + return current['phone'] + current = current['next'] + return None + +def ll_delete(head, name): + if head is None: + return None + + if head['name'] == name: + return head['next'] + + current = head + while current['next'] is not None: + if current['next']['name'] == name: + current['next'] = current['next']['next'] + return head + current = current['next'] + + return head + +def ll_list_all(head): + records = [] + current = head + while current is not None: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x: x[0]) + return records + +def hash_function(name, table_size): + return sum(ord(c) for c in name) % table_size + +def ht_create_table(size=2000): + return [None] * size + +def ht_insert(table, name, phone): + index = hash_function(name, len(table)) + table[index] = ll_insert(table[index], name, phone) + +def ht_find(table, name): + index = hash_function(name, len(table)) + return ll_find(table[index], name) + +def ht_delete(table, name): + index = hash_function(name, len(table)) + table[index] = ll_delete(table[index], name) + +def ht_list_all(table): + all_records = [] + for bucket in table: + if bucket is not None: + current = bucket + while current is not None: + all_records.append((current['name'], current['phone'])) + current = current['next'] + all_records.sort(key=lambda x: x[0]) + return all_records + +def bst_create_node(name, phone): + return {'name': name, 'phone': phone, 'left': None, 'right': None} + +def bst_insert(root, name, phone): + if root is None: + return bst_create_node(name, phone) + + current = root + while True: + if name < current['name']: + if current['left'] is None: + current['left'] = bst_create_node(name, phone) + break + else: + current = current['left'] + elif name > current['name']: + if current['right'] is None: + current['right'] = bst_create_node(name, phone) + break + else: + current = current['right'] + else: + current['phone'] = phone + break + + return root + +def bst_find(root, name): + current = root + while current is not None: + if name < current['name']: + current = current['left'] + elif name > current['name']: + current = current['right'] + else: + return current['phone'] + return None + +def bst_find_min(node): + current = node + while current['left'] is not None: + current = current['left'] + return current + +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 or current['right'] is None: + if current['left'] is not None: + child = current['left'] + else: + child = current['right'] + + if parent is None: + return child + + if parent['left'] == current: + parent['left'] = child + else: + parent['right'] = child + else: + 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['left'] == successor: + successor_parent['left'] = successor['right'] + else: + successor_parent['right'] = successor['right'] + + return root + +def bst_list_all(root): + records = [] + stack = [] + current = root + + while stack or current is not None: + while current is not None: + stack.append(current) + current = current['left'] + current = stack.pop() + records.append((current['name'], current['phone'])) + current = current['right'] + + return records + +def generate_data(n=10000): + records = [(f"User_{i:05d}", f"+7-999-{i:06d}") for i in range(n)] + records_shuffled = records.copy() + random.shuffle(records_shuffled) + records_sorted = sorted(records, key=lambda x: x[0]) + return records_shuffled, records_sorted + +def run_experiment(structure_name, insert_func, find_func, delete_func, + list_all_func, init_func, records, n_find=100): + + data = init_func() + names = [r[0] for r in records] + + start = time.perf_counter() + for name, phone in records: + if structure_name == "HashTable": + insert_func(data, name, phone) + else: + data = insert_func(data, name, phone) + insert_time = time.perf_counter() - start + + find_names = random.sample(names, min(n_find, len(names))) + missing_names = [f"None_{i}" for i in range(10)] + all_find_names = find_names + missing_names + + start = time.perf_counter() + for name in all_find_names: + if structure_name == "HashTable": + find_func(data, name) + else: + find_func(data, name) + find_time = time.perf_counter() - start + + delete_names = random.sample(names, min(50, len(names))) + start = time.perf_counter() + for name in delete_names: + if structure_name == "HashTable": + delete_func(data, name) + else: + data = delete_func(data, name) + delete_time = time.perf_counter() - start + + return insert_time, find_time, delete_time + +def main(): + print("Generating test data...") + records_shuffled, records_sorted = generate_data(10000) + + results = [] + + structures = [ + ("LinkedList", ll_insert, ll_find, ll_delete, ll_list_all, lambda: None), + ("HashTable", ht_insert, ht_find, ht_delete, ht_list_all, lambda: ht_create_table(2000)), + ("BST", bst_insert, bst_find, bst_delete, bst_list_all, lambda: None) + ] + + for mode_name, records in [("random", records_shuffled), ("sorted", records_sorted)]: + print(f"\nMode: {mode_name}") + + for struct_name, insert_f, find_f, delete_f, list_f, init_f in structures: + print(f" Testing {struct_name}...") + + times = [] + for run in range(5): + insert_t, find_t, delete_t = run_experiment( + struct_name, insert_f, find_f, delete_f, list_f, init_f, records + ) + times.append((insert_t, find_t, delete_t)) + print(f" Run {run+1}: insert={insert_t:.4f}s, find={find_t:.4f}s, delete={delete_t:.4f}s") + + avg_insert = sum(t[0] for t in times) / 5 + avg_find = sum(t[1] for t in times) / 5 + avg_delete = sum(t[2] for t in times) / 5 + + results.append([struct_name, mode_name, "insert", avg_insert]) + results.append([struct_name, mode_name, "find", avg_find]) + results.append([struct_name, mode_name, "delete", avg_delete]) + + with open("results.csv", "w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(["Structure", "Mode", "Operation", "Time_seconds"]) + writer.writerows(results) + + print("\n" + "="*60) + print("RESULTS (average over 5 runs):") + print("="*60) + for row in results: + print(f"{row[0]:12} | {row[1]:8} | {row[2]:8} | {row[3]:.6f} sec") + + print("\nResults saved to results.csv") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/agafonovdm/docs/data/1zad/results.csv b/agafonovdm/docs/data/1zad/results.csv new file mode 100644 index 0000000..71a2199 --- /dev/null +++ b/agafonovdm/docs/data/1zad/results.csv @@ -0,0 +1,19 @@ +Structure,Mode,Operation,Time_seconds +LinkedList,random,insert,3.115811080000276 +LinkedList,random,find,0.02396312000018952 +LinkedList,random,delete,0.016048219999720458 +HashTable,random,insert,0.18448304000012286 +HashTable,random,find,0.0012929600005008978 +HashTable,random,delete,0.0009329200001957361 +BST,random,insert,0.017231119999996734 +BST,random,find,0.00014155999961076304 +BST,random,delete,9.299999983340968e-05 +LinkedList,sorted,insert,2.780292439999903 +LinkedList,sorted,find,0.02136590000045544 +LinkedList,sorted,delete,0.014907859999584615 +HashTable,sorted,insert,0.16707750000023225 +HashTable,sorted,find,0.0012113199998566415 +HashTable,sorted,delete,0.0008899600001313956 +BST,sorted,insert,3.844869280000421 +BST,sorted,find,0.031808019999880345 +BST,sorted,delete,0.016554539999560802 diff --git a/agafonovdm/docs/data/2zad/2-nd_ex.py b/agafonovdm/docs/data/2zad/2-nd_ex.py new file mode 100644 index 0000000..8215963 --- /dev/null +++ b/agafonovdm/docs/data/2zad/2-nd_ex.py @@ -0,0 +1,589 @@ +import time +import heapq +from collections import deque +from typing import List, Optional, Dict, Tuple +from abc import ABC, abstractmethod +import csv +import random + + +class Cell: + def __init__(self, x: int, y: int): + self.x = x + self.y = y + self.is_wall = False + self.is_start = False + self.is_exit = False + + def is_passable(self) -> bool: + return not self.is_wall + + +class Maze: + def __init__(self, width: int, height: int): + self.width = width + self.height = height + self.cells = [[Cell(x, y) for y in range(height)] for x in range(width)] + self.start: Optional[Cell] = None + self.exit: Optional[Cell] = None + + def get_cell(self, x: int, y: int) -> Optional[Cell]: + if 0 <= x < self.width and 0 <= y < self.height: + return self.cells[x][y] + return None + + def get_neighbors(self, cell: Cell) -> List[Cell]: + neighbors = [] + for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + nx, ny = cell.x + dx, cell.y + dy + nb = self.get_cell(nx, ny) + if nb and nb.is_passable(): + neighbors.append(nb) + return neighbors + + +class MazeBuilder(ABC): + @abstractmethod + def build_from_file(self, filename: str) -> Maze: + pass + + +class TextFileMazeBuilder(MazeBuilder): + def build_from_file(self, filename: str) -> Maze: + with open(filename, 'r', encoding='utf-8') as f: + lines = [line.rstrip('\n') for line in f.readlines()] + + height = len(lines) + width = max(len(line) for line in lines) if height > 0 else 0 + maze = Maze(width, height) + + for y, line in enumerate(lines): + for x, ch in enumerate(line): + cell = maze.get_cell(x, y) + if cell is None: + continue + if ch == '#': + cell.is_wall = True + elif ch == 'S': + cell.is_start = True + maze.start = cell + elif ch == 'E': + cell.is_exit = True + maze.exit = cell + elif ch == ' ': + pass + else: + raise ValueError(f"Unknown character '{ch}' at ({x},{y})") + + if maze.start is None or maze.exit is None: + raise ValueError("Maze must have start (S) and exit (E)") + return maze + + +class PathFindingStrategy(ABC): + @abstractmethod + def find_path(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]: + pass + + @abstractmethod + def get_name(self) -> str: + pass + + +class BFSStrategy(PathFindingStrategy): + def find_path(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]: + queue = deque([start]) + came_from = {start: None} + + while queue: + current = queue.popleft() + if current == exit: + break + for nb in maze.get_neighbors(current): + if nb not in came_from: + came_from[nb] = current + queue.append(nb) + + if exit not in came_from: + return [] + + path = [] + cur = exit + while cur: + path.append(cur) + cur = came_from[cur] + path.reverse() + return path + + def get_name(self) -> str: + return "BFS" + + +class DFSStrategy(PathFindingStrategy): + def find_path(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]: + stack = [start] + came_from = {start: None} + + while stack: + current = stack.pop() + if current == exit: + break + for nb in maze.get_neighbors(current): + if nb not in came_from: + came_from[nb] = current + stack.append(nb) + + if exit not in came_from: + return [] + + path = [] + cur = exit + while cur: + path.append(cur) + cur = came_from[cur] + path.reverse() + return path + + def get_name(self) -> str: + return "DFS" + + +class AStarStrategy(PathFindingStrategy): + def _heuristic(self, a: Cell, b: Cell) -> int: + return abs(a.x - b.x) + abs(a.y - b.y) + + def find_path(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]: + open_set = [] + heapq.heappush(open_set, (0, id(start), start)) + came_from = {} + g_score = {start: 0} + f_score = {start: self._heuristic(start, exit)} + + while open_set: + _, _, current = heapq.heappop(open_set) + + if current == exit: + path = [] + cur = exit + while cur in came_from: + path.append(cur) + cur = came_from[cur] + path.append(start) + path.reverse() + return path + + for neighbor in maze.get_neighbors(current): + tentative_g = g_score[current] + 1 + if tentative_g < g_score.get(neighbor, float('inf')): + came_from[neighbor] = current + g_score[neighbor] = tentative_g + f_score[neighbor] = tentative_g + self._heuristic(neighbor, exit) + heapq.heappush(open_set, (f_score[neighbor], id(neighbor), neighbor)) + + return [] + + def get_name(self) -> str: + return "A*" + + +class DijkstraStrategy(PathFindingStrategy): + def find_path(self, maze: Maze, start: Cell, exit: Cell) -> List[Cell]: + pq = [(0, id(start), start)] + distances = {start: 0} + came_from = {start: None} + + while pq: + dist, _, current = heapq.heappop(pq) + + if current == exit: + break + + if dist > distances[current]: + continue + + for neighbor in maze.get_neighbors(current): + new_dist = dist + 1 + if new_dist < distances.get(neighbor, float('inf')): + distances[neighbor] = new_dist + came_from[neighbor] = current + heapq.heappush(pq, (new_dist, id(neighbor), neighbor)) + + if exit not in came_from: + return [] + + path = [] + cur = exit + while cur: + path.append(cur) + cur = came_from[cur] + path.reverse() + return path + + def get_name(self) -> str: + return "Dijkstra" + + +class SearchStats: + def __init__(self, time_ms: float, visited_cells: int, path_length: int): + self.time_ms = time_ms + self.visited_cells = visited_cells + self.path_length = path_length + + def __str__(self): + return f"Time: {self.time_ms:.2f}ms, Visited: {self.visited_cells}, Path: {self.path_length}" + + +class MazeSolver: + def __init__(self, maze: Maze, strategy: PathFindingStrategy): + self.maze = maze + self.strategy = strategy + + def set_strategy(self, strategy: PathFindingStrategy): + self.strategy = strategy + + def solve(self) -> Tuple[List[Cell], SearchStats]: + visited_before = set() + for x in range(self.maze.width): + for y in range(self.maze.height): + cell = self.maze.get_cell(x, y) + if cell and cell.is_passable(): + visited_before.add(cell) + + start_time = time.perf_counter() + path = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit) + end_time = time.perf_counter() + + visited_after = set() + for x in range(self.maze.width): + for y in range(self.maze.height): + cell = self.maze.get_cell(x, y) + if cell and cell.is_passable(): + visited_after.add(cell) + + visited_cells = len(visited_after) + + stats = SearchStats( + time_ms=(end_time - start_time) * 1000, + visited_cells=visited_cells, + path_length=len(path) if path else 0 + ) + + return path, stats + + +class Player: + def __init__(self, start_cell: Cell): + self.current_cell = start_cell + self.previous_cell = None + + def move_to(self, cell: Cell) -> bool: + if cell.is_passable(): + self.previous_cell = self.current_cell + self.current_cell = cell + return True + return False + + def undo(self): + if self.previous_cell: + self.current_cell, self.previous_cell = self.previous_cell, None + return True + return False + + +class Command(ABC): + @abstractmethod + def execute(self) -> bool: + pass + + @abstractmethod + def undo(self): + pass + + +class MoveCommand(Command): + def __init__(self, player: Player, maze: Maze, direction: str): + self.player = player + self.maze = maze + self.direction = direction + self.executed = False + + def execute(self) -> bool: + dx, dy = 0, 0 + if self.direction == 'W' or self.direction == 'w': + dy = -1 + elif self.direction == 'S' or self.direction == 's': + dy = 1 + elif self.direction == 'A' or self.direction == 'a': + dx = -1 + elif self.direction == 'D' or self.direction == 'd': + dx = 1 + + new_x = self.player.current_cell.x + dx + new_y = self.player.current_cell.y + dy + new_cell = self.maze.get_cell(new_x, new_y) + + if new_cell and new_cell.is_passable(): + self.executed = self.player.move_to(new_cell) + return self.executed + return False + + def undo(self): + if self.executed: + self.player.undo() + self.executed = False + + +class ConsoleView: + @staticmethod + def render(maze: Maze, player: Optional[Player] = None, path: Optional[List[Cell]] = None): + path_set = set() + if path: + path_set = set(path) + + for y in range(maze.height): + line = "" + for x in range(maze.width): + cell = maze.get_cell(x, y) + if not cell: + line += " " + elif player and player.current_cell == cell: + line += "P" + elif cell.is_start: + line += "S" + elif cell.is_exit: + line += "E" + elif cell.is_wall: + line += "#" + elif path and cell in path_set: + line += "." + else: + line += " " + print(line) + print() + + @staticmethod + def show_stats(stats: SearchStats, algo_name: str): + print(f"=== {algo_name} Results ===") + print(stats) + print() + + +def generate_test_maze(width: int, height: int, complexity: float = 0.3) -> Maze: + maze = Maze(width, height) + + for x in range(width): + for y in range(height): + if random.random() < complexity: + maze.cells[x][y].is_wall = True + + maze.start = maze.get_cell(0, 0) + if maze.start: + maze.start.is_start = True + maze.start.is_wall = False + + maze.exit = maze.get_cell(width - 1, height - 1) + if maze.exit: + maze.exit.is_exit = True + maze.exit.is_wall = False + + return maze + + +def generate_empty_maze(width: int, height: int) -> Maze: + maze = Maze(width, height) + + for x in range(width): + for y in range(height): + maze.cells[x][y].is_wall = False + + maze.start = maze.get_cell(0, 0) + if maze.start: + maze.start.is_start = True + + maze.exit = maze.get_cell(width - 1, height - 1) + if maze.exit: + maze.exit.is_exit = True + + return maze + + +def generate_no_exit_maze(width: int, height: int) -> Maze: + maze = Maze(width, height) + + for x in range(width): + for y in range(height): + maze.cells[x][y].is_wall = False + + for x in range(width): + maze.cells[x][height // 2].is_wall = True + + maze.start = maze.get_cell(0, 0) + if maze.start: + maze.start.is_start = True + + maze.exit = maze.get_cell(width - 1, height - 1) + if maze.exit: + maze.exit.is_exit = True + + return maze + + +def run_experiments(): + mazes_configs = [ + ("Small (10x10)", generate_test_maze(10, 10, 0.2)), + ("Medium (50x50)", generate_test_maze(50, 50, 0.25)), + ("Large (100x100)", generate_test_maze(100, 100, 0.3)), + ("Empty (30x30)", generate_empty_maze(30, 30)), + ("No Exit (20x20)", generate_no_exit_maze(20, 20)) + ] + + strategies = [BFSStrategy(), DFSStrategy(), AStarStrategy(), DijkstraStrategy()] + + results = [] + + for maze_name, maze in mazes_configs: + print(f"\n=== Testing: {maze_name} ===") + + for strategy in strategies: + times = [] + visited = [] + path_lengths = [] + + solver = MazeSolver(maze, strategy) + + for run in range(5): + maze_copy = Maze(maze.width, maze.height) + for x in range(maze.width): + for y in range(maze.height): + orig = maze.get_cell(x, y) + copy = maze_copy.get_cell(x, y) + if orig: + copy.is_wall = orig.is_wall + copy.is_start = orig.is_start + copy.is_exit = orig.is_exit + maze_copy.start = maze_copy.get_cell(maze.start.x, maze.start.y) if maze.start else None + maze_copy.exit = maze_copy.get_cell(maze.exit.x, maze.exit.y) if maze.exit else None + + solver.maze = maze_copy + solver.set_strategy(strategy) + path, stats = solver.solve() + + times.append(stats.time_ms) + visited.append(stats.visited_cells) + path_lengths.append(stats.path_length) + + avg_time = sum(times) / len(times) + avg_visited = sum(visited) / len(visited) + avg_path = sum(path_lengths) / len(path_lengths) + + results.append({ + 'maze': maze_name, + 'algorithm': strategy.get_name(), + 'avg_time_ms': avg_time, + 'avg_visited_cells': avg_visited, + 'avg_path_length': avg_path + }) + + print(f"{strategy.get_name()}: {avg_time:.2f}ms, {avg_visited:.0f} cells, path={avg_path:.0f}") + + with open('experiment_results.csv', 'w', newline='', encoding='utf-8') as f: + writer = csv.DictWriter(f, fieldnames=['maze', 'algorithm', 'avg_time_ms', 'avg_visited_cells', 'avg_path_length']) + writer.writeheader() + writer.writerows(results) + + print("\nResults saved to experiment_results.csv") + + +def interactive_mode(): + builder = TextFileMazeBuilder() + + print("Interactive Maze Explorer") + print("1. Load maze from file") + print("2. Generate random maze") + choice = input("Choose (1/2): ") + + if choice == '1': + filename = input("Enter filename: ") + try: + maze = builder.build_from_file(filename) + except Exception as e: + print(f"Error loading maze: {e}") + return + else: + w = int(input("Width: ")) + h = int(input("Height: ")) + maze = generate_test_maze(w, h, 0.3) + + player = Player(maze.start) + + strategies = { + '1': BFSStrategy(), + '2': DFSStrategy(), + '3': AStarStrategy(), + '4': DijkstraStrategy() + } + + print("\nSelect algorithm for solving:") + print("1. BFS (shortest path)") + print("2. DFS (fast, not optimal)") + print("3. A* (heuristic)") + print("4. Dijkstra") + algo_choice = input("Choose: ") + + solver = MazeSolver(maze, strategies.get(algo_choice, BFSStrategy())) + path, stats = solver.solve() + + view = ConsoleView() + + if path: + print(f"\nPath found! Length: {len(path)}") + view.show_stats(stats, solver.strategy.get_name()) + else: + print("\nNo path found!") + + while True: + view.render(maze, player, path if path else None) + + if player.current_cell == maze.exit: + print("Congratulations! You reached the exit!") + break + + cmd = input("Move (W/A/S/D) | U=undo | Q=quit | S=solve: ").upper() + + if cmd == 'Q': + break + elif cmd == 'U': + player.undo() + print("Undo last move") + elif cmd == 'S' and path: + for cell in path: + if cell == player.current_cell: + continue + player.move_to(cell) + view.render(maze, player, path) + input("Press Enter to continue...") + if player.current_cell == maze.exit: + print("You reached the exit!") + break + elif cmd in ['W', 'A', 'S', 'D']: + move_cmd = MoveCommand(player, maze, cmd) + if move_cmd.execute(): + print("Moved") + else: + print("Can't move there!") + + +def main(): + print("Maze Solver with Design Patterns") + print("1. Run experiments") + print("2. Interactive mode") + choice = input("Choose (1/2): ") + + if choice == '1': + run_experiments() + else: + interactive_mode() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/agafonovdm/docs/data/2zad/RESULT22.py b/agafonovdm/docs/data/2zad/RESULT22.py new file mode 100644 index 0000000..62cd70e --- /dev/null +++ b/agafonovdm/docs/data/2zad/RESULT22.py @@ -0,0 +1,363 @@ +import pandas as pd +import matplotlib.pyplot as plt +import numpy as np + +# Настройка русских шрифтов +plt.rcParams['font.family'] = 'DejaVu Sans' +plt.rcParams['axes.unicode_minus'] = False + +def load_and_prepare_data(filename='experiment_results.csv'): + """Загрузка данных из CSV и подготовка.""" + df = pd.read_csv(filename, delimiter=',') # Используем запятую как разделитель + + # Переименовываем столбцы для удобства + df.columns = ['maze_type', 'algorithm', 'avg_time_ms', 'avg_visited_cells', 'avg_path_length'] + + # Преобразование типов + numeric_cols = ['avg_time_ms', 'avg_visited_cells', 'avg_path_length'] + for col in numeric_cols: + df[col] = pd.to_numeric(df[col], errors='coerce') + + # Добавляем столбец с размером лабиринта для анализа + def extract_maze_size(maze_name): + if 'Small' in maze_name: + return 'Small (10x10)' + elif 'Medium' in maze_name: + return 'Medium (50x50)' + elif 'Large' in maze_name: + return 'Large (100x100)' + elif 'Empty' in maze_name: + return 'Empty (30x30)' + elif 'No Exit' in maze_name: + return 'No Exit (20x20)' + return maze_name + + df['maze_category'] = df['maze_type'].apply(extract_maze_size) + + return df + +def plot_time_comparison(df): + """График 1: Сравнение времени выполнения по лабиринтам.""" + fig, ax = plt.subplots(figsize=(12, 6)) + + maze_types = df['maze_category'].unique() + algorithms = df['algorithm'].unique() + + x = np.arange(len(maze_types)) + width = 0.2 + + colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'] + + for i, algorithm in enumerate(algorithms): + algo_data = df[df['algorithm'] == algorithm] + times = [] + for maze in maze_types: + row = algo_data[algo_data['maze_category'] == maze] + if not row.empty: + times.append(row['avg_time_ms'].values[0]) + else: + times.append(0) + + bars = ax.bar(x + i*width, times, width, label=algorithm, + color=colors[i]) + + ax.set_xlabel('Тип лабиринта', fontsize=12) + ax.set_ylabel('Время выполнения (мс)', fontsize=12) + ax.set_title('Сравнение времени выполнения алгоритмов поиска пути', fontsize=14) + ax.set_xticks(x + width * 1.5) + ax.set_xticklabels(maze_types, rotation=45, ha='right') + ax.legend() + ax.grid(True, alpha=0.3, axis='y') + + # Добавление значений на столбцы + for i, algorithm in enumerate(algorithms): + algo_data = df[df['algorithm'] == algorithm] + for j, maze in enumerate(maze_types): + row = algo_data[algo_data['maze_category'] == maze] + if not row.empty and row['avg_time_ms'].values[0] > 0: + time_val = row['avg_time_ms'].values[0] + ax.text(x[j] + i*width, time_val + 0.02, + f'{time_val:.3f}', ha='center', va='bottom', fontsize=8) + + plt.tight_layout() + plt.savefig('time_comparison.png', dpi=150) + plt.show() + +def plot_visited_cells(df): + """График 2: Количество посещённых клеток.""" + fig, ax = plt.subplots(figsize=(12, 6)) + + maze_types = df['maze_category'].unique() + algorithms = df['algorithm'].unique() + + x = np.arange(len(maze_types)) + width = 0.2 + + colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'] + + for i, algorithm in enumerate(algorithms): + algo_data = df[df['algorithm'] == algorithm] + visited = [] + for maze in maze_types: + row = algo_data[algo_data['maze_category'] == maze] + if not row.empty: + visited.append(row['avg_visited_cells'].values[0]) + else: + visited.append(0) + + ax.bar(x + i*width, visited, width, label=algorithm, color=colors[i]) + + ax.set_xlabel('Тип лабиринта', fontsize=12) + ax.set_ylabel('Количество посещённых клеток', fontsize=12) + ax.set_title('Сравнение количества посещённых клеток', fontsize=14) + ax.set_xticks(x + width * 1.5) + ax.set_xticklabels(maze_types, rotation=45, ha='right') + ax.legend() + ax.grid(True, alpha=0.3, axis='y') + + plt.tight_layout() + plt.savefig('visited_cells.png', dpi=150) + plt.show() + +def plot_path_length(df): + """График 3: Длина найденного пути.""" + fig, ax = plt.subplots(figsize=(12, 6)) + + # Исключаем лабиринты без выхода (где путь = 0) + df_filtered = df[df['avg_path_length'] > 0] + + maze_types = df_filtered['maze_category'].unique() + algorithms = df_filtered['algorithm'].unique() + + x = np.arange(len(maze_types)) + width = 0.2 + + colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'] + + for i, algorithm in enumerate(algorithms): + algo_data = df_filtered[df_filtered['algorithm'] == algorithm] + path_lengths = [] + for maze in maze_types: + row = algo_data[algo_data['maze_category'] == maze] + if not row.empty: + path_lengths.append(row['avg_path_length'].values[0]) + else: + path_lengths.append(0) + + ax.bar(x + i*width, path_lengths, width, label=algorithm, color=colors[i]) + + ax.set_xlabel('Тип лабиринта', fontsize=12) + ax.set_ylabel('Длина пути (количество клеток)', fontsize=12) + ax.set_title('Сравнение длины найденного пути', fontsize=14) + ax.set_xticks(x + width * 1.5) + ax.set_xticklabels(maze_types, rotation=45, ha='right') + ax.legend() + ax.grid(True, alpha=0.3, axis='y') + + plt.tight_layout() + plt.savefig('path_length.png', dpi=150) + plt.show() + +def plot_time_per_maze(df): + """График 4: Для каждого лабиринта - сравнение алгоритмов по времени.""" + maze_types = df['maze_category'].unique() + algorithms = df['algorithm'].unique() + + for maze in maze_types: + fig, ax = plt.subplots(figsize=(10, 6)) + + maze_data = df[df['maze_category'] == maze] + + times = [] + algo_names = [] + for algo in algorithms: + row = maze_data[maze_data['algorithm'] == algo] + if not row.empty: + times.append(row['avg_time_ms'].values[0]) + algo_names.append(algo) + + bars = ax.bar(algo_names, times, + color=['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'][:len(algo_names)]) + + ax.set_xlabel('Алгоритм', fontsize=12) + ax.set_ylabel('Время выполнения (мс)', fontsize=12) + ax.set_title(f'Сравнение алгоритмов на лабиринте: {maze}', fontsize=14) + ax.grid(True, alpha=0.3, axis='y') + + # Добавление значений на столбцы + for bar, time_val in zip(bars, times): + height = bar.get_height() + ax.text(bar.get_x() + bar.get_width()/2., height + 0.02, + f'{time_val:.3f}', ha='center', va='bottom', fontsize=10) + + plt.tight_layout() + # Очищаем имя файла от скобок + safe_maze_name = maze.replace('(', '').replace(')', '').replace(' ', '_') + plt.savefig(f'time_{safe_maze_name}.png', dpi=150) + plt.show() + +def plot_visited_per_maze(df): + """График 5: Для каждого лабиринта - посещённые клетки.""" + maze_types = df['maze_category'].unique() + + for maze in maze_types: + fig, ax = plt.subplots(figsize=(10, 6)) + + maze_data = df[df['maze_category'] == maze] + + visited = maze_data['avg_visited_cells'].values + algo_names = maze_data['algorithm'].values + + bars = ax.bar(algo_names, visited, + color=['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'][:len(algo_names)]) + + ax.set_xlabel('Алгоритм', fontsize=12) + ax.set_ylabel('Количество посещённых клеток', fontsize=12) + ax.set_title(f'Посещённые клетки на лабиринте: {maze}', fontsize=14) + ax.grid(True, alpha=0.3, axis='y') + + # Добавление значений на столбцы + for bar, val in zip(bars, visited): + height = bar.get_height() + ax.text(bar.get_x() + bar.get_width()/2., height + 10, + f'{int(val)}', ha='center', va='bottom', fontsize=10) + + plt.tight_layout() + safe_maze_name = maze.replace('(', '').replace(')', '').replace(' ', '_') + plt.savefig(f'visited_{safe_maze_name}.png', dpi=150) + plt.show() + +def plot_efficiency_ratio(df): + """График 6: Эффективность (время на клетку пути).""" + fig, ax = plt.subplots(figsize=(12, 6)) + + # Исключаем лабиринты без пути + df_filtered = df[(df['avg_path_length'] > 0) & (df['avg_time_ms'] > 0)].copy() + df_filtered['efficiency'] = df_filtered['avg_time_ms'] / df_filtered['avg_path_length'] + + maze_types = df_filtered['maze_category'].unique() + algorithms = df_filtered['algorithm'].unique() + + x = np.arange(len(maze_types)) + width = 0.2 + + colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'] + + for i, algorithm in enumerate(algorithms): + algo_data = df_filtered[df_filtered['algorithm'] == algorithm] + efficiency = [] + for maze in maze_types: + row = algo_data[algo_data['maze_category'] == maze] + if not row.empty: + efficiency.append(row['efficiency'].values[0]) + else: + efficiency.append(0) + + ax.bar(x + i*width, efficiency, width, label=algorithm, color=colors[i]) + + ax.set_xlabel('Тип лабиринта', fontsize=12) + ax.set_ylabel('Время на клетку пути (мс/клетку)', fontsize=12) + ax.set_title('Эффективность алгоритмов (время на единицу длины пути)', fontsize=14) + ax.set_xticks(x + width * 1.5) + ax.set_xticklabels(maze_types, rotation=45, ha='right') + ax.legend() + ax.grid(True, alpha=0.3, axis='y') + + plt.tight_layout() + plt.savefig('efficiency_ratio.png', dpi=150) + plt.show() + +def plot_path_vs_visited(df): + """График 7: Соотношение длины пути и посещённых клеток.""" + fig, ax = plt.subplots(figsize=(10, 6)) + + algorithms = df['algorithm'].unique() + markers = ['o', 's', '^', 'D'] + colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'] + + for algo, marker, color in zip(algorithms, markers, colors): + algo_data = df[df['algorithm'] == algo] + # Только лабиринты с путём + algo_data = algo_data[algo_data['avg_path_length'] > 0] + + if not algo_data.empty: + plt.scatter(algo_data['avg_visited_cells'], + algo_data['avg_path_length'], + marker=marker, s=100, label=algo, color=color, alpha=0.7) + + # Добавляем подписи для каждой точки + for _, row in algo_data.iterrows(): + plt.annotate(row['maze_category'].split()[0], + (row['avg_visited_cells'], row['avg_path_length']), + xytext=(5, 5), textcoords='offset points', fontsize=8) + + plt.xlabel('Количество посещённых клеток', fontsize=12) + plt.ylabel('Длина пути (клеток)', fontsize=12) + plt.title('Соотношение: посещённые клетки vs длина пути', fontsize=14) + plt.legend() + plt.grid(True, alpha=0.3) + + plt.tight_layout() + plt.savefig('path_vs_visited.png', dpi=150) + plt.show() + +def main(): + """Основная функция: загрузка данных и построение всех графиков.""" + try: + df = load_and_prepare_data('experiment_results.csv') + print("Данные успешно загружены") + print(f"Найдено {len(df)} записей") + print("\nСтруктура данных:") + print(df.head()) + print("\nУникальные типы лабиринтов:") + print(df['maze_category'].unique()) + print("\nУникальные алгоритмы:") + print(df['algorithm'].unique()) + + print("\nПостроение графиков...") + + # Базовые графики + plot_time_comparison(df) + plot_visited_cells(df) + plot_path_length(df) + + # Детальные графики по каждому лабиринту + plot_time_per_maze(df) + plot_visited_per_maze(df) + + # Аналитические графики + plot_efficiency_ratio(df) + plot_path_vs_visited(df) + + print("\nВсе графики сохранены в текущей директории:") + print(" - time_comparison.png") + print(" - visited_cells.png") + print(" - path_length.png") + print(" - time_{maze}.png (для каждого лабиринта)") + print(" - visited_{maze}.png (для каждого лабиринта)") + print(" - efficiency_ratio.png") + print(" - path_vs_visited.png") + + # Вывод статистики + print("\n=== Краткая статистика ===") + for maze in df['maze_category'].unique(): + print(f"\n{maze}:") + maze_data = df[df['maze_category'] == maze] + for algo in df['algorithm'].unique(): + algo_data = maze_data[maze_data['algorithm'] == algo] + if not algo_data.empty: + time_val = algo_data['avg_time_ms'].values[0] + visited_val = int(algo_data['avg_visited_cells'].values[0]) + path_val = int(algo_data['avg_path_length'].values[0]) + print(f" {algo}: время={time_val:.6f}мс, посещено={visited_val}, путь={path_val}") + + except FileNotFoundError: + print("Ошибка: файл experiment_results.csv не найден") + print("Убедитесь, что файл находится в текущей директории") + except Exception as e: + print(f"Ошибка: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/agafonovdm/docs/data/2zad/efficiency_ratio.png b/agafonovdm/docs/data/2zad/efficiency_ratio.png new file mode 100644 index 0000000..2edc6d2 Binary files /dev/null and b/agafonovdm/docs/data/2zad/efficiency_ratio.png differ diff --git a/agafonovdm/docs/data/2zad/experiment_results.csv b/agafonovdm/docs/data/2zad/experiment_results.csv new file mode 100644 index 0000000..5417132 --- /dev/null +++ b/agafonovdm/docs/data/2zad/experiment_results.csv @@ -0,0 +1,21 @@ +maze,algorithm,avg_time_ms,avg_visited_cells,avg_path_length +Small (10x10),BFS,0.08572000006097369,79.0,19.0 +Small (10x10),DFS,0.039739999920129776,79.0,31.0 +Small (10x10),A*,0.13467999997374136,79.0,19.0 +Small (10x10),Dijkstra,0.11474000057205558,79.0,19.0 +Medium (50x50),BFS,1.8074600004183594,1874.0,99.0 +Medium (50x50),DFS,0.5937599995377241,1874.0,429.0 +Medium (50x50),A*,1.6300600003887666,1874.0,99.0 +Medium (50x50),Dijkstra,3.1870400001935195,1874.0,99.0 +Large (100x100),BFS,0.014439999722526409,7033.0,0.0 +Large (100x100),DFS,0.014839999857940711,7033.0,0.0 +Large (100x100),A*,0.02542000001994893,7033.0,0.0 +Large (100x100),Dijkstra,0.02548000011302065,7033.0,0.0 +Empty (30x30),BFS,0.784620000194991,900.0,59.0 +Empty (30x30),DFS,0.5252399994787993,900.0,465.0 +Empty (30x30),A*,1.150900000357069,900.0,59.0 +Empty (30x30),Dijkstra,1.564640000287909,900.0,59.0 +No Exit (20x20),BFS,0.2002399993216386,380.0,0.0 +No Exit (20x20),DFS,0.2512400002160575,380.0,0.0 +No Exit (20x20),A*,0.5590400000073714,380.0,0.0 +No Exit (20x20),Dijkstra,0.35640000060084276,380.0,0.0 diff --git a/agafonovdm/docs/data/2zad/path_length.png b/agafonovdm/docs/data/2zad/path_length.png new file mode 100644 index 0000000..b8b08f7 Binary files /dev/null and b/agafonovdm/docs/data/2zad/path_length.png differ diff --git a/agafonovdm/docs/data/2zad/path_vs_visited.png b/agafonovdm/docs/data/2zad/path_vs_visited.png new file mode 100644 index 0000000..aa0c13d Binary files /dev/null and b/agafonovdm/docs/data/2zad/path_vs_visited.png differ diff --git a/agafonovdm/docs/data/2zad/time_Empty_30x30.png b/agafonovdm/docs/data/2zad/time_Empty_30x30.png new file mode 100644 index 0000000..ee520ab Binary files /dev/null and b/agafonovdm/docs/data/2zad/time_Empty_30x30.png differ diff --git a/agafonovdm/docs/data/2zad/time_Large_100x100.png b/agafonovdm/docs/data/2zad/time_Large_100x100.png new file mode 100644 index 0000000..430d9b3 Binary files /dev/null and b/agafonovdm/docs/data/2zad/time_Large_100x100.png differ diff --git a/agafonovdm/docs/data/2zad/time_Medium_50x50.png b/agafonovdm/docs/data/2zad/time_Medium_50x50.png new file mode 100644 index 0000000..0478456 Binary files /dev/null and b/agafonovdm/docs/data/2zad/time_Medium_50x50.png differ diff --git a/agafonovdm/docs/data/2zad/time_No_Exit_20x20.png b/agafonovdm/docs/data/2zad/time_No_Exit_20x20.png new file mode 100644 index 0000000..548ae32 Binary files /dev/null and b/agafonovdm/docs/data/2zad/time_No_Exit_20x20.png differ diff --git a/agafonovdm/docs/data/2zad/time_Small_10x10.png b/agafonovdm/docs/data/2zad/time_Small_10x10.png new file mode 100644 index 0000000..6e2f331 Binary files /dev/null and b/agafonovdm/docs/data/2zad/time_Small_10x10.png differ diff --git a/agafonovdm/docs/data/2zad/time_comparison.png b/agafonovdm/docs/data/2zad/time_comparison.png new file mode 100644 index 0000000..9e9b288 Binary files /dev/null and b/agafonovdm/docs/data/2zad/time_comparison.png differ diff --git a/agafonovdm/docs/data/2zad/visited_Empty_30x30.png b/agafonovdm/docs/data/2zad/visited_Empty_30x30.png new file mode 100644 index 0000000..a4e741c Binary files /dev/null and b/agafonovdm/docs/data/2zad/visited_Empty_30x30.png differ diff --git a/agafonovdm/docs/data/2zad/visited_Large_100x100.png b/agafonovdm/docs/data/2zad/visited_Large_100x100.png new file mode 100644 index 0000000..663ad83 Binary files /dev/null and b/agafonovdm/docs/data/2zad/visited_Large_100x100.png differ diff --git a/agafonovdm/docs/data/2zad/visited_Medium_50x50.png b/agafonovdm/docs/data/2zad/visited_Medium_50x50.png new file mode 100644 index 0000000..7c94c5e Binary files /dev/null and b/agafonovdm/docs/data/2zad/visited_Medium_50x50.png differ diff --git a/agafonovdm/docs/data/2zad/visited_No_Exit_20x20.png b/agafonovdm/docs/data/2zad/visited_No_Exit_20x20.png new file mode 100644 index 0000000..de8ecdd Binary files /dev/null and b/agafonovdm/docs/data/2zad/visited_No_Exit_20x20.png differ diff --git a/agafonovdm/docs/data/2zad/visited_Small_10x10.png b/agafonovdm/docs/data/2zad/visited_Small_10x10.png new file mode 100644 index 0000000..f0d7138 Binary files /dev/null and b/agafonovdm/docs/data/2zad/visited_Small_10x10.png differ diff --git a/agafonovdm/docs/data/2zad/visited_cells.png b/agafonovdm/docs/data/2zad/visited_cells.png new file mode 100644 index 0000000..f657cbd Binary files /dev/null and b/agafonovdm/docs/data/2zad/visited_cells.png differ diff --git a/agafonovdm/docs/otchet1.docx b/agafonovdm/docs/otchet1.docx new file mode 100644 index 0000000..44124b4 Binary files /dev/null and b/agafonovdm/docs/otchet1.docx differ diff --git a/agafonovdm/docs/otchet2.docx b/agafonovdm/docs/otchet2.docx new file mode 100644 index 0000000..37643b3 Binary files /dev/null and b/agafonovdm/docs/otchet2.docx differ