Лекція 11: Рекурсивні функції
Уявіть, що ви стоїте між двома дзеркалами. Ви бачите своє відображення, в якому є ще одне відображення, в якому є ще одне... Це нескінченний процес повторення.
У програмуванні рекурсія — це ситуація, коли функція викликає сама себе.
1. Як це працює?
Замість того щоб використовувати цикл for або while для повторення дій, ми можемо змусити функцію запускати свою копію з новими даними.
Але тут є ризик: якщо функція постійно викликатиме сама себе, це ніколи не закінчиться (як у дзеркалах), і програма "впаде" з помилкою.
Дві обов'язкові частини правильної рекурсії:
- Базовий випадок (Base Case): Умова зупинки. "Якщо ми дійшли до кінця — просто поверни результат і не викликай функцію знову".
- Рекурсивний крок (Recursive Step): Дія, яка наближає нас до базового випадку.
2. Класичний приклад: Факторіал
Факторіал числа N (позначається N!) — це добуток усіх чисел від 1 до N.
Приклад: 5! = 5 × 4 × 3 × 2 × 1 = 120
Ми можемо сказати інакше:
- 5! = 5 × 4!
- 4! = 4 × 3! ...і так далі, поки не дійдемо до 1.
Код на С++:
#include <iostream>
using namespace std;
int factorial(int n) {
// 1. Базовий випадок (Умова зупинки)
if (n <= 1) {
return 1; // Факторіал 1 дорівнює 1. Стоп.
}
// 2. Рекурсивний крок
// Ми множимо n на результат цієї ж функції для (n-1)
return n * factorial(n - 1);
}
int main() {
cout << "Факторіал 5: " << factorial(5) << endl;
return 0;
}
3. Що відбувається "під капотом"? (Call Stack)
Коли код виконується, комп'ютер будує в пам'яті "вежу" з функцій.
Будуємо вежу (Виклики функцій):
- Викликається
factorial(5). Він чекає результат5 * factorial(4). - Викликається
factorial(4). Він чекає результат4 * factorial(3). - Викликається
factorial(3). Він чекає результат3 * factorial(2). - Викликається
factorial(2). Він чекає результат2 * factorial(1). - Викликається
factorial(1). Це "База!" Він одразу повертає1.
Розбираємо вежу (Обчислення у зворотному порядку):
factorial(2)отримує1(відfactorial(1)), рахує2 * 1 = 2, повертає2.factorial(3)отримує2(відfactorial(2)), рахує3 * 2 = 6, повертає6.factorial(4)отримує6(відfactorial(3)), рахує4 * 6 = 24, повертає24.factorial(5)отримує24(відfactorial(4)), рахує5 * 24 = 120. Фініш.
4. Небезпека: Stack Overflow
Якщо ви забудете написати умову зупинки (базовий випадок) або напишете її неправильно, функція буде викликати сама себе нескінченно.
Приклад нескінченної рекурсії: factorial(5) → factorial(5) → factorial(5)...
Пам'ять комп'ютера не гумова. Кожен виклик функції займає трохи місця в ділянці пам'яті, яка називається Стек.
Коли це місце закінчується, виникає помилка Stack Overflow (Переповнення стека), і програма аварійно закривається.
Приклад "поганої" рекурсії (не запускайте це!):
#include <iostream>
using namespace std;
// Приклад "поганої" рекурсії (не запускайте це!)
void infinite() {
cout << "Help!";
infinite(); // Немає умови виходу!
}
int main() {
infinite(); // Програма зависне або впаде!
return 0;
}
5. Приклад 2: Числа Фібоначчі
Це послідовність, де кожне число — це сума двох попередніх: 0, 1, 1, 2, 3, 5, 8, 13, 21...
Рекурсивна формула: F(n) = F(n - 1) + F(n - 2)
База: F(0) = 0, F(1) = 1.
Код на С++:
#include <iostream>
using namespace std;
int fibonacci(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
// Функція викликає сама себе ДВІЧІ
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main() {
cout << "F(7) = " << fibonacci(7) << endl; // Виведе 13
return 0;
}
6. Рекурсія проти Циклів
Будь-яку рекурсію можна переписати через цикли (for, while).
| Характеристика | Рекурсія | Цикли |
|---|---|---|
| Код | Короткий, елегантний | Часто довший |
| Пам'ять | Витрачає багато (на кожен виклик) | Економний |
| Швидкість | Повільніше (через накладні витрати) | Швидше |
| Використання | Складні алгоритми (дерева, графи, сортування) | Прості задачі, лінійні обчислення |
Практичні завдання до Лекції 11
Виконайте ці завдання в своєму середовищі розробки.
Завдання 1: Піднесення до степеня
Напишіть рекурсивну функцію power(base, n), яка обчислює число base у степені n.
Формула: x^n = x × x^(n-1)
Базовий випадок: будь-яке число в степені 0 дорівнює 1.
Приклад: power(2, 3) має повернути 8.
Завдання 2: Сума цифр числа
Напишіть рекурсивну функцію, яка приймає ціле число (наприклад, 1234) і повертає суму його цифр (1+2+3+4 = 10).
Підказка:
- Остання цифра — це
n % 10. - Решта числа — це
n / 10.
Базовий випадок: якщо число менше 10, просто повернути його.
Завдання 3: Зворотний відлік
Напишіть рекурсивну функцію void countdown(int n), яка друкує числа від n до 1, а в кінці пише "Start!".
Приклад виклику: countdown(3)
Очікуваний вивід:
3 2 1 Start!