Учебный репозиторий курсов "Методы программирования" и "Программная инженерия" радиофизического факультета ННГУ rybakovaa
Go to file
2026-04-04 09:12:00 +00:00
agafonovdm [0] initial commit 2026-02-27 18:55:31 +03:00
anikinvd [0] initial commit 2026-02-21 13:38:46 +03:00
BolonkinNM [0] initial commit 2026-02-26 21:00:34 +03:00
BoriskovaDV [0] init 2026-03-15 05:18:35 +00:00
BorisovMI [0] initial commit 2026-02-26 13:38:56 +03:00
BudakovIS [15] FINISH for 1-st exersize 2026-03-04 01:40:38 +03:00
chizhikovaSM [0] initial commit 2026-02-20 16:55:58 +03:00
DerbenevRY [0] initial commit 2026-02-21 12:51:56 +03:00
duznb [0] initial commit 2026-02-21 13:36:01 +03:00
Ezhovnd [0] initial com mit 2026-02-28 13:44:09 +03:00
famutdinovmd [0] initial commit 2026-02-28 14:21:18 +03:00
filippovavm [0] initial commit 2026-02-14 13:57:35 +03:00
fomichevks [0] 2026-02-21 14:13:16 +03:00
GorkinMM [0] initial commit 2026-02-27 23:43:40 +03:00
groshevava [0] initial commit 2026-02-26 23:04:47 +00:00
GutovVM [0] initial commit 2026-02-28 08:25:52 +03:00
ivanchenkoam [0] initial commit 2026-02-14 14:06:38 +03:00
ivantsovma [0] initial commit 2026-02-14 13:38:29 +03:00
KislyuninED [0] initial commit 2026-03-07 17:15:56 +03:00
KolbasovPD [0] initial commit 2026-02-28 01:11:43 +03:00
kolesovve [0] initial commit 2026-02-14 13:41:48 +03:00
komissarovgo [0] initial commit 2026-02-14 13:45:52 +03:00
konnovaea [0] initial commit 2026-02-19 16:52:52 +03:00
kornevma [0] initial commit 2026-02-21 13:32:10 +03:00
KorotkinSE [0] initial commit 2026-02-27 22:13:03 +03:00
krasnovia [0] initial commit 2026-02-20 18:23:20 +03:00
KuzminskiyAA [0] initial commit 2026-02-14 13:46:30 +03:00
KuznetsovAS [0] initial commit 2026-02-14 14:11:12 +03:00
KuznetsovMA [0] initial commit 2026-02-25 19:02:50 +03:00
kuznetsovTD [0] initial commit 2026-02-26 22:53:18 +03:00
KuznetsovYuM [0] initial commit 2026-02-26 13:34:48 +03:00
LarikovaAA [0] initial commit 2026-02-27 20:36:00 +03:00
lomakinae [0] initial commit 2026-02-14 10:14:05 +03:00
LukovnikovDE [0] initial commit 2026-02-27 23:32:09 +03:00
MarkinAM [0] initial commit 2026-02-24 17:57:48 +03:00
MashinDD [0] initial commit 2026-02-20 23:36:56 +03:00
meosyam [0] initial commit 2026-02-21 13:36:14 +03:00
MininaVD [0] initial commit 2026-02-14 14:08:50 +03:00
MochalovAE [0] initial commit 2026-02-28 16:42:45 +03:00
morozovns [1] patched 2026-02-14 11:35:37 +03:00
MusinAA [0] initial commit 2026-02-20 19:58:28 +03:00
MylnikovAS [0] initial commit 2026-02-14 13:03:21 +03:00
nehoroshevaa [0] initial commit 2026-02-21 13:48:49 +03:00
nikitovie [0] initial commit 2026-02-28 13:09:29 +03:00
nikolaevda [0] initial commiy 2026-02-14 18:22:17 +03:00
novikovsd [0] initial commit 2026-02-21 13:39:09 +03:00
osininyai [0] initial commit 2026-02-14 13:31:07 +03:00
petryaninyas [0] initial commit 2026-02-24 23:07:54 +03:00
pogodinda Добавлен файл группы 427 для Погодин 2026-02-14 00:56:21 +03:00
pomelovsd [0] initial commit 2026-02-14 11:55:35 +03:00
ProninVV [0] initila commit 2026-02-14 13:18:12 +03:00
raskatovia [1] second submission 2026-02-23 13:49:00 +03:00
romanovpv [0] initial commit 2026-02-14 12:59:32 +03:00
rybakovaa [0] initial commit 2026-02-26 23:02:00 +03:00
SavelevMI Добавил папку SavelevMI 2026-02-27 20:38:31 +03:00
semyanovra [0] initial commit 2026-02-21 13:58:46 +03:00
shahovaa Remove .DS_Store and add to gitignore 2026-02-21 12:45:34 +03:00
shalovsa [0] initial commit 2026-02-20 18:22:42 +03:00
shapovalovka [0] initial commit 2026-02-27 19:14:53 +03:00
shekurovaa [0] initial commit 2026-02-21 14:04:28 +03:00
ShulpinIN [0] fix: correct file extension to .md 2026-02-26 19:50:20 +03:00
SimonovaMS [0] initial commit 2026-02-14 11:54:34 +03:00
skorohodovsa [0] Добавление main.py 2026-02-14 12:00:32 +03:00
smirnovad [0] initial commit 2026-02-21 13:34:52 +03:00
sobininaas [0] initial commit 2026-02-21 14:23:21 +03:00
SobolevNS [0] initial commit 2026-02-27 09:09:05 +03:00
SokolovEN [0] initial commit 2026-02-27 12:00:34 +03:00
SokolovNE [0] initial commit 2026-02-28 13:34:18 +03:00
soldatkinao [0] initial commit 2026-02-19 12:46:18 +03:00
SolovevDD [0] initial commit 2026-02-27 23:37:07 +03:00
SolovevDS [0] initial commit 2026-02-20 22:32:31 +03:00
soninrv [0] initial commit 2026-02-25 17:16:19 +03:00
SorokinAD [0] initial commit 2026-02-28 13:53:01 +03:00
sorokinfi [0] initial commit 2026-02-14 13:03:54 +03:00
starikovta [0] initial commit 2026-02-28 17:53:25 +03:00
stepinim [0] initial commit 2026-02-20 22:50:08 +03:00
stepushovgs [0] initial commit 2026-02-14 11:41:46 +03:00
svetlakovkyu [0] initial commit 2026-02-14 11:29:22 +03:00
talantsevgi [0] initial commit 2026-02-14 13:38:09 +03:00
tseremonnikovaaa [0] 2026-02-14 14:44:01 +03:00
VaravinVV [0] initial commit 2026-02-14 15:52:46 +03:00
VarnakovAA [0] initial commit 2026-03-23 21:32:01 +03:00
victorovaas [0] initial commit 2026-02-28 17:19:44 +03:00
VildyaevAV [0] initial commit 2026-02-27 18:00:19 +03:00
volkovim [0] initial commit 2026-02-21 13:21:07 +03:00
VolkovVA [0] initial commit 2026-02-21 11:27:54 +03:00
YanyaevAA [0] initial commit 2026-02-20 19:59:41 +03:00
YaroslavtsevAS [0] initial commit 2026-02-20 21:28:01 +03:00
zaharoves [0] initial commit' 2026-02-20 18:25:52 +03:00
ZelentsovAV [0] initial commit 2026-02-20 19:58:35 +03:00
zhigalovrd [0] initial commit 2026-02-25 14:17:27 +03:00
ZhuravlevDV [0] initial commit 2026-02-27 19:17:17 +03:00
zverevem [0] initial commit 2026-02-20 18:37:49 +03:00
.gitignore Обновить .gitignore 2026-02-21 11:45:45 +00:00
README.md Обновить README.md 2026-04-04 07:11:52 +00:00

2026-MP

Практика по курсам "Методы программирования" и "Программная инженерия" РФФ ННГУ

Презентация по курсу (обновляемая)

Для работы необходим python 3.11 и выше. Библиотеки: numpy, pandas, matplotlib, tensorflow, Pillow. Редактор любой. Из неплохих: IDLE (родной, идёт вместе с установщиком), Visual Studio Code, notepad++, PyCharm, vim (для любителей сначала страдать, потом наслаждаться).

Работа с блокнотами онлайн, с возможностью подключения удалённых мощностей гугла (GPU, TPU): https://colab.research.google.com/

Мой контакт: nsmorozov@rf.unn.ru

Внутри папки группы создать папку имени себя (фамилия и имя). В своей папке можете делать все что угодно, в чужие не залезать, в корневую тоже. Я буду ориентироваться на файлы, где в названии будет номер лабораторной.

Название пулл-реквеста должно начинаться с квадратных скобок, в которых перечислены номера сдаваемых лабораторных работ. Не больше одного активного реквеста, если надо довнести -- надо обновить текущий.

Крайний срок приема работ 25.05.2026 до 14:00

Задание 0 -- репозиторий [отдельный срок на создание PR с папкой: 28.02.2026]

  1. Создай пользователя (логин — фамилия+инициалы слитно транслитом, как в терминал-классе).

  2. Зайди в этот репозиторий на Gitea, нажми кнопку Форкнуть, чтобы создать копию в своем аккаунте.

  3. Клонирование: Скопируй ссылку на свой форк и выполни:

    git clone <ссылкаааш_форк>
    cdазвание_репозитория>
    
  4. Создай ветку (название — фамилия+инициалы слитно транслитом, буква в букву как логин):

    git checkout -b IvanovII
    
  5. Создай папку с таким же названием (IvanovII) и внутри неё — текстовый файл, названный номером вашей группы (например, 101.md).

  6. Сохрани изменения:

    git add -A
    git commit -m "[0] initial commit"
    
  7. Отправь ветку в свой форк на Gitea:

    git push origin
    

если просит, перед этим сделать git push --set-upstream origin

  1. Создай запрос на слияние (Pull Request): На Gitea перейди в свой форк, выбери ветку IvanovII, нажмите Запрос на слияние. Убедитесь, что:

    • Базовый репозиторий: учебный (преподавателя)
    • Базовая ветка: develop
    • Сравниваемая ветка: свой форк / IvanovII
  2. Отправь PR.

Задание 1 -- структуры данных

Напоминание: под каждое задание вы создаете отдельную ветку

Для оформления результатов заведи папку docs в своей папке и сохраняй туда отчет (в любом формате от .doc до .md, а то и .jpnb). Вспомогательные файлы клади в подпапку data внутри docs

Цель работы

Реализовать три различные структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций. Вы должны собственными руками написать код, чтобы понять внутреннее устройство связного списка, хеш-таблицы и двоичного дерева поиска, а также осознать их сильные и слабые стороны на практике.

!! Задание выполнять в структурной (процедурной) парадигме, не используя классы. Главное реализовать структуры данных «руками» и сравнить их производительность.

Базовые операции (обязательны для всех):

insert(name, phone) -- добавить или обновить запись.

find(name) -- phone или None.

delete(name) -- удалить запись, игнорировать отсутствие.

list_all() -- список всех записей, отсортированный по имени (для BST inorder обход; для списка и хеш‑таблицы — собрать и отсортировать явно).

1. Связный список (LinkedListPhoneBook)

Узел представляется словарём: {'name': 'Имя', 'phone': '123', 'next': None}.

Функции:

def ll_insert(head, name, phone) — проходит до конца (или сразу добавляет в конец) и возвращает новую голову (если вставка в начало) или изменяет список по ссылке. Удобнее возвращать новую голову, если вставка может быть в начало.

def ll_find(head, name) — ищет узел, возвращает телефон или None.

def ll_delete(head, name) — удаляет узел, возвращает новую голову.

def ll_list_all(head) — собирает все записи в список и сортирует (сортировка вынесена отдельно).

2. Хеш-таблица

Хранится как список buckets фиксированной длины, каждый элемент — голова связного списка (или None).

Функции:

def ht_insert(buckets, name, phone) — вычисляет индекс, вызывает ll_insert для соответствующего бакета.

Аналогично ht_find, ht_delete, ht_list_all (последняя собирает все записи из всех бакетов и сортирует).

3. Двоичное дерево поиска

Узел — словарь: {'name': 'Имя', 'phone': '123', 'left': None, 'right': None}.

Функции:

def bst_insert(root, name, phone) — рекурсивно или итеративно вставляет, возвращает новый корень (если корень меняется).

def bst_find(root, name) — поиск.

def bst_delete(root, name) — удаление, возвращает новый корень.

def bst_list_all(root) — центрированный обход (рекурсивно собирает записи в отсортированном порядке).

Экспериментальная часть (подробно об измерении времени)

1. Генерация тестовых данных

Создайте список records из N элементов (например, N = 10000). Каждый элемент — кортеж (name, phone).

Имена генерируйте как f"User_{i:05d}" (равномерное распределение) или случайные слова из небольшого набора (чтобы были повторения и коллизии). Для проверки влияния порядка подготовьте два варианта одного и того же набора:

records_shuffled — случайный порядок.

records_sorted — отсортированный по имени (по алфавиту).

2. Инструменты замера времени

Используйте модуль time:

import time

start = time.perf_counter()
# ... операции ...
end = time.perf_counter()
elapsed = end - start  # время в секундах

Для многократных замеров удобен timeit, но в этой задаче достаточно просто обернуть код в цикл и усреднить.

3. Проведение замеров

Для каждой структуры данных и для каждого режима входных данных (случайный / отсортированный) выполните:

  • А. Вставка всех записей

Создайте пустую структуру.

Засеките время, выполните insert для каждой записи из входного списка.

Зафиксируйте общее время вставки.

  • Б. Поиск 100 случайных записей

Возьмите 100 случайных имён из того же набора (гарантированно существующих) и 10 имён, которых нет (например, "None_{i}").

Засеките время на выполнение всех 110 вызовов find.

  • В. Удаление 50 случайных записей

Выберите 50 случайных имён из набора.

Засеките время на выполнение delete для каждого.

!! Важно: после вставки структура остаётся заполненной, поиск и удаление выполняются на ней же. Если нужно повторить замер для другого порядка данных — создавайте новую структуру и заполняйте заново.

4. Сохранение результатов

!! Каждый эксперимент повторить минимум 5 раз и записывать и среднее время, и все замеры.

Соберите все замеры в словарь или список, затем сохраните в CSV-файл:

import csv

results = [
    ["Структура", "Режим", "Операция", "Время (сек)"],
    ["LinkedList", "случайный", "вставка", 0.123],
    ...
]

with open("results.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(results)

5. Анализ результатов

Постройте график (столбчатая диаграмма или линейный график) — можно в Excel, Google Sheets или с помощью matplotlib в Python.

Сравните:

  • Как порядок входных данных влияет на скорость вставки в BST (деградация до O(n) на отсортированных данных).

  • Почему хеш-таблица почти не чувствительна к порядку.

  • Почему связный список всегда медленен при поиске.

  • Как удаление работает в каждой структуре.

  • Вывод должен содержать ответ на вопрос: какую структуру и для каких задач (частые вставки, частый поиск, необходимость получать данные в порядке) стоит выбирать в реальной жизни.*

Задание: Поиск выхода из лабиринта (объектно-ориентированная реализация с паттернами)

Цель работы

Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В ходе работы необходимо применить минимум 3 паттерна проектирования из списка GoF, обосновать их выбор и продемонстрировать преимущества такой архитектуры.

Общая схема приложения (пример)

classDiagram
    class Maze {
        -Cell[] cells
        -int width, height
        -Cell start
        -Cell exit
        +getCell(x,y): Cell
        +getNeighbors(cell): List~Cell~
    }
    
    class Cell {
        -int x, y
        -bool isWall
        -bool isStart
        -bool isExit
        +isPassable(): bool
    }
    
    class MazeBuilder {
        <<interface>>
        +buildFromFile(filename): Maze
    }
    
    class TextFileMazeBuilder {
        +buildFromFile(filename): Maze
    }
    
    class PathFindingStrategy {
        <<interface>>
        +findPath(maze, start, exit): List~Cell~
    }
    
    class BFSStrategy
    class DFSStrategy
    class AStarStrategy
    class DijkstraStrategy
    
    class SearchStats {
        +timeMs: float
        +visitedCells: int
        +pathLength: int
    }
    
    class MazeSolver {
        -Maze maze
        -PathFindingStrategy strategy
        +setStrategy(strategy)
        +solve(): SearchStats
    }
    
    class Command {
        <<interface>>
        +execute()
        +undo()
    }
    
    class MoveCommand {
        -Player player
        -Direction dir
        -Cell previousCell
        +execute()
        +undo()
    }
    
    class Player {
        -Cell currentCell
        +moveTo(cell)
    }
    
    class Observer {
        <<interface>>
        +update(event)
    }
    
    class ConsoleView {
        +update(event)
        +render(maze, player, path)
    }
    
    MazeBuilder <|.. TextFileMazeBuilder
    MazeBuilder --> Maze : creates
    PathFindingStrategy <|.. BFSStrategy
    PathFindingStrategy <|.. DFSStrategy
    PathFindingStrategy <|.. AStarStrategy
    PathFindingStrategy <|.. DijkstraStrategy
    MazeSolver --> PathFindingStrategy : uses
    MazeSolver --> Maze : uses
    Command <|.. MoveCommand
    MoveCommand --> Player
    Player --> Cell
    Observer <|.. ConsoleView
    MazeSolver --> Observer : notifies

Выполнение

Этап 1. Модель лабиринта (без паттернов, просто классы)

Задача: Создать классы Cell и Maze, которые представляют карту лабиринта.

  • Cell хранит координаты (x, y), флаги isWall, isStart, isExit, метод isPassable() (возвращает True для прохода, если не стена).
  • Maze хранит двумерный массив клеток, ширину, высоту, ссылки на стартовую и выходную клетку. Методы: getCell(x, y), getNeighbors(cell) возвращает список соседних проходимых клеток (вверх, вниз, влево, вправо, если в пределах границ и не стена).

Результат: Лабиринт можно создать вручную в коде, но загрузку пока не делаем.

Этап 2. Загрузка лабиринта из файла применение паттерна Builder

Задача: Реализовать загрузку лабиринта из текстового файла, где # стена, (пробел) проход, S старт, E выход.

  • Создать интерфейс MazeBuilder с методом buildFromFile(filename).
  • Реализовать класс TextFileMazeBuilder, который читает файл, парсит символы, создаёт объекты Cell, задаёт координаты и флаги, после чего возвращает готовый Maze.

Процесс построения лабиринта сложный (парсинг, валидация, установка старта/выхода). Builder скрывает детали создания от клиента. В будущем можно легко добавить другой формат (например, JSON или бинарный) через новую реализацию MazeBuilder.

Этап 3. Стратегии поиска пути паттерн Strategy

Задача: Реализовать семейство алгоритмов поиска пути от старта до выхода.

  • Создать интерфейс PathFindingStrategy с методом findPath(maze, start, exit), возвращающим список клеток пути (от старта до выхода включительно) или пустой список, если пути нет.
  • Реализовать минимум 3 стратегии:
    • BFS (поиск в ширину) гарантирует кратчайший путь по количеству шагов.
    • DFS (поиск в глубину) быстрый, но не обязательно кратчайший.
    • A* (с эвристикой, например, манхэттенское расстояние) компромисс между скоростью и оптимальностью.
    • (Опционально) Дейкстра полезна для взвешенных лабиринтов, но в базовом варианте все шаги имеют вес 1, тогда она совпадает с BFS.

Каждая стратегия возвращает путь. Для BFS/DFS используйте очередь/стек, для A* приоритетную очередь (heapq). Важно: алгоритмы не должны модифицировать сам лабиринт, только читать состояние клеток.

Strategy позволяет легко переключать алгоритмы во время выполнения, не меняя код остальной программы. Новый алгоритм можно добавить, реализовав интерфейс.

Этап 4. Класс-оркестратор MazeSolver (использует Strategy)

Задача: Создать класс, который принимает лабиринт и стратегию, выполняет поиск и собирает статистику.

  • MazeSolver содержит поля maze и strategy.
  • Метод setStrategy(strategy) для динамической смены алгоритма.
  • Метод solve() вызывает strategy.findPath(...) и возвращает объект SearchStats (время выполнения в миллисекундах, количество посещённых клеток, длина найденного пути).
  • Для замера времени используйте time.perf_counter() до и после вызова стратегии.

Этап 5. Визуализация и пошаговое управление паттерны Observer и Command (по желанию)

5.1. Наблюдатель (Observer) обновление консольного интерфейса.

  • Создать интерфейс Observer с методом update(event), где event может быть строкой или объектом с типом события ("path_found", "move", "maze_loaded").
  • Реализовать класс ConsoleView, который отображает лабиринт, текущее положение игрока (если реализован пошаговый режим) и найденный путь. Метод render(maze, player_position, path) рисует карту в консоли.
  • MazeSolver (или отдельный контроллер) может иметь список наблюдателей и уведомлять их при изменении состояния.

5.2. Команда (Command) для пошагового перемещения игрока по найденному пути (или ручного управления).

  • Создать интерфейс Command с методами execute() и undo().
  • Реализовать MoveCommand, который принимает игрока (Player), направление и изменяет его позицию, сохраняя предыдущую для отмены.
  • Создать класс Player, хранящий текущую клетку.
  • Консольное меню позволяет вводить команды (W/A/S/D), выполнять MoveCommand, при необходимости отменять последний ход (Ctrl+Z). Это опционально, но очень наглядно демонстрирует паттерн.

Observer можно реализовать только для вывода сообщений о начале/конце поиска, а Command для демонстрации undo при ручном исследовании лабиринта.

Этап 6. Экспериментальная часть (аналогично заданию со структурами данных)

Задача: Сравнить эффективность реализованных стратегий на лабиринтах разной сложности.

  1. Подготовка тестовых лабиринтов:

    • Маленький (10×10) с простым путём.
    • Средний (50×50) с тупиками.
    • Большой (100×100) с запутанной структурой.
    • «Пустой» лабиринт (без стен) для демонстрации максимальной производительности.
    • «Без выхода» чтобы проверить обработку отсутствия пути.
  2. Замеры:

    • Для каждого лабиринта и каждой стратегии запустить solve() 510 раз, усреднить время, количество посещённых клеток, длину пути.
    • Записать результаты в CSV: лабиринт,стратегия,время_мс,посещено_клеток,длина_пути.
  3. Анализ:

    • Построить графики для каждого лабиринта.
    • Проанализировать и написать выводы по итогам (эффективность того или иного алгоритма в разных случаях).
  4. Дополнительное задание: Реализовать взвешенные клетки (например, болото вес 3, песок вес 2, асфальт вес 1) и сравнить Дейкстру с A* на взвешенном графе.

Этап 7. Отчёт

Структура отчёта:

  1. Описание задачи и выбранных паттернов (с диаграммой классов из Mermaid).
  2. Листинги ключевых классов (можно выборочно) или ссылка на репозиторий.
  3. Результаты экспериментов (таблицы, графики).
  4. Анализ эффективности алгоритмов и применимости паттернов.
  5. Выводы: как ООП и паттерны помогли сделать код гибким и расширяемым. Что было бы сложно изменить без них.

Советы

  • Для A* самая простая эвристика: abs(x1 - x2) + abs(y1 - y2).
  • При поиске пути надо хранить предшественников (parent для каждой посещённой клетки), чтобы восстановить путь.
  • Для BFS/DFS используй deque (очередь) и list (стек).
  • Визуализацию в консоли можно сделать с помощью os.system('cls' if os.name == 'nt' else 'clear') для перерисовки.