Files
miha-kr/OOP/task/7/TASK.md
T
2026-07-14 12:46:00 +03:00

11 KiB

Ограничение по времени: 3.0 с Ограничение по памяти: 512 МБ

Год 2142. Межзвездная колония на планете Эпсилон-9 столкнулась с невидимой смертью. Внешне безобидная флора планеты скрывает фатальную угрозу: питательный, спасающий от голода «Звездный Корень» и парализующая нервную систему «Теневая Лоза» визуально абсолютно неразличимы. Их выдают только биоэлектрические поля, которые сканируют наши харвестеры.

До вчерашнего дня наш Центральный ИИ «Тензор-Оракул» успешно классифицировал растения. Но аномальная вспышка на местной звезде сожгла серверные стойки. Библиотеки машинного обучения расплавились в цифровом небытии. Остался только аварийный терминал жизнеобеспечения. Он принимает исключительно базовый, первобытный диалект языка Python. Использовать оператор import категорически запрещено — модулей просто не существует. Голод неминуем. Склады забиты смертельно опасным неклассифицированным урожаем.

Командор колонии Лира врывается в ваш инженерный отсек. В её руках дымящийся кристалл памяти.

— Инженер! — кричит она, смахивая пепел с терминала. — Я знаю, ты привык поднимать надежный бэкенд и обучать сложные модели, но сейчас от твоих навыков зависят наши жизни! Я вытащила из мертвых архивов концепты древнего алгоритма K-Ближайших Соседей. Мы должны воссоздать его с нуля, и нам нужна строгая архитектура, чтобы система не рухнула. Будем писать объектно-ориентированно. Слушай меня внимательно, я зачитаю спецификацию протокола прямо с поврежденных секторов, а ты переноси это в код!

Лира подключает кристалл к вашему монитору, и аварийное освещение окрашивает отсек в красный цвет.

— Разделим логику выживания на модули, — диктует Командор, глядя на бегущие строки. — Сначала создай класс DataProcessor. Харвестеры присылают нам сплошной текстовый мусор. Напиши в нем метод parse_sensor_data(self, record). Он должен принимать сырую строку, где числа и класс разделены запятыми, и возвращать кортеж. Первым элементом в нем пойдет список чисел с плавающей точкой — сырые признаки. Вторым — строка класса растения.

Она нервно стучит по экрану: — Данные с сенсоров скачут! Чтобы алгоритм не сошел с ума от разницы в масштабах измерений, нужна калибровка. Добавь метод calculate_statistics(self, dataset). Прогони через него размеченные данные и вычисли для каждого признака среднее арифметическое и стандартное отклонение. Верни это как список словарей с ключами "mean" и "std". И помни, корень придется извлекать возведением в степень 0.5, математических модулей у нас нет!

— Как только получим статистическую базу, — продолжает Лира, — пиши метод normalize_features(self, features, stats). Каждый сырой признак растения нужно очистить от шума, вычтя среднее и разделив на стандартное отклонение. Верни новый список идеальных, нормализованных чисел.

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

— Теперь архитектура самого предсказателя! Создай класс KNNClassifier. При инициализации через __init__(self, k) он должен принимать и сохранять внутри себя количество соседей k. Затем заложи в него геометрию выживания — метод euclidean_distance(self, vector_a, vector_b). Он берет два нормализованных списка чисел и вычисляет дистанцию между ними в многомерном пространстве, возвращая одно число с плавающей точкой.

Лира выводит на экран голограмму базы данных: — Систему нужно обучить. Напиши метод fit(self, train_features, train_labels), который просто сохранит нормализованную обучающую выборку и её классы внутри объекта. А чтобы найти сородичей, добавь метод find_k_nearest(self, test_point). Он должен прочесать сохраненную базу train_features и найти k самых близких соседей для неизвестного растения, вернув список их классов-строк.

— Но соседи могут расходиться во мнениях, — Лира хмурится. — Нам нужна логика большинства. Метод majority_vote(self, neighbors) примет этот список классов и вернет тот, который встречается чаще всего. Если будет ничья — возвращай любой из лидирующих классов.

Она тяжело вздыхает, опираясь на стол: — Оберни этот поиск и голосование в единый боевой интерфейс predict_class(self, test_point). Этот метод примет одно неизвестное нормализованное растение и прогонит его через поиск соседей и голосование, чтобы сразу выдать итоговый класс-строку. А для массовой проверки на архивах добавь метод calculate_accuracy(self, predictions, true_labels). Сравни списки наших предсказаний с истинными классами и верни долю правильных ответов от 0.0 до 1.0. Цена каждой ошибки — чья-то жизнь.

Лира кладет руку вам на плечо. В её глазах отражается код аварийного терминала: — И наконец, Инженер... Великий Синтез. Снаружи классов напиши финальную функцию run_protocol(train_raw_lines, test_raw_lines, k). Она объединит весь пайплайн. Создай экземпляры DataProcessor и KNNClassifier. Распарси обе выборки. Затем вычисли статистику строго и только по обучающей базе, чтобы не было утечки данных из будущего. Нормализуй обе выборки. Скорми обучающие данные в метод fit. Пройдись циклом по тестовым растениям, собирая предсказания через predict_class, и рассчитай точность. В самом конце функция должна вернуть словарь с двумя ключами: "accuracy" с итоговой точностью и "predictions" со списком всех предсказанных классов.

Терминал призывно мигает пустым курсором. Система охлаждения реактора издает последний предупреждающий гудок. Жизнь Эпсилон-9 зависит от чистоты твоей архитектуры. Приступайте.


Блок тестирования протокола (Чек-лист от Командора)

Лира выводит на соседний экран строгую последовательность проверки модулей. — Как только напишешь классы, прогони систему через этот алгоритм запуска. Никаких отклонений!

  1. Запросите справку из архивов: Вызовите документацию (help()) по классам DataProcessor или KNNClassifier, чтобы убедиться, что сектора памяти целы и спецификация ясна.
  2. Создайте объекты спасения: Инициализируйте экземпляры классов DataProcessor и KNNClassifier (задайте нужное число соседей k).
  3. Откалибруйте систему: Используя объект класса DataProcessor, обработайте сырые данные (рассчитайте статистику и нормализуйте базу).
  4. Попробуйте классифицировать: Попробуйте прогнать через систему неизвестное растение (predict_class), чтобы проверить отклик пустой базы.
  5. Завершите обучение: Если алгоритм еще не готов к бою (выдает ошибку или пустой ответ из-за отсутствия базы), загрузите в него нормализованную обучающую выборку (вызовите метод fit).
  6. Соберите "урожай": Как только модель обучена, запустите финальное сканирование тестовой базы, классифицируйте все растения и выведите результат предсказаний.