К содержанию

Подтема 2 из 6

Рекурсивная функция на Python

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

Функция по определению

Как перевести определение функции на Python?

Функцию записывают прямо по определению: база — проверка и return с готовым числом, рекуррентное соотношение — return с вызовом той же функции. Вот функция задачи темы. Нажми «Запуск» и пройди программу по шагам:

def F(n):    if n == 1:        return 1    return n * F(n - 1)  print(F(5))
120
В определенииВ программе
функция F от аргумента ndef F(n):
F(n) = 1 при n = 1if 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))
16

Чётность проверяют остатком от деления на 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 не меняет чётность.

Шаг −2: цепочка от 7 попадает в базу 1 за 3 перехода, цепочка от 6 проходит через 0 и уходит в отрицательные числа, не задев 1.

Навечно программа на F(6) не зависнет: когда ждущих вызовов станет около тысячи, Python остановит её ошибкой RecursionError — превышен .

Поэтому перед программой проверь по условию:

  • какие аргументы допустимы: «натуральное» — 1, 2, 3, …, «целое неотрицательное» — 0, 1, 2, …;
  • куда ведут переходы — вниз или вверх и через сколько чисел;
  • попадает ли каждая нужная цепочка в базу.

В задачах экзамена база подобрана так, что нужные вызовы до неё доходят. Проверка спасает от ошибки при переписывании: если в условии база «n ≤ 1», а в программе if n == 1, чётная цепочка пролетит мимо.

Задача темы программой

Как решить задачу темы программой?

Функция уже есть — осталось напечатать выражение из условия. Но прямой запуск остановится ошибкой:

print((F(2026) + 3 * F(2025)) // F(2024))
RecursionError: maximum recursion depth exceeded

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))
4108725

подключает модуль с настройками Python, а sys.setrecursionlimit(5000) разрешает до 5000 ждущих вызовов — с запасом для 2026. Ответ задачи темы — 4 108 725.

Деление записано через //: выражение делится нацело (почему — увидим в подтеме 4), а // делит целые числа точно. Обычное / напечатало бы дробное 4108725.0.

Тонкости:если поднятого предела мало

Предел в десятки тысяч ставить рискованно: память под ждущие вызовы может кончиться, и программа закроется без сообщения. Для аргументов в десятки тысяч надёжнее прогрев кеша или таблица — они в следующей подтеме.

Итого

  • def F(n):функция с параметром n
  • if n == 1: return 1база — ответ без нового вызова
  • return n * F(n - 1)рекуррентное соотношение — вызов себя
  • if / elif / elseветви, Python выбирает их в каждом вызове
  • До базыкаждая нужная цепочка аргументов попадает в базу
  • sys.setrecursionlimitбольше ждущих вызовов — для аргументов в тысячи
  • //точное деление нацело

Проверьте себя

Ответь сначала сам, потом открой ответ.

Вопрос 1
Что вернёт F(1) в программе задачи темы? Дойдёт ли вызов до строки return n * F(n - 1)?
ОтветСкрыть ответ
Ответ
1. Не дойдёт: return 1 сразу завершает вызов.
Вопрос 2
F(2) = 3; F(n) = n · F(n − 2) при n > 2, n — натуральное. Для какого из аргументов 8 и 9 этих правил хватит?
ОтветСкрыть ответ
Ответ
Для 8: аргументы 8, 6, 4, 2 доходят до базы. Для 9 цепочка 9, 7, 5, 3, 1 пролетает мимо F(2), а F(1) не задано.
Вопрос 3
В функции задачи темы вместо return n * F(n - 1) написали print(n * F(n - 1)). Что произойдёт при вызове F(3)?
ОтветСкрыть ответ
Ответ
Напечатается 2, а потом программа остановится ошибкой: F(2) ничего не вернёт (вернёт None), и 3 * None не вычисляется.
Вопрос 4
Почему print(F(2026)) без sys.setrecursionlimit останавливается ошибкой?
ОтветСкрыть ответ
Ответ
F(2026) ждёт F(2025), тот — F(2024), и так больше двух тысяч ждущих вызовов, а Python по умолчанию разрешает около тысячи.
Вопрос 5
F(n) = 1 при n ≤ 1; F(n) = F(n − 2) + n при n > 1. В программе базу записали как if n == 1:. Что будет при вызове F(6)?
ОтветСкрыть ответ
Ответ
Цепочка 6, 4, 2, 0, −2, … пролетит мимо 1 — ошибка RecursionError. По условию база — n ≤ 1: if n <= 1:.

Практика

Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.

Функция по определению

F(1) = 4; F(n) = 2 · F(n − 1) − n при n > 1. Запиши функцию на Python по определению и найди F(6).

Введите число.

Деление аргумента пополам

F(1) = 0; F(n) = F(n // 2) + 1 при n > 1. Найди F(1 000 000).

Введите число.

Задача в формате экзамена: 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)?

Введите число.

Дойдёт ли вызов до базы

n — целое неотрицательное число: F(0) = 0; F(n) = F(n − 3) + 1 при n > 0. Для какого из чисел 10, 11, 12 вызов F(n) дойдёт до базы?

Введите число.

F(1) = 1; F(n) = F(n − 1) + n % 7 при n > 1. Найди F(3000).

Введите число.