Задача
Дано:
- F(1)=1 при n <= 1
- F(n)=n+F(n−1) при n>1
- n - натуральное число
Найти: F(2023)−F(2020)
Решение рекурсивной функцией. Разбор
Принцип: Функция вызывает саму себя согласно рекуррентному соотношению
Рекурсия — это метод, когда функция вызывает саму себя с изменёнными аргументами до тех пор, пока не достигнет базового случая
В данной задаче:
-
Базовый случай: F(1)=1 — это "якорь" (база), где рекурсия останавливается
-
Рекурсивный случай: F(n)=n+F(n−1) — функция вызывает сама себя с меньшим аргументом
Как работает рекурсивный вызов (дерево вызовов):
F(2023) → нужно вычислить 2023 + F(2022)
↓
F(2022) → нужно вычислить 2022 + F(2021)
↓
F(2021) → нужно вычислить 2021 + F(2020)
↓
F(2020) → нужно вычислить 2020 + F(2019)
↓
... (продолжается вниз)
↓
F(3) → нужно вычислить 3 + F(2)
↓
F(2) → нужно вычислить 2 + F(1)
↓
F(1) → возвращает 1 (базовый случай!)
Затем начинается "раскрутка" назад:
F(1) = 1
F(2) = 2 + F(1) = 2 + 1 = 3
F(3) = 3 + F(2) = 3 + 3 = 6
F(4) = 4 + F(3) = 4 + 6 = 10
...
F(2020) = 2020 + F(2019) = ... (большое число)
F(2021) = 2021 + F(2020) = ...
F(2022) = 2022 + F(2021) = ...
F(2023) = 2023 + F(2022) = ...
Код на Python:
def F(n):
if n == 1: return 1
else:
return n + F(n - 1)
print(F(2023) - F(2020))
Строка if n == 1: — проверка базового случая, без которого рекурсия будет бесконечной
Строка return n + F(n - 1) — рекурсивный вызов: функция вызывает саму себя с аргументом n−1n−1, что гарантирует постепенное приближение к базовому случаю
Строка print(F(2023) - F(2020)) — вычисление разности двух независимых рекурсивных вызовов
Пошаговое выполнение:
- Сначала вычисляется
F(2023):
- Вызов
F(2023) → 2023 + F(2022)
- Вызов
F(2022) → 2022 + F(2021)
- Вызов
F(2021) → 2021 + F(2020)
- Вызов
F(2020) → вычисляется полностью до F(1)
- Затем вычисляется
F(2020) заново (неэффективно!)
- Вычитание:
F(2023) - F(2020)
Минусы
Стек вызовов — это область памяти, где хранятся данные о всех незавершённых вызовах функций
При вызове F(2023):
-
Создаётся запись для F(2023) в стеке
-
Затем для F(2022) (первый вызов ещё не завершён!)
-
Затем для F(2021), и так далее...
-
В итоге в стеке одновременно хранятся 2023 записи
Проблема: В Python по умолчанию максимальная глубина рекурсии — около 1000 вызовов. При попытке вызвать F(2023) возникнет ошибка:
RecursionError: maximum recursion depth exceeded
Решение: Можно увеличить лимит с помощью:
import sys
sys.setrecursionlimit(3000)
Но это только откладывает проблему — для очень больших nn рекурсия всё равно непрактична
Избыточные вычисления:
Время выполнения: O(n) для каждого вызова, итого O(n²) в худшем случае.
Преимущества рекурсивного подхода
Простота реализации: Код максимально близок к математическому определению функции
Понятность: Легко понять логику — каждый шаг соответствует одному рекурсивному соотношению
Отладка: При малых значениях n легко проследить выполнение функции
Когда использовать рекурсию
Рекурсивный подход оправдан для:
Для больших n (как в задаче с 2023) лучше использовать динамическое программирование или аналитическую формулу
Оптимизация: мемоизация
Чтобы избежать повторных вычислений, можно сохранять уже вычисленные значения в словаре:
cache = {}
def F(n):
if n == 1:
return 1
if n in cache: # Если уже вычисляли
return cache[n]
result = n + F(n - 1)
cache[n] = result # Сохраняем результат
return result
print(F(2023) - F(2020))
Это улучшает эффективность при множественных вызовах с одинаковыми аргументами