Понимание рекурсивных функций с помощью Python

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

Вступление

Когда мы думаем о повторении задачи, мы обычно думаем о циклах for и while Эти конструкции позволяют нам выполнять итерацию по списку, коллекции и т. Д.

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

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

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

Что такое рекурсия?

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

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

Давайте посмотрим на небольшой пример, чтобы продемонстрировать оба компонента:

 # Assume that remaining is a positive integer 
 def hi_recursive(remaining): 
 # The base case 
 if remaining == 0: 
 return 
 print('hi') 
 
 # Call to function, with a reduced remaining count 
 hi_recursive(remaining - 1) 

Базовый случай для нас - если remaining переменная равна 0 то есть сколько оставшихся строк «привет» мы должны напечатать. Функция просто возвращается.

После оператора печати мы hi_recursive но с уменьшенным оставшимся значением. Это важно! Если мы не уменьшим remaining значение, функция будет работать бесконечно. Обычно, когда рекурсивная функция вызывает сама себя, параметры меняются, чтобы быть ближе к базовому случаю.

Давайте визуализируем, как это работает, когда мы вызываем hi_recursive(3) :

hi_recursiveпример{.ezlazyload}

После того, как функция напечатает "привет", она вызывает себя с меньшим значением, чтобы remaining пока не достигнет 0 . При нуле функция возвращается туда, где она была вызвана в hi_recursive(1) , которая возвращается туда, где она была вызвана в hi_recursive(2) и в конечном итоге возвращается туда, где она была вызвана в hi_recursive(3) .

Почему бы не использовать петлю?

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

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

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

Примеры

Лучший способ освоить рекурсию или любую концепцию программирования - это попрактиковаться. Создать рекурсивные функции просто: обязательно включите базовый случай и вызовите функцию так, чтобы она приближалась к базовому.

Сумма списка

Python включает функцию sum для списков. Реализация Python по умолчанию, CPython, использует неопределенный цикл for в C для создания этих функций (исходный код здесь для тех, кто заинтересован). Посмотрим, как это сделать с помощью рекурсии:

 def sum_recursive(nums): 
 if len(nums) == 0: 
 return 0 
 
 last_num = nums.pop() 
 return last_num + sum_recursive(nums) 

Базовый случай - пустой список - лучшая sum для этого - 0 . Как только мы обработаем наш базовый случай, мы удаляем последний элемент списка. Наконец, мы вызываем sum_recursive с сокращенным списком и добавляем полученное число к общей сумме.

В интерпретаторе Python и sum([10, 5, 2]) и sum_recursive([10, 5, 2]) должны дать вам 17 .

Факториальные числа

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

 5! = 5 x 4 x 3 x 2 x 1 = 120 

Восклицательный знак обозначает факториал, и мы видим, что умножаем 5 на произведение всех целых чисел от 4 до 1 . Что, если кто-то введет 0 ? Широко известно и доказано, что 0! = 1 . Теперь давайте создадим функцию, как показано ниже:

 def factorial(n): 
 if n == 0 or n == 1: 
 return 1 
 return n * factorial(n - 1) 

Мы учитываем случаи, когда 1 или 0 , а в противном случае мы умножаем текущее число на факториал числа, уменьшенного на 1 .

Простая проверка в интерпретаторе Python покажет, что factorial(5) дает 120 .

Последовательность Фибоначчи

Последовательность Фибоначчи - это последовательность, в которой каждое число является суммой следующих двух чисел. Эта последовательность предполагает, что числа Фибоначчи для 0 и 1 также равны 0 и 1. Таким образом, эквивалент Фибоначчи для 2 будет равен 1.

Посмотрим на последовательность и соответствующие ей натуральные числа:

 Integers: 0, 1, 2, 3, 4, 5, 6, 7 
 Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13 

Мы можем легко закодировать функцию на Python, чтобы определить эквивалент Фибоначчи для любого положительного целого числа, используя рекурсию:

 def fibonacci(n): 
 if n == 0: 
 return 0 
 if n == 1: 
 return 1 
 return fibonacci(n - 1) + fibonacci(n - 2) 

Вы можете убедиться, что он работает должным образом, проверив, что значение fibonacci(6) равно 8 .

Теперь я хотел бы, чтобы вы рассмотрели другую реализацию этой функции, в которой используется цикл for:

 def fibonacci_iterative(n): 
 if n <= 1: 
 return n 
 
 a = 0 
 b = 1 
 for i in range(n): 
 temp = a 
 a = b 
 b = b + temp 
 return a 

Если целое число меньше или равно 1, вернуть его. Теперь, когда наш базовый случай обработан. Мы постоянно добавляем первое число ко второму, сохраняя первое число во temp переменной перед его обновлением.

Вывод такой же, как и у первой функции fibonacci() . Эта версия быстрее, чем рекурсивная, поскольку реализации Python не оптимизированы для рекурсии, но превосходят императивное программирование. Однако решение не так легко читается, как наша первая попытка. В этом одна из самых сильных сторон рекурсии: элегантность . Некоторые программные решения наиболее естественно решаются с помощью рекурсии.

Заключение

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

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus