← Повернутися до лекцій

Лекція 11: Рекурсивні функції

Уявіть, що ви стоїте між двома дзеркалами. Ви бачите своє відображення, в якому є ще одне відображення, в якому є ще одне... Це нескінченний процес повторення.

У програмуванні рекурсія — це ситуація, коли функція викликає сама себе.

1. Як це працює?

Замість того щоб використовувати цикл for або while для повторення дій, ми можемо змусити функцію запускати свою копію з новими даними.

Але тут є ризик: якщо функція постійно викликатиме сама себе, це ніколи не закінчиться (як у дзеркалах), і програма "впаде" з помилкою.

Дві обов'язкові частини правильної рекурсії:

  1. Базовий випадок (Base Case): Умова зупинки. "Якщо ми дійшли до кінця — просто поверни результат і не викликай функцію знову".
  2. Рекурсивний крок (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)

Коли код виконується, комп'ютер будує в пам'яті "вежу" з функцій.

Будуємо вежу (Виклики функцій):

  1. Викликається factorial(5). Він чекає результат 5 * factorial(4).
  2. Викликається factorial(4). Він чекає результат 4 * factorial(3).
  3. Викликається factorial(3). Він чекає результат 3 * factorial(2).
  4. Викликається factorial(2). Він чекає результат 2 * factorial(1).
  5. Викликається 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!