Основы рекурсии и её примеры

Рекурсия — это важный и мощный инструмент программирования, который заключается в том, что функция вызывает саму себя для решения некоторой задачи. Вместо того чтобы использовать итерации (циклы), рекурсивная функция решает задачу путем деления её на более простые версии самой задачи, пока не достигается базовый случай, при котором рекурсивные вызовы прекращаются.

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

Структура рекурсивной функции

Рекурсивная функция обычно имеет две ключевые составляющие:

  1. Базовый случай: Это условие, при котором рекурсивные вызовы прекращаются. Базовый случай гарантирует, что рекурсия не будет продолжаться бесконечно.
  2. Рекурсивный случай: Это условие, при котором функция вызывает саму себя с измененными параметрами для решения подзадачи.


Пример 1: Обратный отсчёт

Рассмотрим простую задачу — вывести числа от n до 1 с помощью рекурсии.

def countdown(n):
    if n <= 0:
        print("Отсчёт завершён!")
    else:
        print(n)
        countdown(n - 1)

# Пример вызова функции
countdown(5)

Что происходит?

  • Каждый раз функция уменьшает значение n на 1 и вызывает себя снова.
  • Когда n становится равным 0, функция выводит сообщение о завершении и завершает выполнение.


Пример 2: Вычисление суммы чисел

Задача: найти сумму всех чисел от 1 до n. Это можно сделать с помощью рекурсии.

def sum_numbers(n):
    if n == 1:
        return 1
    else:
        return n + sum_numbers(n - 1)

# Пример вызова функции
print(sum_numbers(5))  # Вывод: 15

Как это работает?

  • Каждый вызов функции складывает текущее число с результатом следующего рекурсивного вызова, пока не дойдёт до базового случая (n = 1).


Пример 3: Поиск минимального значения в списке

Задача: найти минимальное значение в списке чисел.

def find_min(lst):
    if len(lst) == 1:
        return lst[0]
    else:
        min_of_rest = find_min(lst[1:])
        return lst[0] if lst[0] < min_of_rest else min_of_rest

# Пример вызова функции
numbers = [3, 5, 1, 8, 2]
print(find_min(numbers))  # Вывод: 1

Что происходит?

  • Базовый случай — если в списке один элемент, он является минимальным.
  • Рекурсивный случай — на каждом шаге сравнивается первый элемент списка с минимальным значением остальной части списка.


Пример 4: Подсчёт длины строки

Рекурсия может быть использована даже для простых операций, таких как подсчёт длины строки.

def string_length(s):
    if s == '':
        return 0
    else:
        return 1 + string_length(s[1:])

# Пример вызова функции
print(string_length("Hello"))  # Вывод: 5

Как это работает?

  • Базовый случай — если строка пуста, её длина равна 0.
  • Рекурсивный случай — каждый раз строка сокращается на один символ, пока не станет пустой.


Пример 5: Рекурсивное вычисление факториала

Факториал числа nnn обозначается как n!n!n! и вычисляется как произведение всех целых чисел от 1 до nnn. Например:

5!=5×4×3×2×1=1205! = 5 \times 4 \times 3 \times 2 \times 1 = 1205!=5×4×3×2×1=120

Рекурсивная формулировка факториала:

  • Базовый случай: 0!=10! = 10!=1
  • Рекурсивный случай: n!=n×(n−1)!n! = n \times (n-1)!n!=n×(n−1)!
def factorial(n):
    # Базовый случай: факториал 0 или 1 равен 1
    if n == 0 или n == 1:
        return 1
    # Рекурсивный случай: n * факториал (n - 1)
    return n * factorial(n - 1)

# Пример вызова функции
print(factorial(5))  # Вывод: 120

 

Пример 6: Числа Фибоначчи

Последовательность Фибоначчи — это ряд чисел, где каждое число равно сумме двух предыдущих чисел. Последовательность начинается с 000 и 111.

0,1,1,2,3,5,8,13,21,…0, 1, 1, 2, 3, 5, 8, 13, 21, \dots0,1,1,2,3,5,8,13,21,…

Рекурсивная формулировка:

  • Базовый случай: F(0)=0F(0) = 0F(0)=0 и F(1)=1F(1) = 1F(1)=1
  • Рекурсивный случай: F(n)=F(n−1)+F(n−2)F(n) = F(n-1) + F(n-2)F(n)=F(n−1)+F(n−2)
def fibonacci(n):
    # Базовые случаи
    if n == 0:
        return 0
    if n == 1:
        return 1
    # Рекурсивный случай
    return fibonacci(n - 1) + fibonacci(n - 2)

# Пример вызова функции
print(fibonacci(6))  # Вывод: 8

Как работает этот код?

  1. Вызов fibonacci(6) начинает цепочку рекурсивных вызовов, которая продолжается до базовых случаев.
  2. Когда достигается базовый случай, вычисляются значения и возвращаются.
  3. Каждый вызов функции использует уже рассчитанные значения, чтобы получить результат.


Пример 7: Быстрая сортировка (QuickSort)

Быстрая сортировка — это популярный алгоритм сортировки, который также использует рекурсию.

def quicksort(arr):
    # Базовый случай: если список пустой или состоит из одного элемента, его не нужно сортировать
    if len(arr) <= 1:
        return arr
    else:
        pivot = arr[0]
        less_than_pivot = [x for x in arr[1:] if x <= pivot]
        greater_than_pivot = [x for x in arr[1:] if x > pivot]
        return quicksort(less_than_pivot) + [pivot] + quicksort(greater_than_pivot)

# Пример вызова функции
numbers = [3, 6, 8, 10, 1, 2, 1]
print(quicksort(numbers))  # Вывод: [1, 1, 2, 3, 6, 8, 10]

Как работает этот код?

  1. Функция выбирает элемент "опорный элемент" (pivot) и делит список на два подсписка: элементы, меньшие или равные опорному элементу, и элементы, большие опорного элемента.
  2. Каждый подсписок сортируется рекурсивно.
  3. Результатом является объединение отсортированных подсписков и опорного элемента.

 

Вот таблица, представляющая ключевые аспекты рекурсии без примеров:

РазделОписание
Основная идея рекурсииРазделение сложных задач на более простые подзадачи одного и того же типа до достижения простого базового случая.
Структура рекурсивной функцииВключает два ключевых компонента: 
1. Базовый случай — условие, при котором рекурсия прекращается. 
2. Рекурсивный случай — условие, при котором функция вызывает саму себя с измененными параметрами.
Базовый случайУсловие, при котором рекурсивные вызовы прекращаются. Гарантирует, что рекурсия не будет продолжаться бесконечно.
Рекурсивный случайУсловие, при котором функция вызывает саму себя с измененными параметрами для решения подзадачи.
Преимущества рекурсии- Простота кода: Рекурсивные решения часто проще и короче, чем итерационные. 
- Прозрачность: Рекурсия естественна для задач, которые можно решить путем деления на подзадачи того же типа.
Недостатки рекурсии- Потребление памяти: Рекурсия использует стек вызовов, что может привести к переполнению стека при глубокой рекурсии. 
- Производительность: В некоторых случаях рекурсия может быть менее эффективной по времени, чем итеративные решения, особенно при многократных вызовах одной и той же функции.

Эта таблица дает общее представление о рекурсии, ее структуре, преимуществах и недостатках.

 

Рекурсия — это мощный инструмент, который помогает решать задачи более элегантно и кратко. Использование рекурсии позволяет эффективно разбивать сложные задачи на более простые подзадачи, решая их с помощью вызовов функции самой себя.

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

Перейти к следующему шагу

Комментарии