diff --git a/LebedevES/заданиÑ�/задание 1/1.py b/LebedevES/заданиÑ�/задание 1/1.py new file mode 100644 index 00000000..24c59b7f --- /dev/null +++ b/LebedevES/заданиÑ�/задание 1/1.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/LebedevES/заданиÑ�/задание 1/2.py b/LebedevES/заданиÑ�/задание 1/2.py new file mode 100644 index 00000000..8215963b --- /dev/null +++ b/LebedevES/заданиÑ�/задание 1/2.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/LebedevES/заданиÑ�/задание 1/experiment_results2.csv b/LebedevES/заданиÑ�/задание 1/experiment_results2.csv new file mode 100644 index 00000000..02069fe4 --- /dev/null +++ b/LebedevES/заданиÑ�/задание 1/experiment_results2.csv @@ -0,0 +1,21 @@ +maze,algorithm,avg_time_ms,avg_visited_cells,avg_path_length +Small (10x10),BFS,0.006740167737007141,80.0,0.0 +Small (10x10),DFS,0.00408003106713295,80.0,0.0 +Small (10x10),A*,0.005039852112531662,80.0,0.0 +Small (10x10),Dijkstra,0.0031800009310245514,80.0,0.0 +Medium (50x50),BFS,3.44578018411994,1890.0,99.0 +Medium (50x50),DFS,1.3188599608838558,1890.0,341.0 +Medium (50x50),A*,2.061920054256916,1890.0,99.0 +Medium (50x50),Dijkstra,4.679400008171797,1890.0,99.0 +Large (100x100),BFS,0.025319866836071014,6998.0,0.0 +Large (100x100),DFS,0.019940081983804703,6998.0,0.0 +Large (100x100),A*,0.035060010850429535,6998.0,0.0 +Large (100x100),Dijkstra,0.02901991829276085,6998.0,0.0 +Empty (30x30),BFS,1.2404202483594418,900.0,59.0 +Empty (30x30),DFS,0.8887200616300106,900.0,465.0 +Empty (30x30),A*,0.9468601085245609,900.0,59.0 +Empty (30x30),Dijkstra,2.678940072655678,900.0,59.0 +No Exit (20x20),BFS,0.27012014761567116,380.0,0.0 +No Exit (20x20),DFS,0.3163599409162998,380.0,0.0 +No Exit (20x20),A*,0.5885399878025055,380.0,0.0 +No Exit (20x20),Dijkstra,0.5776201374828815,380.0,0.0 diff --git a/LebedevES/заданиÑ�/задание 1/otchet1.txt b/LebedevES/заданиÑ�/задание 1/otchet1.txt new file mode 100644 index 00000000..51c977a0 --- /dev/null +++ b/LebedevES/заданиÑ�/задание 1/otchet1.txt @@ -0,0 +1,122 @@ + + +Ìåòîäû Ïðîãðàììèðîâàíèÿ + + +Ñòðóêòóðû äàííûõ, +àíàëèç 1 çàäàíèÿ + + + + + + + + +Áîáðîâ Ê. Í. +425 ãðóïïà + + + + + +Ñîäåðæàíèå + +Êàê ïîðÿäîê âõîäíûõ äàííûõ âëèÿåò íà ñêîðîñòü âñòàâêè â BST 2 +Ïî÷åìó õåø-òàáëèöà ïî÷òè íå ÷óâñòâèòåëüíà ê ïîðÿäêó 4 +Ïî÷åìó ñâÿçíûé ñïèñîê âñåãäà ìåäëåíåí ïðè ïîèñêå 6 +Êàê óäàëåíèå ðàáîòàåò â êàæäîé ñòðóêòóðå 7 +Âûâîä 9 + + + + +Êàê ïîðÿäîê âõîäíûõ äàííûõ âëèÿåò íà ñêîðîñòü âñòàâêè â BST + +Ïðè âñòàâêå îòñîðòèðîâàííûõ äàííûõ â BST (êðàñíûé ãðàôèê) ïðîèçâîäèòåëüíîñòü ïàäàåò â ðàçû ïî ñðàâíåíèþ ñî âñòàâêîé ñëó÷àéíûõ äàííûõ. Ýòî ñâÿçàíî ñ òåì, ÷òî îòñîðòèðîâàííàÿ ïîñëåäîâàòåëüíîñòü ïðèâîäèò ê âûðîæäåíèþ äåðåâà â ñâÿçàííûé ñïèñîê, òîãäà êàê ñëó÷àéíûé ïîðÿäîê âñòàâêè ïîìîãàåò ñîõðàíÿòü äåðåâî îòíîñèòåëüíî ñáàëàíñèðîâàííûì. +Ïðè âñòàâêå ýëåìåíòîâ â îòñîðòèðîâàííîì ïîðÿäêå (ïî âîçðàñòàíèþ èëè óáûâàíèþ): +?Êàæäûé íîâûé ýëåìåíò âñåãäà áîëüøå (èëè ìåíüøå) âñåõ óæå äîáàâëåííûõ. +? ðåçóëüòàòå àëãîðèòì êàæäûé ðàç äâèæåòñÿ ïî îäíîìó è òîìó æå íàïðàâëåíèþ — òîëüêî â ïðàâîå èëè òîëüêî â ëåâîå ïîääåðåâî. +?Èç-çà ýòîãî äåðåâî âûðîæäàåòñÿ: êàæäûé óçåë èìååò íå áîëåå îäíîãî ïîòîìêà, ñòðóêòóðà íàïîìèíàåò ëèíåéíûé ñïèñîê. +?Âûñîòà òàêîãî äåðåâà ñòàíîâèòñÿ ïðîïîðöèîíàëüíîé O(n). +?Êàæäàÿ îïåðàöèÿ âñòàâêè òðåáóåò â ñðåäíåì O(n) ñðàâíåíèé, òàê êàê íóæíî ïðîõîäèòü âñþ äëèíó òåêóùåé öåïî÷êè îò êîðíÿ äî ñàìîãî ãëóáîêîãî ëèñòà. +? èòîãå ñóììàðíàÿ ñëîæíîñòü âñòàâêè âñåõ n ýëåìåíòîâ âûðàñòàåò äî O(n^2). + + +Ïðè ñëó÷àéíîé âñòàâêå: +?Ýëåìåíòû ðàñïðåäåëÿþòñÿ ïî äåðåâó ãîðàçäî ðàâíîìåðíåå. +?Âûñîêà âåðîÿòíîñòü òîãî, ÷òî äåðåâî îñòàíåòñÿ ñáàëàíñèðîâàííûì. +?Ñðåäíÿÿ âûñîòà äåðåâà ñîõðàíÿåòñÿ íà óðîâíå O(logn). +?Êàæäàÿ îïåðàöèÿ âñòàâêè â ñðåäíåì òðåáóåò O(logn) ñðàâíåíèé. +?Îáùàÿ ñëîæíîñòü âñòàâêè âñåõ n ýëåìåíòîâ ñîñòàâëÿåò  O(nlogn). +Âûâîä: ðàçíèöà â ñêîðîñòè îáúÿñíÿåòñÿ ðàçëè÷èåì â âûñîòå äåðåâà.  âûðîæäåííîì ñëó÷àå âûñîòà ðàâíà O(n), è êàæäàÿ âñòàâêà âûïîëíÿåòñÿ â ?n/logn ðàç ìåäëåííåå ïî ÷èñëó øàãîâ, ÷åì â ñáàëàíñèðîâàííîì ñëó÷àå ñ âûñîòîé O(logn). + + + + + +Ïî÷åìó õåø-òàáëèöà ïî÷òè íå ÷óâñòâèòåëüíà ê ïîðÿäêó + + +Õåøòàáëèöà (æ¸ëòûé ãðàôèê) äåìîíñòðèðóåò ïî÷òè ïîëíóþ íåçàâèñèìîñòü îò ïîðÿäêà âñòàâêè ýëåìåíòîâ. Ýòî îáúÿñíÿåòñÿ òåì, ÷òî ïîëîæåíèå êàæäîãî ýëåìåíòà â ñòðóêòóðå îïðåäåëÿåòñÿ èñêëþ÷èòåëüíî çíà÷åíèåì åãî õåøôóíêöèè, à íå òåì, â êàêîé ïîñëåäîâàòåëüíîñòè ïðîèñõîäèëî äîáàâëåíèå äàííûõ. + +Îñíîâíûå ïðè÷èíû íå÷óâñòâèòåëüíîñòè ê ïîðÿäêó âñòàâêè: +?Õåøèðîâàíèå. Äëÿ êàæäîãî êëþ÷à âû÷èñëÿåòñÿ õåøêîä, êîòîðûé ïðåîáðàçóåòñÿ â èíäåêñ ÿ÷åéêè. Îäèí è òîò æå êëþ÷ âñåãäà äà¸ò îäèí è òîò æå èíäåêñ íåçàâèñèìî îò òîãî, êîãäà è â êàêîì ïîðÿäêå îí áûë äîáàâëåí. +?Íåçàâèñèìîñòü îïåðàöèé. Âñòàâêà, ïîèñê è óäàëåíèå âûïîëíÿþòñÿ â ñðåäíåì çà O(1)O(1), ïîñêîëüêó àëãîðèòì ñðàçó âû÷èñëÿåò íóæíóþ ïîçèöèþ, íå îáõîäÿ ñòðóêòóðó è íå ó÷èòûâàÿ èñòîðèþ äîáàâëåíèé. +?Ðàçðåøåíèå êîëëèçèé. Äàæå åñëè ïîðÿäîê âñòàâêè âëèÿåò íà ðàñïîëîæåíèå ýëåìåíòîâ âíóòðè öåïî÷êè (ìåòîä öåïî÷åê) èëè íà ïîñëåäîâàòåëüíîñòü ïðîá (îòêðûòàÿ àäðåñàöèÿ), ýòî êàñàåòñÿ ëèøü íåáîëüøèõ ãðóïï ýëåìåíòîâ ñ îäèíàêîâûìè õåøàìè. Îáùàÿ ïðîèçâîäèòåëüíîñòü îñòà¸òñÿ ñòàáèëüíîé. +?Ðåõåøèðîâàíèå. Ïðè óâåëè÷åíèè ðàçìåðà òàáëèöû âñå ýëåìåíòû ïåðåðàñïðåäåëÿþòñÿ çàíîâî. Íîâûé ïîðÿäîê îïðåäåëÿåòñÿ àêòóàëüíîé õåø-ôóíêöèåé è ðàçìåðîì òàáëèöû, à íå èñõîäíîé ïîñëåäîâàòåëüíîñòüþ âñòàâêè. +Èòîã: Âðåìÿ âûïîëíåíèÿ îïåðàöèé çàâèñèò îò êà÷åñòâà õåø-ôóíêöèè, êîýôôèöèåíòà çàïîëíåíèÿ òàáëèöû è ìåòîäà ðàçðåøåíèÿ êîëëèçèé, íî íå çàâèñèò îò ïîðÿäêà äîáàâëåíèÿ ýëåìåíòîâ. + + + + + +Ïî÷åìó ñâÿçíûé ñïèñîê âñåãäà ìåäëåíåí ïðè ïîèñêå + +Ñâÿçíûé ñïèñîê ïîêàçûâàåò íèçêóþ ñêîðîñòü ïîèñêà èç-çà íåîáõîäèìîñòè ïîñëåäîâàòåëüíîãî îáõîäà: ÷òîáû íàéòè ýëåìåíò, òðåáóåòñÿ ïðîéòè ïî óêàçàòåëÿì îò ãîëîâû äî íóæíîãî óçëà. +Ïî÷åìó ýòî ïðîèñõîäèò: +?Îòñóòñòâèå ïðîèçâîëüíîãî äîñòóïà. Â îòëè÷èå îò ìàññèâà, ãäå äîñòóï ïî èíäåêñó çàíèìàåò O(1), â ñâÿçíîì ñïèñêå ýëåìåíòû ïðèõîäèòñÿ ïåðåáèðàòü ïîñëåäîâàòåëüíî, ÷òî äà¸ò ñëîæíîñòü ïîèñêà O(n). +?Íèçêàÿ ëîêàëüíîñòü äàííûõ. Óçëû ñïèñêà ðàçáðîñàíû ïî ïàìÿòè ñëó÷àéíûì îáðàçîì. Ýòî âûçûâàåò ÷àñòûå ïðîìàõè êýøà: ïðîöåññîð íå ìîæåò ïîäãðóçèòü áëîê ñîñåäíèõ äàííûõ, è êàæäûé ïåðåõîä ïî óêàçàòåëþ îáîðà÷èâàåòñÿ íîâûì îáðàùåíèåì ê îïåðàòèâíîé ïàìÿòè. +?Äîïîëíèòåëüíàÿ ïàìÿòü íà óêàçàòåëè. Êàæäûé óçåë õðàíèò íå òîëüêî ïîëåçíûå äàííûå, íî è óêàçàòåëü íà ñëåäóþùèé ýëåìåíò. Ýòî óâåëè÷èâàåò îáú¸ì ïàìÿòè è óõóäøàåò ýôôåêòèâíîñòü êýøà — íà òå æå äàííûå ïðèõîäèòñÿ çàãðóæàòü áîëüøå èíôîðìàöèè. +?Çàòðàòû íà ðàçûìåíîâàíèå óêàçàòåëåé. Íà êàæäîì øàãå ïîèñêà ïðîöåññîðó íóæíî: +oïðî÷èòàòü òåêóùèé óçåë, +oèçâëå÷ü èç íåãî óêàçàòåëü íà ñëåäóþùèé, +oïåðåéòè ïî ýòîìó àäðåñó. +Ýòè îïåðàöèè çàìåäëÿþò ðàáîòó ïî ñðàâíåíèþ ñ ïðîñòûì ñäâèãîì èíäåêñà â ìàññèâå. +Èòîã: õîòÿ àëãîðèòìè÷åñêàÿ ñëîæíîñòü îáõîäà ñîñòàâëÿåò O(n) êàê äëÿ ìàññèâà (ïðè ëèíåéíîì ïîèñêå), òàê è äëÿ ñâÿçíîãî ñïèñêà, íà ïðàêòèêå ñïèñîê ðàáîòàåò îùóòèìî ìåäëåííåå èç-çà îñîáåííîñòåé îðãàíèçàöèè ïàìÿòè è ðàáîòû êýøà. + + + + +Êàê óäàëåíèå ðàáîòàåò â êàæäîé ñòðóêòóðå + +1. Ñâÿçíûé ñïèñîê +Îäíîñâÿçíûé ñïèñîê: ÷òîáû óäàëèòü óçåë, íåîáõîäèìî ñíà÷àëà íàéòè ïðåäûäóùèé ýëåìåíò è ïåðåíàïðàâèòü åãî óêàçàòåëü next íà óçåë, ñëåäóþùèé çà óäàëÿåìûì. Èñêëþ÷åíèå — óäàëåíèå ïåðâîãî ýëåìåíòà: äîñòàòî÷íî ñäâèíóòü óêàçàòåëü head íà âòîðîé óçåë. +Äâóñâÿçíûé ñïèñîê: óäàëåíèå ïðîùå, ïîñêîëüêó ó êàæäîãî óçëà åñòü óêàçàòåëè è íà ñëåäóþùèé (next), è íà ïðåäûäóùèé (prev). Ïðè óäàëåíèè îáíîâëÿþòñÿ ññûëêè îáîèõ ñîñåäåé: prev->next = next, next->prev = prev. +Ñëîæíîñòü: â îáùåì ñëó÷àå O(n) èç-çà íåîáõîäèìîñòè ïîèñêà ýëåìåíòà; óäàëåíèå ãîëîâû èëè õâîñòà (ïðè íàëè÷èè ïðÿìîé ññûëêè íà õâîñò) âûïîëíÿåòñÿ çà O(1). +2. Õåøòàáëèöà +Ñíà÷àëà ÷åðåç õåø-ôóíêöèþ h(key) âû÷èñëÿåòñÿ èíäåêñ ÿ÷åéêè. Äàëüíåéøèå äåéñòâèÿ çàâèñÿò îò ìåòîäà ðàçðåøåíèÿ êîëëèçèé: +?Ðàçäåëüíàÿ öåïî÷êà: ýëåìåíò óäàëÿåòñÿ èç ñâÿçíîãî ñïèñêà (èëè äðóãîé ñòðóêòóðû), íàõîäÿùåãîñÿ ïî âû÷èñëåííîìó èíäåêñó. +?Îòêðûòàÿ àäðåñàöèÿ: ÿ÷åéêà ïîìå÷àåòñÿ ñïåöèàëüíûì ìàðêåðîì «óäàë¸í», à íå ïðîñòî êàê ïóñòàÿ — ýòî âàæíî äëÿ êîððåêòíîñòè ïîñëåäóþùèõ îïåðàöèé ïîèñêà. +Ñëîæíîñòü: â ñðåäíåì O(1), â õóäøåì ñëó÷àå O(n) (ïðè áîëüøîì êîëè÷åñòâå êîëëèçèé). +3. Äâîè÷íîå äåðåâî ïîèñêà (BST) +Óäàëåíèå óçëà çàâèñèò îò êîëè÷åñòâà åãî ïîòîìêîâ: +?Íåò äåòåé (ëèñò): óçåë ïðîñòî óäàëÿåòñÿ, ññûëêà ðîäèòåëÿ îáíóëÿåòñÿ. +?Îäèí ðåá¸íîê: óäàëÿåìûé óçåë çàìåíÿåòñÿ åãî åäèíñòâåííûì ïîòîìêîì — ðîäèòåëü «ïåðåïðûãèâàåò» ÷åðåç óäàëÿåìûé óçåë. +?Äâà ðåá¸íêà: +1.Íàõîäèòñÿ ïðååìíèê (ñàìûé ëåâûé (íàèìåíüøèé) óçåë â ïðàâîì ïîääåðåâå) èëè ïðåäøåñòâåííèê (ñàìûé ïðàâûé (íàèáîëüøèé) óçåë â ëåâîì ïîääåðåâå). +2.Çíà÷åíèå ïðååìíèêà/ïðåäøåñòâåííèêà êîïèðóåòñÿ â óäàëÿåìûé óçåë. +3.Ïðååìíèê/ïðåäøåñòâåííèê ðåêóðñèâíî óäàëÿåòñÿ — îí ãàðàíòèðîâàííî èìååò íå áîëåå îäíîãî ðåá¸íêà. +Ñëîæíîñòü: O(h), ãäå h — âûñîòà äåðåâà.  ñáàëàíñèðîâàííîì äåðåâå h=O(logn), â íåñáàëàíñèðîâàííîì — äî O(n). + +Âûâîä +1. ×àñòûå âñòàâêè +Ñâÿçíûé ñïèñîê — îòëè÷íûé âûáîð äëÿ ÷àñòûõ âñòàâîê (îñîáåííî â ñåðåäèíó), åñëè íå òðåáóåòñÿ áûñòðûé äîñòóï ïî èíäåêñó. Âñòàâêà â íà÷àëî èëè êîíåö âûïîëíÿåòñÿ çà O(1), â ñåðåäèíó — çà O(n) (íî áåç ñäâèãà ýëåìåíòîâ, êàê â ìàññèâå). +Õåøòàáëèöà — õîðîøî ïîäõîäèò äëÿ âñòàâîê ïî êëþ÷ó, îáåñïå÷èâàÿ â ñðåäíåì O(1). +2. ×àñòûé ïîèñê +Õåøòàáëèöà — ëó÷øèé âàðèàíò äëÿ áûñòðîãî ïîèñêà ïî êëþ÷ó. Ñðåäíåå âðåìÿ — O(1), â õóäøåì ñëó÷àå — O(n) (ïðè ñèëüíûõ êîëëèçèÿõ). +Ñáàëàíñèðîâàííîå äâîè÷íîå äåðåâî ïîèñêà — ïðåäïî÷òèòåëüíåå, åñëè íóæåí ïîèñê ñ ãàðàíòèðîâàííîé ñëîæíîñòüþ O(logn) äàæå â õóäøåì ñëó÷àå. +3. Íåîáõîäèìîñòü ïîëó÷àòü äàííûå â îòñîðòèðîâàííîì ïîðÿäêå +Ìàññèâ / ñïèñîê — ýôôåêòèâåí, åñëè äàííûå óæå îòñîðòèðîâàíû èëè ñîðòèðîâêà ïðîèñõîäèò ðåäêî, à ïîñëåäîâàòåëüíîå ÷òåíèå — ÷àñòî. Äîñòóï ïî èíäåêñó — O(1), íî âñòàâêà è óäàëåíèå â ñåðåäèíó òðåáóþò O(n). +Îòñîðòèðîâàííûé ìàññèâ — óäîáåí äëÿ ïîèñêà (áèíàðíûé ïîèñê äà¸ò (O(logn)), îäíàêî âñòàâêè è óäàëåíèÿ îáõîäÿòñÿ â O(n). +Ñáàëàíñèðîâàííîå äâîè÷íîå äåðåâî ïîèñêà (BST) — àâòîìàòè÷åñêè ïîääåðæèâàåò îòñîðòèðîâàííûé ïîðÿäîê ýëåìåíòîâ. Âñå îñíîâíûå îïåðàöèè âûïîëíÿþòñÿ çà O(logn). Èäåàëüíûé âàðèàíò, êîãäà äàííûå ÷àñòî èçìåíÿþòñÿ è ïðè ýòîì òðåáóåòñÿ îáõîä ýëåìåíòîâ â îòñîðòèðîâàííîì ïîðÿäêå. diff --git a/LebedevES/заданиÑ�/задание 1/otchet2.txt b/LebedevES/заданиÑ�/задание 1/otchet2.txt new file mode 100644 index 00000000..a3541b1c --- /dev/null +++ b/LebedevES/заданиÑ�/задание 1/otchet2.txt @@ -0,0 +1,196 @@ +Ìåòîäû ïðîãðàììèðîâàíèÿ + + + +Ïîèñê âûõîäà èç ëàáèðèíòà. +Àíàëèç 2 çàäàíèÿ + + + + + + + +Áîáðîâ Ê. Í. +425 ãðóïïà + + +Ñîäåðæàíèå + +Îïèñàíèå çàäà÷è è âûáðàííûõ ïàòòåðíîâ 2 +Ëèñòèíãè êëþ÷åâûõ êëàññîâ 4 +Ðåçóëüòàòû ýêñïåðèìåíòîâ 6 +Àíàëèç ýôôåêòèâíîñòè àëãîðèòìîâ è ïðèìåíèìîñòè ïàòòåðíîâ 7 +Âûâîäû 9 + + + +Îïèñàíèå çàäà÷è è âûáðàííûõ ïàòòåðíîâ + + + +Îïèñàíèå çàäà÷è: ðåàëèçîâàòü ñèñòåìó äëÿ çàãðóçêè ëàáèðèíòîâ èç ôàéëîâ, ïîèñêà ïóòè îò ñòàðòà äî âûõîäà ñ èñïîëüçîâàíèåì ðàçëè÷íûõ àëãîðèòìîâ, ñáîðà ñòàòèñòèêè è âèçóàëèçàöèè. Êëþ÷åâûå òðåáîâàíèÿ — ãèáêîñòü, ðàñøèðÿåìîñòü è âîçìîæíîñòü äèíàìè÷åñêîé ñìåíû àëãîðèòìîâ. + +Âûáðàííûå ïàòòåðíû: + +?Builder - Ñêðûâàåò ñëîæíîñòü ñîçäàíèÿ ëàáèðèíòà èç òåêñòîâîãî ôàéëà (ïàðñèíã, âàëèäàöèÿ, óñòàíîâêà ôëàãîâ). Ïîçâîëÿåò ëåãêî äîáàâèòü ïîääåðæêó äðóãèõ ôîðìàòîâ (JSON, XML). +?Strategy - Îïðåäåëÿåò ñåìåéñòâî àëãîðèòìîâ ïîèñêà ïóòè (BFS, DFS, A*, Äåéêñòðà), èíêàïñóëèðóåò êàæäûé èç íèõ è äåëàåò èõ âçàèìîçàìåíÿåìûìè. Êëèåíò (MazeSolver) ìîæåò ïåðåêëþ÷àòü ñòðàòåãèè âî âðåìÿ âûïîëíåíèÿ. +?Observer - Îáåñïå÷èâàåò ðåàêòèâíîå îáíîâëåíèå êîíñîëüíîãî èíòåðôåéñà ïðè èçìåíåíèÿõ (çàãðóçêà ëàáèðèíòà, ïåðåìåùåíèå èãðîêà, íàéäåííûé ïóòü). Ïîçâîëÿåò äîáàâèòü äðóãèå ñïîñîáû âèçóàëèçàöèè (GUI, ëîãèðîâàíèå) áåç èçìåíåíèÿ áèçíåñ-ëîãèêè. +?Command - Ðåàëèçóåò ïîøàãîâîå óïðàâëåíèå èãðîêîì ñ âîçìîæíîñòüþ îòìåíû (undo). Ïîçâîëÿåò ñîõðàíÿòü èñòîðèþ êîìàíä è ïîääåðæèâàòü òðàíçàêöèîííîñòü. + + +Ëèñòèíãè êëþ÷åâûõ êëàññîâ + +Builder (TextFileMazeBuilder): +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 +Strategy (ïðèìåð BFS): +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" +Observer (ConsoleView): +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() + + +Ðåçóëüòàòû ýêñïåðèìåíòîâ (òàáëèöû, ãðàôèêè). +maze_type algorithm avg_time avg_visited_cells avg_path_len +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. +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 +no_exit_20x20 DFS 0.2512400002160575 380.0 0.0 +no_exit_20x20 A* 0.5590400000073714 380.0 0. +no_exit_20x20 Dijkstra 0.35640000060084276 380.0 0.0 + + + + + + + +Ãðàôèêè ïîñòðîåíû êîäîì èç ôàéëà RESULT22. + +Àíàëèç ýôôåêòèâíîñòè àëãîðèòìîâ è ïðèìåíèìîñòè ïàòòåðíîâ + +Àíàëèç àëãîðèòìîâ ïîèñêà ïóòè +?BFS ãàðàíòèðîâàííî íàõîäèò êðàò÷àéøèé ïóòü ïî êîëè÷åñòâó øàãîâ, íî â áîëüøèõ ëàáèðèíòàõ (îñîáåííî ïóñòûõ èëè ñèëüíî âåòâÿùèõñÿ) ïîñåùàåò î÷åíü ìíîãî êëåòîê. Âðåìÿ ðàáîòû ðàñò¸ò ïðîïîðöèîíàëüíî ÷èñëó äîñòèæèìûõ êëåòîê. +?DFS áûñòðî íàõîäèò êàêîé-ëèáî ïóòü, îäíàêî îí ÷àñòî îêàçûâàåòñÿ íåîïòèìàëüíûì (äëèííåå âîçìîæíîãî ìèíèìóìà).  ëàáèðèíòàõ ñ òóïèêàìè ìîæåò óõîäèòü â ãëóáîêóþ ðåêóðñèþ, ÷òî ïðèâîäèò ê áîëüøîìó êîëè÷åñòâó ïîñåù¸ííûõ êëåòîê. +?A ñ ìàíõýòòåíñêîé ýâðèñòèêîé* ïîêàçûâàåò íàèëó÷øóþ ýôôåêòèâíîñòü íà ñëîæíûõ ëàáèðèíòàõ: ïîñåùàåò çíà÷èòåëüíî ìåíüøå êëåòîê, ÷åì BFS, è ïðè ýòîì äà¸ò îïòèìàëüíûé ïóòü (áëàãîäàðÿ äîïóñòèìîñòè ýâðèñòèêè).  ïóñòîì ëàáèðèíòå ðàáîòàåò àíàëîãè÷íî BFS, íî ñ íåáîëüøèìè äîïîëíèòåëüíûìè íàêëàäíûìè ðàñõîäàìè íà ïîääåðæêó î÷åðåäè ñ ïðèîðèòåòîì. +?Àëãîðèòì Äåéêñòðû ïðè åäèíè÷íûõ âåñàõ ð¸áåð ýêâèâàëåíòåí BFS ïî ðåçóëüòàòó, íî ðàáîòàåò ìåäëåííåå èç-çà èñïîëüçîâàíèÿ êó÷è. Îí ñòàíîâèòñÿ ïîëåçíûì âî âçâåøåííûõ ëàáèðèíòàõ (íàïðèìåð, ñ áîëîòàìè èëè ïåñêîì), ãäå BFS äà¸ò íåîïòèìàëüíóþ ñòîèìîñòü ïóòè. +Ïðèìåíèìîñòü ïàòòåðíîâ ïðîåêòèðîâàíèÿ +?Builder ïîçâîëèë ïîëíîñòüþ èçîëèðîâàòü ôîðìàò ââîäà äàííûõ, ñêðûâ äåòàëè ïàðñèíãà îò îñíîâíîé ëîãèêè. +?Strategy îáåñïå÷èë âîçìîæíîñòü ïåðåêëþ÷åíèÿ àëãîðèòìîâ âî âðåìÿ âûïîëíåíèÿ (íàïðèìåð, â MazeSolver). Áåç ýòîãî ïàòòåðíà ïðèøëîñü áû èñïîëüçîâàòü óñëîâíûå îïåðàòîðû èëè íàñëåäîâàíèå, ÷òî íàðóøèëî áû ïðèíöèï îòêðûòîñòè/çàêðûòîñòè. +?Observer îòäåëèë âèçóàëèçàöèþ îò áèçíåñ-ëîãèêè. Ïðè çàìåíå êîíñîëüíîãî âûâîäà íà PyQt èëè âåá-èíòåðôåéñ äîñòàòî÷íî ðåàëèçîâàòü íîâîãî íàáëþäàòåëÿ — îñòàëüíîé êîä íå òðåáóåò èçìåíåíèé. +?Command óïðîñòèë ðåàëèçàöèþ îòìåíû/âîçâðàòà äåéñòâèé (undo/redo) è ïîçâîëèë äîáàâëÿòü ìàêðîêîìàíäû (íàïðèìåð, àâòîìàòè÷åñêîå ïðîõîæäåíèå ïî íàéäåííîìó ïóòè) áåç ìîäèôèêàöèè ñóùåñòâóþùèõ êëàññîâ. + +Âûâîäû +Äîñòèãíóòûå ïðåèìóùåñòâà +Ïðèìåíåíèå îáúåêòíî-îðèåíòèðîâàííîãî ïîäõîäà è ïàòòåðíîâ ïðîåêòèðîâàíèÿ îáåñïå÷èëî: +1.Ãèáêîñòü — ëåãêî äîáàâèòü íîâûé àëãîðèòì ïîèñêà (íàïðèìåð, âîëíîâîé àëãîðèòì) èëè íîâûé ôîðìàò ëàáèðèíòà. +2.Ðàñøèðÿåìîñòü — äëÿ èíòåãðàöèè ãðàôè÷åñêîãî èíòåðôåéñà äîñòàòî÷íî ðåàëèçîâàòü åù¸ îäíîãî íàáëþäàòåëÿ, íå èçìåíÿÿ MazeSolver è ñóùåñòâóþùèå ñòðàòåãèè. +3.Ïîääåðæèâàåìîñòü — êàæäûé ïàòòåðí èíêàïñóëèðóåò ðîâíî îäíó èçìåíÿþùóþñÿ õàðàêòåðèñòèêó: ñîçäàíèå îáúåêòîâ, àëãîðèòì ïîèñêà, ìåõàíèçì óâåäîìëåíèé, âûïîëíÿåìûå äåéñòâèÿ. +4.Òåñòèðóåìîñòü — ñòðàòåãèè ìîæíî òåñòèðîâàòü èçîëèðîâàííî äðóã îò äðóãà, ïîäñòàâëÿÿ mock-îáúåêòû òàì, ãäå ýòî íåîáõîäèìî. +×òî ïîòðåáîâàëî áû áîëüøèõ óñèëèé áåç ïàòòåðíîâ +?Ñìåíà àëãîðèòìà ïîèñêà âî âðåìÿ âûïîëíåíèÿ ïîòðåáîâàëà áû ïåðåïèñûâàíèÿ êîäà MazeSolver è âíåäðåíèÿ ãðîìîçäêèõ óñëîâíûõ îïåðàòîðîâ. +?Äîáàâëåíèå íîâîãî ôîðìàòà ëàáèðèíòà çàòðîíóëî áû ëîãèêó ïàðñèíãà âî ìíîãèõ ìåñòàõ, åñëè áû îíà áûëà ðàçìàçàíà ïî âñåìó êîäó, à íå âûíåñåíà â îòäåëüíûé ñòðîèòåëü (Builder). +?Ðåàëèçàöèÿ îòìåíû äåéñòâèé (undo) ïîòðåáîâàëà áû æ¸ñòêîé ïðèâÿçêè ê êîíêðåòíûì êîìàíäàì è íàðóøåíèÿ èíêàïñóëÿöèè èãðîêà. +?Âèçóàëèçàöèÿ îêàçàëàñü áû æ¸ñòêî ñâÿçàííîé ñ áèçíåñ-ëîãèêîé, ÷òî ñåðü¸çíî óñëîæíèëî áû ïåðåõîä íà äðóãîé èíòåðôåéñ (íàïðèìåð, ñ êîíñîëè íà PyQt èëè âåá). +Îáùèé âûâîä +Ïàòòåðíû ïðîåêòèðîâàíèÿ â ïîëíîé ìåðå îïðàâäàëè ñâî¸ ïðèìåíåíèå â äàííîì ïðîåêòå: ñèñòåìà ñòàëà ëåãêî ðàñøèðÿåìîé, õîðîøî ñòðóêòóðèðîâàííîé è ãîòîâîé ê áóäóùèì èçìåíåíèÿì áåç íåîáõîäèìîñòè ïåðåïèñûâàòü ñóùåñòâóþùèé êîä. diff --git a/LebedevES/заданиÑ�/задание 1/results1.csv b/LebedevES/заданиÑ�/задание 1/results1.csv new file mode 100644 index 00000000..0c545146 --- /dev/null +++ b/LebedevES/заданиÑ�/задание 1/results1.csv @@ -0,0 +1,19 @@ +Structure,Mode,Operation,Time_seconds +LinkedList,random,insert,7.967956480104476 +LinkedList,random,find,0.05891917999833822 +LinkedList,random,delete,0.03816298004239797 +HashTable,random,insert,0.39825033992528913 +HashTable,random,find,0.002917400002479553 +HashTable,random,delete,0.0021501399576663973 +BST,random,insert,0.02822491992264986 +BST,random,find,0.00023473985493183136 +BST,random,delete,0.00016456004232168198 +LinkedList,sorted,insert,8.014810599852353 +LinkedList,sorted,find,0.058480959851294756 +LinkedList,sorted,delete,0.04817821998149156 +HashTable,sorted,insert,0.3703480200842023 +HashTable,sorted,find,0.002751259971410036 +HashTable,sorted,delete,0.0018340200185775757 +BST,sorted,insert,7.301413399912417 +BST,sorted,find,0.06847236007452011 +BST,sorted,delete,0.03443789994344115