Теория
Что такое рекурсия
Функция может вызывать другую функцию. Но функция также может вызывать и саму себя — это и есть рекурсия. Простейшая иллюстрация:
def short_story():
print("У попа была собака, он ее любил.")
print("Она съела кусок мяса, он ее убил,")
print("В землю закопал и надпись написал:")
short_story() # бесконечный рекурсивный вызов
Подобный прием (вызов функцией самой себя) называется рекурсией, а сама функция — рекурсивной.
Классический пример: факториал
Хорошо известно, что 0! = 1, 1! = 1. Для n > 1 используем соотношение n! = n · (n − 1)! — пока не дойдём до базы 0!.
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
print(factorial(5))
Проблемы рекурсии
- Бесконечная рекурсия: цепочка вызовов «уходит вниз», пока не закончится стек. Частые причины:
- Нет корректного условия выхода (базового случая).
- Рекурсивный вызов сделан с неправильными параметрами (например, не уменьшаем аргумент).
- Ограничение глубины:
RecursionError: maximum recursion depth exceeded in comparison— по умолчанию предел около 1000 вызовов. Решение из презентации: увеличить лимит стека.
import sys
sys.setrecursionlimit(10_000)
Ускорение вычислений: кэширование (мемоизация)
Рекурсивные функции часто пересчитывают одни и те же значения. Кэширование (мемоизация) устраняет повторные вычисления.
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(50)) # очень быстро благодаря кэшированию
Использование кэша и предварительных вычислений
Пример с «глубокой» рекурсией и большим числом однотипных вызовов:
import sys
sys.setrecursionlimit(50_000)
def f(n):
if n < 20:
return n
else:
return (n - 6) * f(n - 7)
for i in range (1, 47):
f(i)
print((f(47872) - 290 * f(47865)) / f(47858))
Чтобы избежать повторных вычислений, предложено предварительно заполнить кэш (через цикл) и использовать @lru_cache:
from functools import lru_cache
@lru_cache(None)
def f(n):
if n < 20:
return n
else:
return (n - 6) * f(n - 7)
for i in range(20, 47973):
f(i) # прогрев кэша
print((f(47872) - 290 * f(47865)) / f(47858))
Примечание: в презентационных примерах деление / используется буквально; для целочисленной рекурсии в Python 3 при необходимости применяйте //
Примеры решений заданий
Задание 1
Условие: Алгоритм вычисления значения функции F(n), где n — целое неотрицательное число:
F(0) = 0;F(n) = F(n / 2), еслиn > 0и при этом чётно;F(n) = 1 + F(n − 1), еслиnнечётно.
Сколько существует таких чисел n, что 1 ≤ n ≤ 1000 и F(n) = 3? :contentReference[oaicite:7]{index=7}
def f(n):
if n == 0:
return 0
elif n % 2 == 0:
return f(n / 2) # в презентации — деление /; при желании замените на //
else:
return 1 + f(n - 1)
cnt = 0
for n in range(1, 1001):
if f(n) == 3:
cnt += 1
print(cnt)
Задание 2
Условие (из презентации): Алгоритм вычисления F(n), где n — натуральное:
F(1) = 1F(2) = 3F(n) = F(n − 1) · n + F(n − 2) · (n − 1), приn > 2.
Чему равно F(5)? В ответе запишите только натуральное число. :contentReference[oaicite:8]{index=8}
def f(n):
if n == 1:
return 1
elif n == 2:
return 3
else:
return f(n - 1) * n + f(n - 2) * (n - 1)
print(f(5))
Задание 3
Условие (из презентации): Обозначим mod(a, b) — остаток от деления натурального a на натуральное b. Алгоритм F(n), где n — целое неотрицательное:
F(0) = 0;F(n) = F(n / 3), еслиn > 0иmod(n, 3) = 0;F(n) = mod(n, 3) + F(n − mod(n, 3)), еслиmod(n, 3) > 0.
Назовите минимальное значение n, для которого F(n) = 9. :contentReference[oaicite:9]{index=9}
def f(n):
if n == 0:
return 0
elif n % 3 == 0:
return f(n / 3) # как в презентации; при необходимости используйте //
else:
return n % 3 + f(n - n % 3)
for n in range(0, 1000):
if f(n) == 9:
print(n)
break
Задание 4
Условие (из презентации):
F(0) = 0;F(n) = F(n − 1) + 1, еслиnнечётно;F(n) = F(n / 2), еслиn > 0иnчётно.
Укажите количество таких значений n < 1 000 000 000, для которых F(n) = 3. В презентации дано пояснение: эта рекурсивная функция фактически считает число единиц в двоичной записи n, то есть ищем числа, у которых ровно три «1». Число 1 000 000 000 имеет 30 двоичных разрядов ⇒ разместить 3 единицы в 30 позициях: C(30,3). :contentReference[oaicite:10]{index=10}
import math
def C(n, k): return math.comb(n, k)
print(C(30, 3))
Практические ремарки
- В заданиях, где деление указано как «
/», это цитата из презентации. В рабочих решениях на Python целочисленную рекурсию корректнее писать с//, чтобы не превращать аргументы в float и не усложнять кэширование. :contentReference[oaicite:11]{index=11} - При больших глубинах вызовов используйте
sys.setrecursionlimitи/или@lru_cache(как показано выше). :contentReference[oaicite:12]{index=12}
-
- «Чётно / нечётно» — почти всегда «ход» к делению на 2 и двоичной записи.
- «mod(n, k)» — поиск суммы цифр в базе
kили переходов по базеk. - Линейные рекурсии (как в Задании 2) — удобно считать снизу вверх (итеративно) при больших
n, либо кэшировать. :contentReference[oaicite:13]{index=13}
Задания для подготовки
Простые
- https://kompege.ru/task?id=15
- https://kompege.ru/task?id=60
- https://kompege.ru/task?id=99
- https://kompege.ru/task?id=591 Смотреть разбор
- https://kompege.ru/task?id=1129
Средние
- https://inf-ege.sdamgia.ru/problem?id=55633 Смотреть разбор
- https://inf-ege.sdamgia.ru/problem?id=56544 Смотреть разбор
- https://inf-ege.sdamgia.ru/problem?id=48437 Смотреть разбор
- https://inf-ege.sdamgia.ru/problem?id=47013 Смотреть разбор
- https://inf-ege.sdamgia.ru/problem?id=48464 Смотреть разбор
- https://kompege.ru/task?id=928 Смотреть разбор
- https://kompege.ru/task?id=20962 Смотреть разбор
- https://kompege.ru/task?id=21711 Смотреть разбор
- https://kompege.ru/task?id=23200 Смотреть разбор
- https://kompege.ru/task?id=23275 Смотреть разбор
- https://kompege.ru/task?id=21902 Смотреть разбор
- https://kompege.ru/task?id=23562 Смотреть разбор
- https://kompege.ru/task?id=628 Смотреть разбор
Сложные
- https://inf-ege.sdamgia.ru/problem?id=62470 Код Смотреть разбор
- https://kompege.ru/task?id=11531
- https://kompege.ru/task?id=6848
- https://kompege.ru/task?id=3034
- https://kompege.ru/task?id=3033
- https://kompege.ru/task?id=19801
- https://kompege.ru/task?id=14337
- https://kompege.ru/task?id=13301
- https://kompege.ru/task?id=9703
- https://kompege.ru/task?id=9371
- https://kompege.ru/task?id=9069
- https://kompege.ru/task?id=8372
- https://kompege.ru/task?id=6790
- https://kompege.ru/task?id=6269
- https://kompege.ru/task?id=5881
- https://kompege.ru/task?id=5230
- https://kompege.ru/task?id=3369
- https://kompege.ru/task?id=3085
- https://kompege.ru/task?id=2862
- https://kompege.ru/task?id=2709
- https://kompege.ru/task?id=2248
- https://kompege.ru/task?id=2247
- https://kompege.ru/task?id=780
- https://kompege.ru/task?id=581
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8479
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8478
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8477
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8476
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8475
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8396
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8183
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8041
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8040
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8039
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=8038
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7425
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7389
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7251
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7250
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7249
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7248
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7247
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7246
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7245
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7244
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7243
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7242
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7241
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7240
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7239
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=6347
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=5996
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=5923
- https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=5922
- https://education.yandex.ru/ege/inf/task/8ec5b1dd-8425-43f3-b795-c3beb1b65d9b
- https://education.yandex.ru/ege/inf/task/c2de1849-f600-4507-8972-bf4117c7fc31
- https://education.yandex.ru/ege/inf/task/768a7194-db18-4b74-ac4a-43d063659eb9
- https://education.yandex.ru/ege/inf/task/6024f406-7fe8-4d26-9f7c-a0e5b216cfb2
- https://education.yandex.ru/ege/inf/task/f7b497e4-c082-46d7-81c5-b8a17d36479c
- https://education.yandex.ru/ege/inf/task/331b792d-6dda-40b9-9e6b-8f1808a73c94
- https://education.yandex.ru/ege/inf/task/bdce5058-d4f0-47ee-971a-d0fce36d9068
- https://education.yandex.ru/ege/inf/task/7f679fe9-e69a-4764-8d0e-61c52af8712b
- https://education.yandex.ru/ege/inf/task/288921a2-05cf-4eff-9754-9ddc9aceb382
- https://education.yandex.ru/ege/inf/task/69f7b81f-7df6-4bb9-b1d0-abb5de248068
- https://education.yandex.ru/ege/inf/task/e6698118-3eea-44a7-9649-652cc0eb183a
