Подтема 2 из 6
Рекурсивная функция на Python
Как записать определение функцией Python, проверить, что вызовы доходят до базы, и решить задачу темы программой.
Функция по определению
Как перевести определение функции на Python?
Функцию записывают прямо по определению: база — проверка и return с готовым числом, рекуррентное соотношение — return с вызовом той же функции. Вот функция задачи темы. Нажми «Запуск» и пройди программу по шагам:
def F(n): if n == 1: return 1 return n * F(n - 1) print(F(5))| В определении | В программе |
|---|---|
| функция F от аргумента n | def F(n): |
| F(n) = 1 при n = 1 | if n == 1: return 1 |
| F(n) = n · F(n − 1) | return n * F(n - 1) |
Разберём строки по порядку:
- создаёт функцию F.
n— её параметр: при вызовеF(5)в n попадает 5; if n == 1:— проверка. Двойной знак сравнивает, а одинарный=присваивает;return 1завершает вызов и отдаёт 1 туда, откуда функцию вызвали. Строки ниже в этом вызове уже не выполняются;return n * F(n - 1)вызывает F(n − 1), ждёт ответа, умножает его на n и отдаёт результат.
В пошаговом запуске видно то же, что мы делали вручную: вызовы F(5), F(4), F(3), F(2) ждут, у каждого своё n, а после базы возвращаются 1, 2, 6, 24, 120.
Тонкости:return и print
print только показывает число на экране, а return отдаёт его тому, кто вызвал функцию. Функция без return возвращает — «ничего», и выражение n * F(n - 1) с таким ответом остановится ошибкой. Строки тела функции пишут с отступом: по нему Python понимает, что относится к функции, а что — к if.
Ветви в программе
Как записать формулу с ветвями?
Каждая ветвь определения — своя проверка: , затем elif («иначе, если»), а для последней — else («иначе»). Функция с ветвями из прошлой подтемы:
def F(n): if n <= 2: return n elif n % 2 == 0: return F(n - 1) + n else: return 2 * F(n - 2) print(F(8))Чётность проверяют остатком от деления на 2: — остаток, и n % 2 == 0 значит «n чётно». Ветвь Python выбирает в каждом вызове по его собственному n — как мы делали вручную. Программа печатает 16.
Тонкости:лишние проверки
После if n <= 2 в ветви elif и else попадают только n > 2, поэтому повторять «n > 2» не нужно. А условие «F(n − 1) чётно» проверяет уже значение функции, а не аргумент: F(n - 1) % 2 == 0.
Вызовы доходят до базы
Всегда ли программа по определению досчитает до конца?
Только если от нужного аргумента все вызовы доходят до базы. Например: F(1) = 1; F(n) = F(n − 2) + n при n > 1. Для F(7) аргументы 7, 5, 3, 1 — база достигнута. Для F(6) — 6, 4, 2, 0, −2, … База пропущена: вычитание 2 не меняет чётность.
Навечно программа на F(6) не зависнет: когда ждущих вызовов станет около тысячи, Python остановит её ошибкой RecursionError — превышен .
Поэтому перед программой проверь по условию:
- какие аргументы допустимы: «натуральное» — 1, 2, 3, …, «целое неотрицательное» — 0, 1, 2, …;
- куда ведут переходы — вниз или вверх и через сколько чисел;
- попадает ли каждая нужная цепочка в базу.
В задачах экзамена база подобрана так, что нужные вызовы до неё доходят. Проверка спасает от ошибки при переписывании: если в условии база «n ≤ 1», а в программе if n == 1, чётная цепочка пролетит мимо.
Задача темы программой
Как решить задачу темы программой?
Функция уже есть — осталось напечатать выражение из условия. Но прямой запуск остановится ошибкой:
print((F(2026) + 3 * F(2025)) // F(2024))F(2026) ждёт F(2025), тот — F(2024), и так до F(1): больше двух тысяч ждущих вызовов. А Python по умолчанию разрешает около тысячи. Предел поднимают в начале программы:
import sys sys.setrecursionlimit(5000) def F(n): if n == 1: return 1 return n * F(n - 1) print((F(2026) + 3 * F(2025)) // F(2024)) подключает модуль с настройками Python, а sys.setrecursionlimit(5000) разрешает до 5000 ждущих вызовов — с запасом для 2026. Ответ задачи темы — 4 108 725.
Деление записано через //: выражение делится нацело (почему — увидим в подтеме 4), а // делит целые числа точно. Обычное / напечатало бы дробное 4108725.0.
Тонкости:если поднятого предела мало
Предел в десятки тысяч ставить рискованно: память под ждущие вызовы может кончиться, и программа закроется без сообщения. Для аргументов в десятки тысяч надёжнее прогрев кеша или таблица — они в следующей подтеме.
Итого
def F(n):функция с параметром nif n == 1: return 1база — ответ без нового вызоваreturn n * F(n - 1)рекуррентное соотношение — вызов себя- if / elif / elseветви, Python выбирает их в каждом вызове
- До базыкаждая нужная цепочка аргументов попадает в базу
sys.setrecursionlimitбольше ждущих вызовов — для аргументов в тысячи//точное деление нацело
Проверьте себя
Ответь сначала сам, потом открой ответ.
F(1) в программе задачи темы? Дойдёт ли вызов до строки return n * F(n - 1)?ОтветСкрыть ответ
return 1 сразу завершает вызов.ОтветСкрыть ответ
return n * F(n - 1) написали print(n * F(n - 1)). Что произойдёт при вызове F(3)?ОтветСкрыть ответ
None), и 3 * None не вычисляется.print(F(2026)) без sys.setrecursionlimit останавливается ошибкой?ОтветСкрыть ответ
if n == 1:. Что будет при вызове F(6)?ОтветСкрыть ответ
RecursionError. По условию база — n ≤ 1: if n <= 1:.Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Функция по определению
F(1) = 4; F(n) = 2 · F(n − 1) − n при n > 1. Запиши функцию на Python по определению и найди F(6).
Введите число.
Подсказка
База — if n == 1: return 4, шаг — return 2 * F(n - 1) - n.
Решение
Программа повторяет определение: база и рекуррентное соотношение. Значения F(2), …, F(6): 6, 9, 14, 23, 40.
def F(n): if n == 1: return 4 return 2 * F(n - 1) - n print(F(6))Ответ: 40
Деление аргумента пополам
F(1) = 0; F(n) = F(n // 2) + 1 при n > 1. Найди F(1 000 000).
Введите число.
Подсказка
Каждый вызов делит аргумент пополам — шагов до 1 немного, программа справится сразу.
Решение
Аргумент делится пополам: 1 000 000, 500 000, 250 000, …, 1. До единицы — 19 делений, и каждое добавляет 1. Ждущих вызовов мало, предел поднимать не нужно.
def F(n): if n == 1: return 0 return F(n // 2) + 1 print(F(1_000_000))Ответ: 19
Задача в формате экзамена: F(20)
Алгоритм вычисления значения функции F(n), где n — натуральное число, задан следующими соотношениями: F(n) = n при n < 3; F(n) = F(n − 1) + n, если n ≥ 3 и n делится на 3; F(n) = F(n − 2) + 2, если n ≥ 3 и n не делится на 3. Чему равно значение функции F(20)?
Введите число.
Подсказка
Три ветви — if, elif, else; делимость на 3 проверяй через n % 3 == 0.
Решение
Записываем три ветви определения и печатаем F(20). Вручную было бы долго: F(20) = F(18) + 2, F(18) = F(17) + 18 и так далее.
def F(n): if n < 3: return n elif n % 3 == 0: return F(n - 1) + n else: return F(n - 2) + 2 print(F(20))Ответ: 77
Дойдёт ли вызов до базы
n — целое неотрицательное число: F(0) = 0; F(n) = F(n − 3) + 1 при n > 0. Для какого из чисел 10, 11, 12 вызов F(n) дойдёт до базы?
Введите число.
Подсказка
Шаг 3 не меняет остаток от деления на 3, а база — 0.
Решение
Вычитание 3 сохраняет остаток от деления на 3, поэтому до 0 доходят только кратные 3: 12, 9, 6, 3, 0. От 10 цепочка 10, 7, 4, 1, −2 проскакивает 0, от 11 — 11, 8, 5, 2, −1. Проверка: пройдём цепочку от каждого числа и посмотрим, попадёт ли она ровно в 0:
for start in (10, 11, 12): n = start while n > 0: n -= 3 if n == 0: print(start)Ответ: 12
Предел глубины
F(1) = 1; F(n) = F(n − 1) + n % 7 при n > 1. Найди F(3000).
Введите число.
Подсказка
Прямая рекурсия упрётся в предел глубины — подними его через sys.setrecursionlimit.
Решение
От F(3000) до базы — почти три тысячи ждущих вызовов, а по умолчанию Python разрешает около тысячи. Поднимаем предел и считаем:
import sys sys.setrecursionlimit(5000) def F(n): if n == 1: return 1 return F(n - 1) + n % 7 print(F(3000))Ответ: 8998