Files
miha-kr/OOP/task/7/TASK.md
T
Dmitry Belan e00320edb1
CI Check / lint-and-check (push) Failing after 9s
update: update task 7
2026-07-14 17:28:57 +03:00

63 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### Последняя Надежда Эпсилон-9: Протокол «К-Резонанс»
**Ограничение по времени:** 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)`. Он должен прочесать сохраненную базу и найти `k` самых близких соседей для неизвестного растения `test_point`. От тебя требуется вернуть только список их классов-строк.
— Но соседи могут расходиться во мнениях, — Лира хмурится. — Нам нужна логика большинства. Метод `majority_vote(self, neighbors)` в этом же классе примет этот список и вернет тот класс, который встречается чаще всего. Если будет ничья — система должна выжить, просто возвращай любой из лидирующих классов. Это и будет наш приговор.
Она тяжело вздыхает, опираясь на стол:
— Оберни этот поиск и голосование в единый боевой интерфейс `predict_class(self, test_point)`. Ему мы будем скармливать одно неизвестное нормализованное растение, чтобы он сам вызвал поиск соседей, провел голосование и сразу выдал итоговый класс-строку.
— Но прежде чем отправить харвестеры в поля и кормить людей, — голос Лиры дрожит, — мы обязаны проверить алгоритм на архивах. Создай отдельный класс `AccuracyCalculator`. В нем будет всего один метод `calculate(self, predictions, true_labels)`. Сравни списки наших предсказаний с истинными классами на тестовом полигоне и верни долю правильных ответов от `0.0` до `1.0`. Цена каждой ошибки — чья-то жизнь.
Лира кладет руку вам на плечо. В её глазах отражается код аварийного терминала:
— И наконец, Инженер... Великий Синтез. Вне классов напиши финальную функцию `run_protocol(train_raw_lines, test_raw_lines, k)`. Она должна объединить весь пайплайн воедино. Создай экземпляры `DataProcessor`, `KNNClassifier` и `AccuracyCalculator`. Сначала распарси обе выборки. Затем вычисли статистику строго и только по обучающей базе, чтобы не было утечки данных из будущего. Потом нормализуй обе выборки. Скорми обучающие данные в метод `fit`. Пройдись циклом и сделай предсказания для каждого растения в тесте. В самом конце функция должна рассчитать точность и вернуть словарь с двумя ключами: `"accuracy"` с итоговой точностью и `"predictions"` со списком всех предсказанных классов.
Терминал призывно мигает пустым курсором. Система охлаждения реактора издает последний предупреждающий гудок. Жизнь Эпсилон-9 зависит от чистоты твоей архитектуры. Приступай.
---
### Блок тестирования: Симуляция запуска на архивных данных
Лира выводит на соседний голографический экран небольшую выборку исторических данных.
— Инженер, мы не можем рисковать. Прежде чем подключать твой код напрямую к харвестерам, запусти симуляцию на этом крошечном наборе. Если твой «Синтез» работает верно, мы спасены.
**Требования к проверке:**
1. Запишите в код следующие архивные данные:
```python
train_raw = [
"0.5, 1.2, Звездный Корень",
"0.6, 1.3, Звездный Корень",
"5.1, 8.2, Теневая Лоза",
"4.9, 8.1, Теневая Лоза"
]
test_raw = [
"0.55, 1.25, Звездный Корень",
"5.0, 8.15, Теневая Лоза"
]