diff --git a/SimonovaMS/428.txt b/SimonovaMS/428.txt deleted file mode 100644 index e69de29..0000000 diff --git a/SimonovaMS/analys_report.txt b/SimonovaMS/analys_report.txt new file mode 100644 index 0000000..42aea90 --- /dev/null +++ b/SimonovaMS/analys_report.txt @@ -0,0 +1,50 @@ +['Structura', 'shuffled/sorted', 'Operation', 'Time'] +LinkedList | shuffled | insert | 3.798362 +LinkedList | shuffled | find | 0.028610 +LinkedList | shuffled | delete | 0.035444 +LinkedList | sorted | insert | 3.117239 +LinkedList | sorted | find | 0.020465 +LinkedList | sorted | delete | 0.028734 +HashTable | shuffled | insert | 0.013259 +HashTable | shuffled | find | 0.000109 +HashTable | shuffled | delete | 0.000079 +HashTable | sorted | insert | 0.014760 +HashTable | sorted | find | 0.000107 +HashTable | sorted | delete | 0.000076 +Bst | shuffled | insert | 0.020712 +Bst | shuffled | find | 0.000246 +Bst | shuffled | delete | 0.000096 +Bst | sorted | insert | 3.905296 +Bst | sorted | find | 0.029092 +Bst | sorted | delete | 0.018350 + +Результаты: +Структура Режим вставка поиск удаление +LinkedList shuffled 3.798362 0.028610 0.035444 +LinkedList sorted 3.117239 0.020465 0.028734 +HashTable shuffled 0.013259 0.000109 0.000079 +HashTable sorted 0.014760 0.000107 0.000076 +Bst shuffled 0.020712 0.000246 0.000096 +Bst sorted 3.905296 0.029092 0.018350 +График +График сохранён в файл: results_plot.png + +Анализ: + +ВСТАВКА: + Лучшая: HashTable (0.014010 сек) + Худшая: LinkedList (3.457801 сек) + +ПОИСК: + Лучшая: HashTable (0.000108 сек) + Худшая: LinkedList (0.024537 сек) + +УДАЛЕНИЕ: + Лучшая: HashTable (0.000077 сек) + Худшая: LinkedList (0.032089 сек) + +Вывод: +Для вставок, поиска и удаления лучше всего использовать HashTable как для отсортированных, так и для неотсортированных данных +BST неплох для отсортированных данных, но всё равно хуже HashTable +LinkedList показал худшие результаты +HashTable - оптимальный выбор для телефонного справочника diff --git a/SimonovaMS/analyz.py b/SimonovaMS/analyz.py new file mode 100644 index 0000000..14d1992 --- /dev/null +++ b/SimonovaMS/analyz.py @@ -0,0 +1,121 @@ +import csv +import matplotlib.pyplot as plt +import numpy as np +from collections import defaultdict +import os + +report_file = open("analys_report.txt", "w", encoding="utf-8") +data = defaultdict(lambda: defaultdict(dict)) + +with open("C:/Users/Honor/Documents/dep2k/lab_inf_1/data/results.csv", "r", encoding="utf-8") as f: + reader = csv.reader(f) + header = next(reader) + print(f"{header}") + report_file.write(f"{header}\n") + + for row in reader: + if len(row) >= 4: + struct = row[0] # LinkedList, HashTable, Bst + mode = row[1] # shuffled или sorted + op = row[2] # insert, find, delete + time_val = float(row[3]) + + data[struct][mode][op] = time_val + print(f"{struct} | {mode} | {op} | {time_val:.6f}") + report_file.write(f"{struct} | {mode} | {op} | {time_val:.6f}\n") + +op_names = { + 'insert': 'вставка', + 'find': 'поиск', + 'delete': 'удаление' +} + +structures = ["LinkedList", "HashTable", "Bst"] +modes = ["shuffled", "sorted"] +operations = ["insert", "find", "delete"] + +print("Результаты:") +report_file.write("\nРезультаты:\n") +print(f"{'Структура':<15} {'Режим':<10} {'вставка':<15} {'поиск':<15} {'удаление':<15}") +report_file.write(f"{'Структура':<15} {'Режим':<10} {'вставка':<15} {'поиск':<15} {'удаление':<15}\n") + +for struct in structures: + for mode in modes: + insert_time = data[struct][mode]['insert'] + find_time = data[struct][mode]['find'] + delete_time = data[struct][mode]['delete'] + print(f"{struct:<15} {mode:<10} {insert_time:<15.6f} {find_time:<15.6f} {delete_time:<15.6f}") + report_file.write(f"{struct:<15} {mode:<10} {insert_time:<15.6f} {find_time:<15.6f} {delete_time:<15.6f}\n") + +#графики +fig, axes = plt.subplots(1, 3, figsize=(15, 5)) + +for idx, op in enumerate(operations): + ax = axes[idx] + + x = np.arange(len(structures)) + width = 0.35 + + shuffled_vals = [data[s]["shuffled"][op] for s in structures] + sorted_vals = [data[s]["sorted"][op] for s in structures] + + bars1 = ax.bar(x - width/2, shuffled_vals, width, label='shuffled', color='orange', alpha=0.8) + bars2 = ax.bar(x + width/2, sorted_vals, width, label='sorted', color='cyan', alpha=0.8) + + ax.set_xlabel('Структура') + ax.set_ylabel('Время (сек)') + ax.set_title(f'{op_names[op]}') + ax.set_xticks(x) + ax.set_xticklabels(structures, rotation=45) + ax.legend() + ax.set_yscale('log') + + for bar in bars1: + height = bar.get_height() + ax.text(bar.get_x() + bar.get_width()/2, height, + f'{height:.3f}', ha='center', va='bottom', fontsize=8) + for bar in bars2: + height = bar.get_height() + ax.text(bar.get_x() + bar.get_width()/2, height, + f'{height:.3f}', ha='center', va='bottom', fontsize=8) + +plt.tight_layout() +plot_filename = "results_plot.png" +plt.savefig('results_plot.png', dpi=150) +plt.show() + +report_file.write("График\n") +report_file.write(f"График сохранён в файл: {plot_filename}\n") + +print("Анализ:") +report_file.write("\nАнализ:\n") + +for op in operations: + print(f"\n{op_names[op].upper()}:") + report_file.write(f"\n{op_names[op].upper()}:\n") + + # Среднее по двум режимам + avg_times = [] + for s in structures: + avg = (data[s]["shuffled"][op] + data[s]["sorted"][op]) / 2 + avg_times.append((s, avg)) + + avg_times.sort(key=lambda x: x[1]) + print(f" Лучшая: {avg_times[0][0]} ({avg_times[0][1]:.6f} сек)") + print(f" Худшая: {avg_times[-1][0]} ({avg_times[-1][1]:.6f} сек)") + report_file.write(f" Лучшая: {avg_times[0][0]} ({avg_times[0][1]:.6f} сек)\n") + report_file.write(f" Худшая: {avg_times[-1][0]} ({avg_times[-1][1]:.6f} сек)\n") + + +print("Вывод:") +report_file.write("\nВывод:\n") +print("Для вставок, поиска и удаления лучше всего использовать HashTable как для отсортированных, так и для неотсортированных данных") +print("BST неплох для отсортированных данных, но всё равно хуже HashTable") +print("LinkedList показал худшие результаты") +print("HashTable - оптимальный выбор для телефонного справочника") + +report_file.write("Для вставок, поиска и удаления лучше всего использовать HashTable как для отсортированных, так и для неотсортированных данных\n") +report_file.write("BST неплох для отсортированных данных, но всё равно хуже HashTable\n") +report_file.write("LinkedList показал худшие результаты\n") +report_file.write("HashTable - оптимальный выбор для телефонного справочника\n") +report_file.close() \ No newline at end of file diff --git a/SimonovaMS/generator.py b/SimonovaMS/generator.py new file mode 100644 index 0000000..4907297 --- /dev/null +++ b/SimonovaMS/generator.py @@ -0,0 +1,29 @@ +import random +from typing import List, Tuple + +def generate_data(n=10000): + records = [] + for i in range(n): + name = f"User_{i:05d}" + phone = f"8{random.randint(900,999)}{random.randint(100,999)}{random.randint(0,9)}{random.randint(0,9)}{random.randint(0,9)}{random.randint(0,9)}" + records.append((name,phone)) + + records_shuffled = records.copy() + random.shuffle(records_shuffled) + records_sorted = sorted(records, key=lambda x:x[0]) + + return records_shuffled, records_sorted + +def generate_search(records, exist_count=100, no_exist_count=10): + exist_names = [name for name, _ in records] + select_exist = random.sample(exist_names, min(exist_count, len(exist_names))) + + no_exist_count=[f"None_{i:05d}" for i in range(no_exist_count)] + + return select_exist + no_exist_count + +def generate_delete(records, count=50): + names = [name for name, _ in records] + return random.sample(names, min(count, len(names))) + + diff --git a/SimonovaMS/lab2/experiments.py b/SimonovaMS/lab2/experiments.py new file mode 100644 index 0000000..7a48d31 --- /dev/null +++ b/SimonovaMS/lab2/experiments.py @@ -0,0 +1,200 @@ +# experiments.py +import time +import csv +from typing import List, Dict +from maze_model import Maze +from maze_builder import TextFileMazeBuilder +from pathfinding_strategies import BFSStrategy, DFSStrategy, AStarStrategy +from maze_solver import MazeSolver, SearchStats + + +class ExperimentRunner: + + def __init__(self): + self.builder = TextFileMazeBuilder() + self.strategies = [ + BFSStrategy(), + DFSStrategy(), + AStarStrategy(), + ] + self.results: List[Dict] = [] + + def create_test_maze_file(self, filename: str, maze_data: List[str]) -> None: + with open(filename, 'w', encoding='utf-8') as f: + f.write('\n'.join(maze_data)) + + def generate_simple_maze(self) -> List[str]: + maze = [ + "S E", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " " + ] + return maze + + def generate_complex_maze(self, size: int = 50) -> List[str]: + import random + random.seed(42) + + maze = [] + for y in range(size): + row = [] + for x in range(size): + if (x == 0 and y == 0): + row.append('S') + elif (x == size - 1 and y == size - 1): + row.append('E') + elif random.random() < 0.3: # 30% стен + row.append('#') + else: + row.append(' ') + maze.append(''.join(row)) + + for i in range(size): + if maze[i][0] == '#': + row = list(maze[i]) + row[0] = ' ' + maze[i] = ''.join(row) + if maze[0][i] == '#': + row = list(maze[0]) + row[i] = ' ' + maze[0] = ''.join(row) + + return maze + + def generate_empty_maze(self, size: int = 50) -> List[str]: + maze = [] + for y in range(size): + row = [] + for x in range(size): + if x == 0 and y == 0: + row.append('S') + elif x == size - 1 and y == size - 1: + row.append('E') + else: + row.append(' ') + maze.append(''.join(row)) + return maze + + def generate_no_exit_maze(self, size: int = 20) -> List[str]: + maze = [] + for y in range(size): + row = [] + for x in range(size): + if x == 0 and y == 0: + row.append('S') + elif x == size - 1 and y == size - 1: + row.append('#') # Выход заблокирован + else: + row.append('#') # Всё стены + maze.append(''.join(row)) + + # выход в тупике + row = list(maze[size - 1]) + row[size - 1] = 'E' + maze[size - 1] = ''.join(row) + + return maze + + def run_experiment(self, maze_name: str, maze_data: List[str], + num_runs: int = 5) -> List[Dict]: + filename = f"test_{maze_name}.txt" + self.create_test_maze_file(filename, maze_data) + + maze = self.builder.build_from_file(filename) + results = [] + + for strategy in self.strategies: + solver = MazeSolver(maze, strategy) + + times = [] + path_lengths = [] + + for run in range(num_runs): + stats = solver.solve() + times.append(stats.time_ms) + path_lengths.append(stats.path_length) + + avg_time = sum(times) / len(times) + avg_path_length = sum(path_lengths) / len(path_lengths) + + result = { + 'maze': maze_name, + 'strategy': strategy.name, + 'avg_time_ms': round(avg_time, 3), + 'min_time_ms': round(min(times), 3), + 'max_time_ms': round(max(times), 3), + 'path_length': int(avg_path_length) if avg_path_length else 0, + 'path_found': avg_path_length > 0 + } + results.append(result) + + print(f"{maze_name} - {strategy.name}: " + f"{avg_time:.3f} мс, путь: {int(avg_path_length)}") + + return results + + def run_all_experiments(self): + + experiments = [ + ("simple_10x10", self.generate_simple_maze()), + ("complex_50x50", self.generate_complex_maze(50)), + ("large_100x100", self.generate_complex_maze(100)), + ("empty_50x50", self.generate_empty_maze(50)), + ("no_exit_20x20", self.generate_no_exit_maze(20)) + ] + + all_results = [] + + for name, data in experiments: + print(f"\n Лабиринт: {name} ---") + results = self.run_experiment(name, data) + all_results.extend(results) + + self.save_to_csv(all_results, "experiment_results.csv") + + + + return all_results + + def save_to_csv(self, results: List[Dict], filename: str): + if not results: + return + + with open(filename, 'w', newline='', encoding='utf-8') as csvfile: + fieldnames = results[0].keys() + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(results) + + +def print_analysis(results: List[Dict]): + + # Группировка + mazes = set(r['maze'] for r in results) + + for maze in sorted(mazes): + print(f"\nЛабиринт: {maze}") + print("-" * 40) + + maze_results = [r for r in results if r['maze'] == maze] + + #по времени + sorted_results = sorted(maze_results, key=lambda x: x['avg_time_ms']) + + for r in sorted_results: + status = "✓" if r['path_found'] else "✗" + print(f" {status} {r['strategy']:8} | " + f"Время: {r['avg_time_ms']:8.3f} мс | " + f"Путь: {r['path_length']:4} шагов") + + # Определяем лучший + fastest = sorted_results[0] + print(f"\n → Самый быстрый: {fastest['strategy']} " + f"({fastest['avg_time_ms']:.3f} мс)") \ No newline at end of file diff --git a/SimonovaMS/lab2/main.py b/SimonovaMS/lab2/main.py new file mode 100644 index 0000000..c3ec3d0 --- /dev/null +++ b/SimonovaMS/lab2/main.py @@ -0,0 +1,146 @@ +import sys +from maze_builder import TextFileMazeBuilder +from pathfinding_strategies import BFSStrategy, DFSStrategy, AStarStrategy +from maze_solver import MazeSolver +from visualization import ConsoleView, GameController, EventType +from experiments import ExperimentRunner, print_analysis +from analysis import plot_results + +def create_sample_maze(): + sample_maze = [ + "S ##### ", + "# # ### ", + "# # # # ", + "# # ### # ", + "# # # ", + "### # ### ", + "# # # ", + "# ####### ", + "# E ", + "##########" + ] + + filename = "sample_maze.txt" + with open(filename, 'w', encoding='utf-8') as f: + f.write('\n'.join(sample_maze)) + + return filename + + +def interactive_mode(): + + + builder = TextFileMazeBuilder() + filename = create_sample_maze() + + try: + maze = builder.build_from_file(filename) + print(f"Лабиринт загружен: {maze.width}x{maze.height}") + except Exception as e: + print(f"Ошибка загрузки: {e}") + return + + view = ConsoleView() + controller = GameController(maze, view) + + strategies = { + '1': BFSStrategy(), + '2': DFSStrategy(), + '3': AStarStrategy(), + } + + print("\nДоступные алгоритмы поиска пути:") + print(" 1. BFS (поиск в ширину) - кратчайший путь") + print(" 2. DFS (поиск в глубину) - быстрый, не оптимальный") + print(" 3. A* - оптимальный с эвристикой") + + # Выбор стратегии + while True: + choice = input("\nВыберите алгоритм (1-3): ").strip() + if choice in strategies: + strategy = strategies[choice] + break + print("Неверный выбор. Попробуйте снова.") + + # Поиск пути + print(f"\nИспользуем: {strategy.name}") + print("Поиск пути...") + + solver = MazeSolver(maze, strategy) + stats = solver.solve() + + if stats.path_found: + print(f" Путь найден! Победа! Длина: {stats.path_length} шагов") + print(f" Время: {stats.time_ms:.3f} мс") + + path = strategy.find_path(maze, maze.start, maze.exit) + controller.set_path(path) + + # Интерактивное управление + print("\nДемонстрация паттерна Command:") + print(" Используйте W/A/S/D для перемещения") + print(" Нажмите U для отмены последнего хода") + print(" Нажмите Q для выхода") + print("\nТочка '.' показывает найденный путь") + print("Буква 'P' показывает текущую позицию игрока") + + controller._render() + + while True: + key = input("\n> ").lower() + if key == 'q': + break + elif key == 'w': + from visualization import Direction + controller.move(Direction.UP) + elif key == 's': + from visualization import Direction + controller.move(Direction.DOWN) + elif key == 'a': + from visualization import Direction + controller.move(Direction.LEFT) + elif key == 'd': + from visualization import Direction + controller.move(Direction.RIGHT) + elif key == 'u': + controller.undo() + print("Ход отменён!") + else: + print("Команды: W(вверх), S(вниз), A(влево), D(вправо), U(отмена), Q(выход)") + else: + print("Путь не найден, грустно") + + +def experimental_mode(): + print("эксперименты") + print("Запуск экспериментов на лабиринтах разной сложности...") + + runner = ExperimentRunner() + results = runner.run_all_experiments() + print_analysis(results) + + #графики + plot_results(results) + + +def main(): + + + print("\nВыберите режим работы:") + print(" 1. Интерактивный режим (с визуализацией)") + print(" 2. Экспериментальный режим (замеры производительности)") + print(" 3. Выход") + + choice = input("\nВаш выбор (1-3): ").strip() + + if choice == '1': + interactive_mode() + elif choice == '2': + experimental_mode() + else: + print("Adios!") + sys.exit(0) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/SimonovaMS/lab2/maze_builder.py b/SimonovaMS/lab2/maze_builder.py new file mode 100644 index 0000000..7522a27 --- /dev/null +++ b/SimonovaMS/lab2/maze_builder.py @@ -0,0 +1,65 @@ +from abc import ABC, abstractmethod +from typing import Tuple +import os +from maze_model import Maze, Cell + + +class MazeBuilder(ABC): + + @abstractmethod + def build_from_file(self, filename: str) -> Maze: + pass + + +class TextFileMazeBuilder(MazeBuilder): + + def build_from_file(self, filename: str) -> Maze: + if not os.path.exists(filename): + raise FileNotFoundError(f"Файл {filename} не найден..") + + with open(filename, 'r', encoding='utf-8') as file: + lines = [line.rstrip('\n') for line in file.readlines()] + + if not lines: + raise ValueError("Пусто(") + + height = len(lines) + width = len(lines[0]) if lines else 0 + + for i, line in enumerate(lines): + if len(line) != width: + raise ValueError(f"Лабиринт не прямоугольный, что-то не так с размерами!") + + maze = Maze(width, height) + start_found = False + exit_found = False + + for y, line in enumerate(lines): + for x, char in enumerate(line): + cell = Cell(x, y) + + if char == '#': + cell.is_wall = True + elif char == 'S': + cell.is_start = True + cell.is_wall = False + maze.start = cell + start_found = True + elif char == 'E': + cell.is_exit = True + cell.is_wall = False + maze.exit = cell + exit_found = True + elif char == ' ': + cell.is_wall = False + else: + raise ValueError(f"Недопустимый символ-'{char}' в позиции ({x}, {y}), уберите его") + + maze.set_cell(x, y, cell) + + if not start_found: + raise ValueError("В лабиринте нет начала") + if not exit_found: + raise ValueError("В лабиринте нет конца") + + return maze \ No newline at end of file diff --git a/SimonovaMS/lab2/maze_model.py b/SimonovaMS/lab2/maze_model.py new file mode 100644 index 0000000..fcb5b98 --- /dev/null +++ b/SimonovaMS/lab2/maze_model.py @@ -0,0 +1,67 @@ +# maze_model.py +from __future__ import annotations +from typing import List, Optional +from dataclasses import dataclass + + +@dataclass +class Cell: + x: int + y: int + is_wall: bool = False + is_start: bool = False + is_exit: bool = False + + def is_passable(self) -> bool: + return not self.is_wall + + def __hash__(self) -> int: + return hash((self.x, self.y)) + + def __eq__(self, other) -> bool: + if not isinstance(other, Cell): + return False + return self.x == other.x and self.y == other.y + + +class Maze: + + def __init__(self, width: int, height: int): + self.width = width + self.height = height + self._cells: List[List[Cell]] = [] + self.start: Optional[Cell] = None + self.exit: Optional[Cell] = None + + for y in range(height): + row = [] + for x in range(width): + row.append(Cell(x, y)) + self._cells.append(row) + + def set_cell(self, x: int, y: int, cell: Cell) -> None: + if 0 <= x < self.width and 0 <= y < self.height: + self._cells[y][x] = cell + + def get_cell(self, x: int, y: int) -> Optional[Cell]: + if 0 <= x < self.width and 0 <= y < self.height: + return self._cells[y][x] + return None + + def get_neighbors(self, cell: Cell) -> List[Cell]: + neighbors = [] + # вверх, вниз, влево, вправо + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + + for dx, dy in directions: + neighbor = self.get_cell(cell.x + dx, cell.y + dy) + if neighbor and neighbor.is_passable(): + neighbors.append(neighbor) + + return neighbors + + def get_all_cells(self) -> List[Cell]: + cells = [] + for row in self._cells: + cells.extend(row) + return cells \ No newline at end of file diff --git a/SimonovaMS/lab2/maze_solver.py b/SimonovaMS/lab2/maze_solver.py new file mode 100644 index 0000000..dcb5d8c --- /dev/null +++ b/SimonovaMS/lab2/maze_solver.py @@ -0,0 +1,52 @@ +import time +from dataclasses import dataclass +from typing import List, Optional +from maze_model import Maze, Cell +from pathfinding_strategies import PathFindingStrategy + + +@dataclass +class SearchStats: + time_ms: float + visited_cells: int + path_length: int + path_found: bool + strategy_name: str + + +class MazeSolver: + def __init__(self, maze: Maze, strategy: Optional[PathFindingStrategy] = None): + self.maze = maze + self._strategy = strategy + + def set_strategy(self, strategy: PathFindingStrategy) -> None: + self._strategy = strategy + + def solve(self) -> SearchStats: + if self._strategy is None: + raise ValueError("Стратегии нет!") + + if self.maze.start is None or self.maze.exit is None: + raise ValueError("Лабиринт не содержит начала или конца") + + start_time = time.perf_counter() + + if hasattr(self._strategy, '_find_path_with_stats'): + path, visited = self._strategy._find_path_with_stats( + self.maze, self.maze.start, self.maze.exit + ) + else: + path = self._strategy.find_path( + self.maze, self.maze.start, self.maze.exit + ) + visited = 0 + + end_time = time.perf_counter() + + return SearchStats( + time_ms=(end_time - start_time) * 1000, + visited_cells=visited, + path_length=len(path) if path else 0, + path_found=len(path) > 0, + strategy_name=self._strategy.name + ) \ No newline at end of file diff --git a/SimonovaMS/lab2/otchet_l2.docx b/SimonovaMS/lab2/otchet_l2.docx new file mode 100644 index 0000000..58a0237 Binary files /dev/null and b/SimonovaMS/lab2/otchet_l2.docx differ diff --git a/SimonovaMS/lab2/pathfinding_strategies.py b/SimonovaMS/lab2/pathfinding_strategies.py new file mode 100644 index 0000000..9b29d5b --- /dev/null +++ b/SimonovaMS/lab2/pathfinding_strategies.py @@ -0,0 +1,142 @@ +from abc import ABC, abstractmethod +from typing import List, Dict, Optional, Tuple +from collections import deque +import heapq +from maze_model import Maze, Cell + + +class PathFindingStrategy(ABC):#интерфейс стратегии поиска + + @abstractmethod + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + pass + + @property + @abstractmethod + def name(self) -> str: + pass + + + +class BFSStrategy(PathFindingStrategy):#в ширину + @property + def name(self) -> str: + return "BFS" + + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + path, _ = self._find_path_with_stats(maze, start, exit_cell) + return path + + def _find_path_with_stats(self, maze: Maze, start: Cell, exit_cell: Cell) -> tuple: + if start == exit_cell: + return [start], 1 + + from collections import deque + queue = deque([start]) + visited = {start} + parent = {start: None} + + while queue: + current = queue.popleft() + + if current == exit_cell: + return self._reconstruct_path(parent, exit_cell), len(visited) + + for neighbor in maze.get_neighbors(current): + if neighbor not in visited: + visited.add(neighbor) + parent[neighbor] = current + queue.append(neighbor) + + return [], len(visited) + + def _reconstruct_path(self, parent: dict, exit_cell: Cell) -> List[Cell]: + path = [] + current = exit_cell + while current is not None: + path.append(current) + current = parent[current] + return list(reversed(path)) + + +class DFSStrategy(PathFindingStrategy):#в глубину + @property + def name(self) -> str: + return "DFS" + + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + path, _ = self._find_path_with_stats(maze, start, exit_cell) + return path + + def _find_path_with_stats(self, maze: Maze, start: Cell, exit_cell: Cell) -> tuple: + if start == exit_cell: + return [start], 1 + + stack = [(start, [start])] + visited = {start} + + while stack: + current, path = stack.pop() + + if current == exit_cell: + return path, len(visited) + + for neighbor in maze.get_neighbors(current): + if neighbor not in visited: + visited.add(neighbor) + stack.append((neighbor, path + [neighbor])) + + return [], len(visited) + + +class AStarStrategy(PathFindingStrategy): #A* + @property + def name(self) -> str: + return "A*" + + def _heuristic(self, cell: Cell, target: Cell) -> int: + return abs(cell.x - target.x) + abs(cell.y - target.y) + + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + path, _ = self._find_path_with_stats(maze, start, exit_cell) + return path + + def _find_path_with_stats(self, maze: Maze, start: Cell, exit_cell: Cell) -> tuple: + import heapq + + if start == exit_cell: + return [start], 1 + + counter = 0 + open_set = [(0, counter, start)] + came_from = {} + visited = {start} + + g_score = {start: 0} + f_score = {start: self._heuristic(start, exit_cell)} + + while open_set: + current = heapq.heappop(open_set)[2] + + if current == exit_cell: + return self._reconstruct_path(came_from, exit_cell), len(visited) + + for neighbor in maze.get_neighbors(current): + visited.add(neighbor) + tentative_g = g_score[current] + 1 + + if neighbor not in g_score or tentative_g < g_score[neighbor]: + came_from[neighbor] = current + g_score[neighbor] = tentative_g + f_score[neighbor] = tentative_g + self._heuristic(neighbor, exit_cell) + counter += 1 + heapq.heappush(open_set, (f_score[neighbor], counter, neighbor)) + + return [], len(visited) + + def _reconstruct_path(self, came_from: dict, current: Cell) -> List[Cell]: + path = [current] + while current in came_from: + current = came_from[current] + path.append(current) + return list(reversed(path)) \ No newline at end of file diff --git a/SimonovaMS/lab2/sample_maze.txt b/SimonovaMS/lab2/sample_maze.txt new file mode 100644 index 0000000..c51ff60 --- /dev/null +++ b/SimonovaMS/lab2/sample_maze.txt @@ -0,0 +1,10 @@ +S ##### +# # ### +# # # # +# # ### # +# # # +### # ### +# # # +# ####### +# E +########## \ No newline at end of file diff --git a/SimonovaMS/lab2/test_complex_50x50.txt b/SimonovaMS/lab2/test_complex_50x50.txt new file mode 100644 index 0000000..990951b --- /dev/null +++ b/SimonovaMS/lab2/test_complex_50x50.txt @@ -0,0 +1,50 @@ +S + ## # # ## ## ## # # ### # + # # # # # # # # # # # # # # # # + ## ### # ## ## ## # ## ## # + # # # # ## # # ## ## # # # + # ### # # # ### # # # # ## # ## + ## ### # # # # # ### # + # # ## # # # ## ## + # ## #### # # # # # # ## + ## #### ## # # # ## # # + # # # # # ### #### # # # ## + # # # # # # # # ## ## + # ## ##### ## ###### # # + ## # ## # # ## #### ## + ## ## ## ## ## # # # + # # # ## # # # # # + ## # # # # # # + ## # # # # ## # # ### # # # # + # # # # ## ## # # # + # ### ## # # # # # # + # ## # ## # ## # # #### ## # ## # + # ## ## # # # # # # ## + # # ## # ## # # # # # + # # # # # # # ### # # # ## ## + # # # ### # ## ## # # + ### ## # # ## # # + ## ### # # # # # # # + ## # # # # # ## # # ## # ### # + # # # ## # # # ## # # # + ### # # # # # # # # + # ## ## ## # # # # + ### # # # # #### # # + # ## # ### # # #### # # + # # # # # # # # ## + # # # # # # # ## # ## + # ## # ### ## ## # # # # + # # # # # # # # # # ## + ## # # ## ### # ## # # ### + # # # # # ## # # # # # # + # #### # # # #### # ## # # + # # # # ### # ## # + # # # # # # ### # # # # + ## # # # # # # #### # # + ### # ## ## # ### # # + ## # ## ## ### # # # # # # # + ### ## # # # # # # + # # # # ## ## # # + # # # ### # # # # # ## # # + # ### # # # # # ## ## ## # ## + # # # # ##### # ## # # #E \ No newline at end of file diff --git a/SimonovaMS/lab2/test_empty_50x50.txt b/SimonovaMS/lab2/test_empty_50x50.txt new file mode 100644 index 0000000..3d2a539 --- /dev/null +++ b/SimonovaMS/lab2/test_empty_50x50.txt @@ -0,0 +1,50 @@ +S + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + E \ No newline at end of file diff --git a/SimonovaMS/lab2/test_large_100x100.txt b/SimonovaMS/lab2/test_large_100x100.txt new file mode 100644 index 0000000..7d93a21 --- /dev/null +++ b/SimonovaMS/lab2/test_large_100x100.txt @@ -0,0 +1,100 @@ +S + # # # # # # # # # # # # # # # # ## ### # ## ## ## # ## ## # + # # # # ## # # ## ## # # # # # ### # # # ### # # # # ## # ## + ## ### # # # # # ### # # # ## # # # ## ## + # ## #### # # # # # # ## ## #### ## # # # ## # # + # # # # # ### #### # # # ## # # # # # # # # # ## ## + # ## ##### ## ###### # # ## # ## # # ## #### ## + ## ## ## ## ## # # # # # # ## # # # # # + ## # # # # # # ## # # # # ## # # ### # # # # + # # # # ## ## # # # # ### ## # # # # # # + # ## # ## # ## # # #### ## # ## # # # ## ## # # # # # # ## + # # ## # ## # # # # ### # # # # # # ### # # # ## ## + # # # ### # ## ## # # ### ## # # ## # # + ## ### # # # # # # # # ## # # # # # ## # # ## # ### # + # # # ## # # # ## # # # #### # # # # # # # # + # ## ## ## # # # # ### # # # # #### # # + # ## # ### # # #### # # # # # # # # # # ## + # # # # # # # ## # ### # ## # ### ## ## # # # # + # # # # # # # # # # ### ## # # ## ### # ## # # ### + # # # # # ## # # # # # # # #### # # # #### # ## # # + # # # # ### # ## # # # # # # # ### # # # # + ## # # # # # # #### # # # ### # ## ## # ### # # + ## # ## ## ### # # # # # # # ### ## # # # # # # + # # # # ## ## # # # # # ### # # # # # ## # # + # ### # # # # # ## ## ## # ## # # # # ##### # ## # # # + # # # # # # ## ## # # # # # # # # # # ## # ## # # # # # # ## + ### ## # # ##### # # # # ## # # ## # # ## # + # # # # # ## # # # ## # ## # # # # # # ### # ## ### ## + ### # ## # # # ## # # ## # # # # #### # ## #### # # # # # # # + # # # ### # ## # # ## # # # # # # # # # # ###### # + ## # ## # # # #### #### # # ##### # # # ### # # # # # + # # # # ## ### # # # # # # # ## # # ## # # ## # # # # + # # # ## # # ### # ## # # # # #### # # ## # ## + ## # ## # # ## # # # # # ## # # # # # # # # # # # # ###### # + ## # # ## ### # #### # # # # # # # # # # # # # ## # # # # # + ## # # # ## # ## # # # ### # # # # # # # # # # + # # # # ## # ### # # # # # ## ## ## # # ## # ### + ### # # # # # # ## # # ## ## # # # # # # + ## ## ## ### # # ### # # # # ### # # # # # + # # ## # # # ### ## ## ## ## # # ### # ## # # # # ## ## # + # # # # ## # # # ## # # # # ## # ### #### # ## ## + ### ### # # # ### ### # # ## # # # ### ## # ## # + # ## # # # ### #### # # # # ### # # # ## ### ## # # + #### # ### ## # # # # # # # # ### # # # # ## # ### ### + # # # # # # # # # # ## ### ## ### # ## # # # ## # #### # + ## # # # # # # # # # # # ### # # # # # ## # # # + # # ## # # # # ## # # # # ## ## ## # # ## # ## # # ## # + ## # # ## # # # # ### # # # # # # # ## # # # ## # ### ## # # + # # ## # ## ### ## # # # # ## # # # # # # + # ## # # ### # # # # # ## # # # # # ## # ## # # # + # # ## # ### # ## # # ## # # # # # # # # + # # # ## #### # # ### # ## # # ## # # ## + # # # ## # ### # ## ## # # # # ### # # # # + # # # # # # ## # ## ## ### ### # # ## # # # ## # # + # ## # # ### ##### # # # # ## # # # # # ## # # # + # # # ## # # ## # ## ## # ## # ### # # # # ## ## + # ### # ## ### # # ## # # # # # # # # # # # ## ## # # + # # # # # # # # ## # # # # # # # # # # # ## # # # ## # # + # # # ## # # ## # # ## # # # ### ### # # # # # # # + ## # ## # # # # # # # ## # # ## # ### ### # # ## ## + # ## # # #### # # # # ##### # ## #### # # # # # # #### + ## # ### ### # ## # ## # # ## # # # # # # ## #### # ## # # + # # # ## # # # # # # # ## # # # # # # + ## # ## ## # ### #### # # # # ## # ## # ## # + # # # ## ## # ## # ## # # # # # # ## + # # # # # # ### ## ### # ## # # #### # # # ##### # + ## ## # # # ## # ## ## # # # # # # # # # # ## + ##### # ### # ## # # # ## # ### #### # # ### # ## # + ### ## ## # ## # ### # ## ### # ## ## ## ## # # # # + # # ### # ## # # ## # # # # ## ## # ## # ## # + # ## # ## # ## # ## # # # # # # # # # + # ## # # # ####### # ## ## ## # # # # # # # # # ## # + # # # # ## # # ### # # # ## #### # # # # # # + ### # ### # ### # ### ## # # # # # ## # # # # # # # # + # # ##### # ## ##### #### ## # # # ## # ## # # ## # + # ### ## ## # ##### # ## # # # # # # + # # # ## ## # ## ## ## # ## # ## #### # # ## # # # # # ## + # # ## # # # #### # # ## # ## ## # # ## # ## ## # # ## # # + # # # #### # ## # # # ## ### ## #### # # # # # + ## ### # # # ## # # # # # # # ## # ## ### + # ## # ## # # # # # # # # # # # ### # # # ## # + # ## ## # #### # ## # # # # # # # + # # ## ### # # # ## ## # ## # # # ## # # # # # #### + # # ## ### # # ## ## # # # # ### # # ## # # # ## + ## # # # # ## ## # ## # # #### # # # # + # ## # # # # # # ### ## # #### # # ## # # # # ### ## # ## + ### # ## ## # # # # # # ## # # # ## # #### # ##### # + # # # # # # # ## ## ### # ### ### # # #### # # # # ## # ## + # # # #### # # # # ## # # ## # # ## # # ## # ## + # # # ## ## # ## # # # ## ## # ### ## # ## # # # # # # # + ## # # # ## # ## ## ## # # ## # # # # # ## # # # # + ### # # # ## # # # # # # # # # # # # ## # # # ## + # # # ## # # # # ## # # ## # # # # # # ## # # # # + # # # ## # ## # ### # # ### # ## # # # ## # ### # + ## # # # ## # # ## # # # ## # # #### ## # # # ### # + # #### ## ### ### # # ### # # ## # # # ### # ####### # ## + # # ## ## ### ## ### # # # # # # # # # # # + # ### # ## # ### # ## ## ## # # # # # # # ## ## # ### + # ## ### ## # # # # # # # # # # # # ### + # # # # # ## ### # # ## ## ## ### # # # # # # ## # # E \ No newline at end of file diff --git a/SimonovaMS/lab2/test_no_exit_20x20.txt b/SimonovaMS/lab2/test_no_exit_20x20.txt new file mode 100644 index 0000000..f39bf98 --- /dev/null +++ b/SimonovaMS/lab2/test_no_exit_20x20.txt @@ -0,0 +1,20 @@ +S################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +#################### +###################E \ No newline at end of file diff --git a/SimonovaMS/lab2/test_simple_10x10.txt b/SimonovaMS/lab2/test_simple_10x10.txt new file mode 100644 index 0000000..9bd9e1b --- /dev/null +++ b/SimonovaMS/lab2/test_simple_10x10.txt @@ -0,0 +1,10 @@ +S E + + + + + + + + + \ No newline at end of file diff --git a/SimonovaMS/lab2/visualization.py b/SimonovaMS/lab2/visualization.py new file mode 100644 index 0000000..39a542a --- /dev/null +++ b/SimonovaMS/lab2/visualization.py @@ -0,0 +1,160 @@ +from abc import ABC, abstractmethod +from typing import List, Optional, Set +from enum import Enum +from maze_model import Maze, Cell + + +class EventType(Enum): + PATH_FOUND = "path_found" + MOVE = "move" + MAZE_LOADED = "maze_loaded" + SOLVE_START = "solve_start" + SOLVE_END = "solve_end" + + +class Observer(ABC): + + @abstractmethod + def update(self, event_type: EventType, data: any) -> None: + pass + + +class ConsoleView(Observer): + + def __init__(self): + self.last_path: Optional[List[Cell]] = None + + def update(self, event_type: EventType, data: any) -> None: + if event_type == EventType.MAZE_LOADED: + print("Лабиринт загружен") + elif event_type == EventType.SOLVE_START: + print("Начинается поиск пути...") + elif event_type == EventType.SOLVE_END: + print(f"Поиск завершён. Статистика: {data}") + elif event_type == EventType.PATH_FOUND: + self.last_path = data + + def render(self, maze: Maze, player_pos: Optional[Cell] = None, + path: Optional[List[Cell]] = None) -> None: #рисует лаб + import os + os.system('cls' if os.name == 'nt' else 'clear') + + path_set = set(path) if path else set() + + # Верх + print("┌" + "─" * maze.width + "┐") + + for y in range(maze.height): + line = "│" + for x in range(maze.width): + cell = maze.get_cell(x, y) + if player_pos and player_pos.x == x and player_pos.y == y: + line += "P" + elif cell == maze.start: + line += "S" + elif cell == maze.exit: + line += "E" + elif cell is not None and cell.is_wall: + line += "#" + elif path and cell in path_set: + line += "." + else: + line += " " + line += "│" + print(line) + + # Низ + print("└" + "─" * maze.width + "┘") + + if path: + print(f"\nПуть найден! Длина: {len(path)} шагов") + elif path == []: + print("\nПуть не найден:(") + + +class Player: + + def __init__(self, start_cell: Cell): + self.current_cell = start_cell + + def move_to(self, cell: Cell) -> None: + self.current_cell = cell + + def get_position(self) -> Cell: + return self.current_cell + + +class Direction(Enum): + UP = (0, -1) + DOWN = (0, 1) + LEFT = (-1, 0) + RIGHT = (1, 0) + + +class Command(ABC): + + @abstractmethod + def execute(self) -> None: + pass + + @abstractmethod + def undo(self) -> None: + pass + + +class MoveCommand(Command): + + def __init__(self, player: Player, maze: Maze, direction: Direction): + self.player = player + self.maze = maze + self.direction = direction + self.previous_cell = player.current_cell + + def execute(self) -> None: + dx, dy = self.direction.value + 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.previous_cell = self.player.current_cell + self.player.move_to(new_cell) + return True + return False + + def undo(self) -> None: + self.player.move_to(self.previous_cell) + + +class GameController: + + def __init__(self, maze: Maze, view: ConsoleView): + if maze.start is None: + raise ValueError("Лабиринт не имеет стартовой клетки") + + self.maze = maze + self.view = view + self.player = Player(maze.start) + self.command_history: List[Command] = [] + self.found_path: Optional[List[Cell]] = None + + def move(self, direction: Direction) -> bool: + command = MoveCommand(self.player, self.maze, direction) + if command.execute(): + self.command_history.append(command) + self._render() + return True + return False + + def undo(self) -> None: + if self.command_history: + command = self.command_history.pop() + command.undo() + self._render() + + def set_path(self, path: List[Cell]) -> None: + self.found_path = path + self._render() + + def _render(self) -> None: + self.view.render(self.maze, self.player.get_position(), self.found_path) \ No newline at end of file diff --git a/SimonovaMS/phonebook.py b/SimonovaMS/phonebook.py new file mode 100644 index 0000000..ac33ef1 --- /dev/null +++ b/SimonovaMS/phonebook.py @@ -0,0 +1,247 @@ +import time +import csv +import random +from functools import lru_cache +from operator import index + + +#LinkedListPhoneBook +def create_node(name, phone): + return {'name': name, 'phone': phone, 'next': None} + +def ll_insert(head, name, phone): + new_node = create_node(name,phone) + + if head is None: + return new_node + current = head + while current['next'] is not None: + if current['next']['name'] == name: + new_node['next'] = current['next']['next'] + current['next']=new_node + return head + current=current['next'] + + current['next'] = new_node + return head + +def ll_find(head, name): + current = head + while current is not None: + if current['name'] ==name: + return current['phone'] + current=current['next'] + return None +def ll_delete(head, name): + if head is None: + return None + if head['name'] == name: + return head['next'] + + current =head + while current['next'] is not None: + if current['next']['name'] == name: + current['next'] = current['next']['next'] + return head + current=current['next'] + + return head + +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 create_buckets(size=1000): + return [None] * size + +def hash_function(name, buckest_size): + hash_value = 0 + for char in name: + hash_value = (hash_value * 31 + ord(char)) % buckest_size + return hash_value + +def ht_insert(buckets, name, phone): + index = hash_function(name, len(buckets)) + buckets[index] = ll_insert(buckets[index], name, phone) + +def ht_find(buckets, name): + index = hash_function(name, len(buckets)) + return ll_find(buckets[index], name) + +def ht_delete(buckets, name): + index = hash_function(name, len(buckets)) + buckets[index] = ll_delete(buckets[index], name) + +def ht_list_all(buckets): + records = [] + for bucket in buckets: + current = bucket + while current is not None: + records.append((current['name'], current['phone'])) + current = current['next'] + records.sort(key=lambda x:x[0]) + return records + +#bts +def create_bst_node(name, phone): + return {'name': name, 'phone': phone, 'left': None, 'right': None} + +def bst_insert(root, name, phone): + new_node = create_bst_node(name, phone) + if root is None: + return new_node + current = root + while True: + if name == current['name']: + current['phone'] = phone + return root + elif name < current['name']: + if current['left'] is None: + current['left'] = new_node + return root + current = current['left'] + else: + if current['right'] is None: + current['right'] = new_node + return root + current = current['right'] + + +def bst_find(root, name): + current = root + while current is not None: + if name == current['name']: + return current['phone'] + elif name < current['name']: + current=current['left'] + else: + current=current['right'] + return None + +def bst_find_min(node): + current = node + while current['left'] is not None: + current = current['left'] + return current + + +def bst_delete(root, name): + if root is None: + return None + + if root['name'] == name: + if root['left'] is None and root['right'] is None: + return None + if root['left'] is None: + return root['right'] + if root['right'] is None: + return root['left'] + parent = root + min_node = root['right'] + while min_node['left']: + parent = min_node + min_node = min_node['left'] + + root['name'] = min_node['name'] + root['phone'] = min_node['phone'] + + + if parent == root: + parent['right'] = min_node['right'] + else: + parent['left'] = min_node['right'] + + return root + + parent = None + current = root + while current and current['name'] != name: + parent = current + if name < current['name']: + current = current['left'] + else: + current = current['right'] + + if current is None: + return root + + + if current['left'] is None and current['right'] is None: + if parent['left'] == current: + parent['left'] = None + else: + parent['right'] = None + + + elif current['left'] is None: + if parent['left'] == current: + parent['left'] = current['right'] + else: + parent['right'] = current['right'] + + + elif current['right'] is None: + if parent['left'] == current: + parent['left'] = current['left'] + else: + parent['right'] = current['left'] + + else: + min_parent = current + min_node = current['right'] + while min_node['left']: + min_parent = min_node + min_node = min_node['left'] + + + current['name'] = min_node['name'] + current['phone'] = min_node['phone'] + + + if min_parent == current: + min_parent['right'] = min_node['right'] + else: + min_parent['left'] = min_node['right'] + + return root + + +def bst_list_all(root): + records = [] + stack = [] + current = root + + while stack or current: + while current: + stack.append(current) + current = current['left'] + + current = stack.pop() + records.append((current['name'], current['phone'])) + current = current['right'] + + return records + +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 + diff --git a/SimonovaMS/result_plot.png b/SimonovaMS/result_plot.png new file mode 100644 index 0000000..1b903d7 Binary files /dev/null and b/SimonovaMS/result_plot.png differ diff --git a/SimonovaMS/test.py b/SimonovaMS/test.py new file mode 100644 index 0000000..d9cade5 --- /dev/null +++ b/SimonovaMS/test.py @@ -0,0 +1,211 @@ +import time +import csv +import random + +from phonebook import (ll_insert, ll_find, ll_delete, create_buckets, ht_insert, ht_find, ht_delete, bst_insert, bst_find, bst_delete) +from generator import generate_data + +def run_exp(): + records_shuffled, records_sorted = generate_data(10000) + all_names = [name for name, _ in records_shuffled] + search_names = random.sample(all_names, 100) + [f"None_{i}" for i in range(10)] + delete_names = random.sample(all_names, 50) + + results = [["Structura", "shuffled/sorted", "Operation", "Time"]] + + times =[] + print('LinkedList - shuffled') + for r in range(5): + head = None + start = time.perf_counter() + for name, phone in records_shuffled: + head = ll_insert(head, name, phone) + times.append(time.perf_counter() - start) + avg = sum(times)/5 + results.append(["LinkedList", "shuffled", "insert", avg]) + print(f"вставка - {avg:.6f}") + + times=[] + for r in range(5): + start = time.perf_counter() + for name in search_names: + ll_find(head, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["LinkedList", "shuffled", "find", avg]) + print(f"поиск - {avg:.6f}") + + times=[] + for r in range(5): + start = time.perf_counter() + for name in delete_names: + head = ll_delete(head, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["LinkedList", "shuffled", "delete", avg]) + print(f"удаление - {avg:.6f}") + + print('LinkedList - sorted') + for r in range(5): + head = None + start = time.perf_counter() + for name, phone in records_sorted: + head = ll_insert(head, name, phone) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["LinkedList", "sorted", "insert", avg]) + print(f"вставка - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in search_names: + ll_find(head, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["LinkedList", "sorted", "find", avg]) + print(f"поиск - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in delete_names: + head = ll_delete(head, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["LinkedList", "sorted", "delete", avg]) + print(f"удаление - {avg:.6f}") + + print('HashTable - shuffled') + times =[] + for r in range(5): + buckets = create_buckets(1000) + start = time.perf_counter() + for name, phone in records_shuffled: + ht_insert(buckets,name,phone) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["HashTable", "shuffled", "insert", avg]) + print(f"вставка - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in search_names: + ht_find(buckets, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["HashTable", "shuffled", "find", avg]) + print(f"поиск - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in delete_names: + ht_delete(buckets, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["HashTable", "shuffled", "delete", avg]) + print(f"удаление - {avg:.6f}") + + print('sorted') + times = [] + for r in range(5): + buckets = create_buckets(1000) + start = time.perf_counter() + for name, phone in records_sorted: + ht_insert(buckets, name, phone) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["HashTable", "sorted", "insert", avg]) + print(f"вставка - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in search_names: + ht_find(buckets, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["HashTable", "sorted", "find", avg]) + print(f"поиск - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in delete_names: + ht_delete(buckets, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["HashTable", "sorted", "delete", avg]) + print(f"удаление - {avg:.6f}") + + print("BST - shuffled") + times = [] + for r in range(5): + root = None + start = time.perf_counter() + for name, phone in records_shuffled: + root = bst_insert(root, name, phone) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["Bst", "shuffled", "insert", avg]) + print(f"вставка - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in search_names: + bst_find(root, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["Bst", "shuffled", "find", avg]) + print(f"поиск - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in delete_names: + root = bst_delete(root, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["Bst", "shuffled", "delete", avg]) + print(f"удаление - {avg:.6f}") + + print('sorted') + times = [] + for r in range(5): + root = None + start = time.perf_counter() + for name, phone in records_sorted: + root = bst_insert(root, name, phone) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["Bst", "sorted", "insert", avg]) + print(f"вставка - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in search_names: + bst_find(root, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["Bst", "sorted", "find", avg]) + print(f"поиск - {avg:.6f}") + + times = [] + for r in range(5): + start = time.perf_counter() + for name in delete_names: + root = bst_delete(root, name) + times.append(time.perf_counter() - start) + avg = sum(times) / 5 + results.append(["Bst", "sorted", "delete", avg]) + print(f"удаление - {avg:.6f}") + + with open("C:/Users/Honor/Documents/dep2k/lab_inf_1/data/results.csv", "w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerows(results) +if __name__ == "__main__": + run_exp() \ No newline at end of file