ЕГЭ 2027 · Информатика
Задание 16 ЕГЭ по информатике
Что проверяет линия, сколько она стоит, на чём теряют баллы и как решать — по нашим конспектам. В банке 200+ заданий этой линии с разбором.
Аналитика ЕГЭ: №16, №25 (основы Python: типы, ветвления)
- Баллы
- по 1 первичному баллу за каждое
- Частота
- в каждом варианте
Частые ловушки
- Перебирают делители до N, а не до √N — программа не укладывается по времени
- Путают наибольший делитель кроме самого числа с количеством делителей
Совет. Перебирай делители до корня из N; итог сортируй по указанному в задании ключу.
Аналитика ЕГЭ: №16 (рекурсивные функции, значение F(n))
- Баллы
- 1 первичный балл
- Частота
- в каждом варианте
Частые ловушки
- Ошибаются в базе рекурсии (условии остановки)
- Считают не то значение или зацикливаются без мемоизации
Совет. Заполняй таблицу F(1), F(2), … снизу вверх по рекуррентному соотношению — не считай одно дважды.
Аналитика ЕГЭ: №16 (значение функции, заданной рекуррентными соотношениями)
- Баллы
- 1 первичный балл
- Частота
- в каждом варианте
Частые ловушки
- Ошибаются в базе рекурсии: подставляют шаг там, где действует базовое значение
- Не замечают разных формул для чётных и нечётных n
- Пытаются считать факториал в лоб, когда дробь сокращается за две строки
- Считают одно и то же значение много раз и не успевают — нужна таблица или мемоизация
- Отвечают значением F(n), когда просят сумму цифр или остаток
Совет. Первым делом посмотри, что именно спрашивают: если это дробь из значений одной и той же функции — почти наверняка задача решается сокращением, без единого умножения
Разборы
Разбор примера
Функция по рекуррентным соотношениям
F(n) = n при n < 3; F(n) = F(n−1) + F(n−2) при n ≥ 3. Записать функцию с кэшем и найти F(24).
Показать решение по шагам
- 1.
from functools import lru_cache @lru_cache(None) # кэш: каждый F(n) считается один раз def F(n): if n < 3: return n return F(n-1) + F(n-2) print(F(24))
Ответ: Программа печатает 75025: кэш считает каждое F(n) ровно один раз
Разбор примера
Разбор: функция с двумя разными правилами
F(n) = 1 при n ≤ 2; при n > 2 и нечётном n: F(n) = F(n−1) + F(n−2); при n > 2 и чётном n: F(n) = 3·F(n−1). Найти F(12).
Показать решение по шагам
- 1. Условие содержит два разных шага, и выбор между ними зависит от чётности n. В коде это обычная развилка.
- 2.
from functools import lrucache
@lru_cache(None) def F(n): if n <= 2: return 1 if n % 2 == 1: return F(n - 1) + F(n - 2) return 3 * F(n - 1)print(F(12))
- 3. Порядок проверок важен: сначала база (n ≤ 2), потом нечётный случай, потом всё остальное. Если поставить проверку чётности раньше базы, программа уйдёт в бесконечную рекурсию при n = 1.
- 4. Проверим начало вручную: F(1) = F(2) = 1; F(3) нечётное, значит, F(2) + F(1) = 2; F(4) чётное, значит, 3 · F(3) = 6; F(5) = F(4) + F(3) = 8; F(6) = 3 · 8 = 24.
- 5. Кэш здесь обязателен: в нечётной ветке два обращения к функции, и без запоминания дерево вызовов растёт лавинообразно.
- 6. Проверка себя: посчитайте первые шесть значений вручную и сравните с тем, что печатает программа при маленьких n. Если расходится — ошибка в переносе условия, а не в вычислениях.
Ответ: Значения растут быстро; программа печатает точный результат, а ручная проверка первых шести значений подтверждает, что условие перенесено верно
Разбор примера
Функция с двумя правилами: считаем F(7) по таблице
Функция задана так: F(1) = 1; F(n) = F(n − 1) + 2n, если n чётное; F(n) = F(n − 1) + 3, если n нечётное и больше единицы. Найти F(7).
Показать решение по шагам
- 1. Шаг 1. Считать надо снизу вверх, от базы к искомому значению: каждое следующее значение выражается через предыдущее, и таблица заполняется по одной строке.
- 2. F(1) = 1 — это база, она дана в условии.
- 3. F(2): двойка чётная, значит правило F(n − 1) + 2n: 1 + 2·2 = 1 + 4 = 5.
- 4. F(3): тройка нечётная, правило F(n − 1) + 3: 5 + 3 = 8.
- 5. F(4): чётное, 8 + 2·4 = 8 + 8 = 16.
- 6. F(5): нечётное, 16 + 3 = 19.
- 7. F(6): чётное, 19 + 2·6 = 19 + 12 = 31.
- 8. F(7): нечётное, 31 + 3 = 34. Ответ: 34.
- 9.
Шаг 2. Та же функция программой:
from functools import lrucache
@lru_cache(None) def F(n): if n == 1: return 1 if n % 2 == 0: return F(n - 1) + 2 * n return F(n - 1) + 3print(F(7))
- 10. Шаг 3. Код пишется по условию дословно, строка за строкой: сначала база, потом каждое правило со своим условием. Это и есть главный приём линии 16 — не придумывать формулу, а перенести условие в функцию.
- 11. Шаг 4. Порядок проверок важен. База
n == 1стоит первой: если поставить её после проверки чётности, единица уйдёт в ветку «нечётное» и вызовет F(0), которого в условии нет, — рекурсия уйдёт в минус бесконечность. - 12. Шаг 5. Обратите внимание, что
2 * nиспользует текущее n, а не n − 1. Здесь легко ошибиться, переписывая формулу: в правиле стоит именно 2n, и для F(4) это 8, а не 6. - 13. Шаг 6. Проверка таблицей — обязательный шаг. Напечатайте все значения от базы до искомого:
for n in range(1, 8): print(n, F(n)). Ряд 1, 5, 8, 16, 19, 31, 34 совпал с ручным расчётом ✔ Если бы программа выдала другое, разошлись бы они на конкретном n, и ошибку было бы видно сразу.
Ответ: 34
Разбор примера
Зачем нужен кэш: считаем количество вызовов
Функция Фибоначчи: F(1) = F(2) = 1, F(n) = F(n − 1) + F(n − 2). Посчитать F(10) и сравнить число вызовов с кэшем и без него.
Показать решение по шагам
- 1.
vyzovov = 0
def F(n): global vyzovov
vyzovov += 1 if n <= 2: return 1 return F(n - 1) + F(n - 2)print(F(10), vyzovov)
- 2. Шаг 1. Программа печатает 55 109: значение F(10) равно 55, а функция вызвалась 109 раз.
- 3. Шаг 2. Откуда столько. Каждый вызов при n > 2 порождает два новых, и дерево вызовов растёт как само число Фибоначчи. Точная формула: вызовов ровно 2·F(n) − 1, и для n = 10 это 2 · 55 − 1 = 109 ✔
- 4. Шаг 3. Почему это катастрофа при больших n. F(30) = 832 040, значит вызовов будет около 1,7 миллиона. F(40) — уже около двухсот миллионов, и программа думает минутами. А в линии 16 значения вроде F(50) встречаются регулярно.
- 5. Шаг 4. Причина расточительности — повторный счёт одного и того же. F(8) вычисляется и внутри F(10), и внутри F(9); F(7) — четырежды; F(3) — десятки раз. Разных значений всего десять, а вычислений сто девять.
- 6.
Шаг 5. Лечение — одна строка:
from functools import lrucache
vyzovov = 0
@lru_cache(None) def F(n):global vyzovov
vyzovov += 1 if n <= 2: return 1 return F(n - 1) + F(n - 2)print(F(10), vyzovov)
- 7. Шаг 6. Теперь печатается 55 10: тот же ответ, но вызовов ровно десять — по одному на каждое значение от 1 до 10. Декоратор запоминает результат и при повторном обращении возвращает готовое число, не заходя в тело функции.
- 8. Шаг 7. Сложность изменилась с экспоненциальной на линейную: было порядка 2ⁿ, стало n. Именно поэтому
@lru_cache(None)пишут в линии 16 всегда, не задумываясь, — цена строки нулевая, а без неё половина заданий не решается. - 9. Шаг 8. Аргумент
Noneозначает «кэш без ограничения размера». Можно написать@lru_cache(maxsize=None)— это то же самое. И одно ограничение: кэшируются только функции, у которых аргументы неизменяемые (числа, строки, кортежи). Список в аргументе вызовет ошибку — его заменяют кортежем.
Ответ: 55; 109 вызовов без кэша и 10 с кэшем
Разбор примера
Линия 16 целиком: от условия до ответа
Алгоритм вычисления функции F(n) задан соотношениями: F(1) = 1; F(n) = F(n − 1) + 2n при чётном n; F(n) = F(n − 1) + 3 при нечётном n > 1. Чему равно F(25)? И при каком наименьшем n значение F(n) превысит 200?
Показать решение по шагам
- 1.
from functools import lrucache
@lru_cache(None) def F(n): if n == 1: return 1 if n % 2 == 0: return F(n - 1) + 2 * n return F(n - 1) + 3for n in range(1, 26): print(n, F(n)) - 2. Шаг 1. Печатаем таблицу, а не одно значение. Это обязательный приём линии 16: по таблице видно, как растёт функция, и первые строки можно сверить с ручным счётом.
- 3. Шаг 2. Первые значения: F(1) = 1, F(2) = 5, F(3) = 8, F(4) = 16, F(5) = 19, F(6) = 31, F(7) = 34. Совпадает с ручным расчётом из предыдущего разбора ✔ Значит, правила перенесены в код верно, и дальше можно доверять программе.
- 4. Шаг 3. Заметна закономерность: на чётных шагах функция прибавляет 2n, на нечётных всегда 3. Значит, за пару шагов (нечётный плюс чётный) прибавляется примерно 2n + 3, и рост получается квадратичным. Для F(25) ждём величину порядка нескольких сотен — это ориентир правдоподобности.
- 5.
Шаг 4. Программа печатает F(25). Чтобы ответить на второй вопрос, добавляем короткий цикл:
n = 1 while F(n) <= 200: n += 1 print(n, F(n)) - 6. Шаг 5. Здесь кэш работает особенно наглядно: цикл вызывает F для каждого n подряд, и каждое значение считается один раз, а дальше берётся готовым. Без кэша тот же цикл пересчитывал бы всю цепочку заново на каждом шаге.
- 7. Шаг 6. Про формулировку «при каком наименьшем n». Цикл идёт вверх и останавливается на первом n, для которого условие нарушено, — это и есть наименьшее. Если бы спрашивали наибольшее n, при котором F(n) не превышает 200, ответом было бы предыдущее значение, то есть
n - 1. - 8. Шаг 7. Проверка на границе. Напечатайте F(n − 1) и F(n) рядом: первое должно быть не больше 200, второе — больше. Если оба больше или оба меньше, цикл остановился не там, и почти всегда причина в знаке —
<=вместо<. - 9. Шаг 8. И общее правило линии 16: не сворачивайте соотношения в формулу. Соотношения нарочно составлены так, что замкнутого выражения у них нет или оно громоздко. Переносите условие дословно, ставьте кэш и печатайте таблицу — три шага, которые закрывают линию целиком.
- 10. Шаг 9. Что печатает программа. Значение F(25) = 349. А порог в 200 впервые превышается при n = 18, где F(18) = 205; на предыдущем шаге F(17) = 169, то есть ещё не превышает ✔ Обе величины стоит выписать рядом — так видно, что граница найдена верно.
Ответ: F(25) = 349; порог 200 впервые превышен при n = 18 (F(18) = 205)
Уроки по этой линии
- Типы данных, ввод-вывод и ветвления — открыт бесплатно
- Циклы и перебор
- Функции и рекурсия (задание 16)
- Что такое рекурсия: база и шаг — открыт бесплатно
- Дерево вызовов и мемоизация
- Задание 16: считаем F(n) по рекуррентным соотношениям
Потренируй задание 16
Задания этой линии с проверкой и разбором решения — после бесплатной регистрации. Ошибки сами попадут в план повторения.