Информатика · тема 12 из 13 · ЕГЭ № 16

Рекурсивные функции

Как работает функция, которая вызывает сама себя: база и шаг, дерево вызовов, порядок печати и вычисление больших значений.

16 мин чтенияЕГЭ12 заданий

Коротко

главное за 30 секунд
  1. У рекурсии два обязательных элемента: база (условие выхода) и шаг (вызов себя для меньшей задачи)
  2. Вызовы копятся в стеке; значения возвращаются от самого глубокого вызова к первому
  3. Печать до рекурсивного вызова идёт «на спуске», после вызова — «на подъёме»
  4. Число вызовов и значения удобно считать по дереву вызовов или таблицей значений от малых n
  5. Функцию с повторными подзадачами ускоряет @lru_cache; для глубокой рекурсии нужен sys.setrecursionlimit

База и шаг

Рекурсивная функция — функция, которая в своём теле вызывает саму себя. Чтобы вызовы не шли бесконечно, нужны две части.

База — случай, где ответ известен сразу (например, F(1) = 1).
Шаг — сведение задачи к такой же, но меньшей (F(n) выражается через F(n − 1)).
def F(n):
    if n == 1:          # база
        return 1
    return 2 * F(n - 1) + 1    # шаг

print(F(5))   # 31
Ловушка. Без базы или с шагом, который не приближает к базе, программа вызовет ошибку RecursionError (переполнение стека вызовов).
Проверь себя. F(1) = 1, F(n) = 2·F(n − 1) + 1. Чему равно F(5)?

Ответ: 31

F(3) = 3 · F(2)= 6F(2) = 2 · F(1)= 2F(1) = 1= 1вызоввозврат
Рис. 1. Вызовы F(3) → F(2) → F(1) копятся в стеке, значения возвращаются вверх: 1, 2, 6

Как вычисляют значение: спуск и подъём

Чтобы найти F(5), компьютер вызывает F(4), тот — F(3) и так до базы F(1). Затем значения возвращаются наверх: F(2) = 3, F(3) = 7, F(4) = 15, F(5) = 31.

На бумаге проще идти от малых n вверх: заполните таблицу значений F(1), F(2), F(3), … по формуле шага.

Пример (ЕГЭ-16). F(n) = n при n ≤ 10; F(n) = n + F(n − 3) при n > 10. Найти F(20): F(20) = 20 + F(17) = 20 + 17 + F(14) = 37 + 14 + F(11) = 51 + 11 + F(8) = 62 + 8 = 70.

Для больших n (например, 2024) не считайте всё подряд. Ищите закономерность: часто в разности F(n)/F(n−k) многое сокращается. Например, если F(n) = n·F(n − 1), то F(2024)/F(2022) = 2024 · 2023.

Порядок печати и дерево вызовов

Печать до рекурсивного вызова выполняется по пути вниз, после — на пути вверх.

def f(n):
    if n > 0:
        print(n, end='')
        f(n - 1)
        print(n, end='')

f(3)     # 321123

Если функция вызывает себя дважды (как числа Фибоначчи), вызовы образуют дерево. Число вызовов: G(1) = G(2) = 1 вызов; G(n) = 1 + G(n − 1) + G(n − 2).

Пример. Функция def F(n): return 1 if n < 3 else F(n − 1) + F(n − 2). Число вызовов при F(5): G(3) = 3, G(4) = 5, G(5) = 1 + 5 + 3 = 9.

F(5)F(4)F(3)F(3)F(2)F(2)F(1)F(2)F(1)9 вызововF(3) — дважды
Рис. 2. Дерево вызовов F(5) при F(n) = F(n−1) + F(n−2): всего 9 вызовов, F(3) считается дважды
спуск: печатаем до вызова321f(0): стопподъём: печатаем после вызова123на экране: 3 2 1 1 2 3
Рис. 3. f(3): печать до рекурсивного вызова идёт на спуске (3 2 1), после вызова — на подъёме (1 2 3)

Python: запоминание и глубина

У дерева вызовов много одинаковых поддеревьев (F(3) считается дважды и более). Декоратор lru_cache запоминает уже найденные значения и делает вычисление быстрым.

from functools import lru_cache
import sys
sys.setrecursionlimit(100000)     # глубина по умолчанию около 1000

@lru_cache(None)
def F(n):
    if n <= 10:
        return n
    return n + F(n - 3)

print(F(2024) - F(2021))
Проверь себя. Функция считает F(n) = F(n − 1) + F(n − 2) при n ≥ 3, F(1) = F(2) = 1. Сколько вызовов будет сделано при вычислении F(5) без запоминания?

Ответ: 9

1F(1)1F(2)2F(3)3F(4)5F(5)8F(6)13F(7)21F(8)F(n) = F(n − 1) + F(n − 2)
Рис. 4. Числа Фибоначчи строятся снизу вверх: каждое — сумма двух предыдущих (так работает и lru_cache)
Практика

Закрепите тему: 12 заданий для ЕГЭ

От простых к сложным: базовый уровень — 3, повышенный — 6, высокий — 3. Мгновенная проверка, подсказки, подробные решения и тренажёр ошибок.

Решать задания по теме Флеш-карточки