Перейти к содержимому

Рекурсия в питоне что это

  • автор:

Что такое рекурсия и как она работает в языке Python

python машинное обучение курс, машинное обучение python курс, предобработка данных python, нейронные сети python, обработка естественного языка python, machine learning курсы, задача классификации python

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

Что представляет собой рекурсия в языке Python

Рекурсия — это описание (определение) какого-либо объекта или процесса внутри этого самого объекта или процесса (структуры). В Python механизм рекурсии очень распространен в функциях. Функциями называются участки кода, которые изолированы от остального кода программы. Их выполнение происходит только в том случае, если они вызываются. Все функции обладают одним общим свойством: они могут принимать параметры (ноль, один или несколько), а также возвращать значения (но это не всегда). Функцию, в которой используется механизм рекурсии, называют рекурсивной функцией. Отличительной особенностью рекурсивных функций является то, что в своей конструкции они содержат собственный вызов, то есть вызывают сами себя. Рекурсию также принято считать альтернативой несложным циклам и итерациям для того, чтобы повысить производительность программы путем более быстрого выполнения. Хотя также стоит отметить, что рекурсия не всегда применима в сложных циклических конструкциях (например, когда необходимо использовать вложенные циклы).

Как работает рекурсия: несколько практических примеров

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

def mysum (L) : if not L: return 0 else: return L[0] + mysum(L[1:])

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

mysum([1, 2, 3, 4, 5])##15

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

def mysum(L) : print (L) # Вывод уровней рекурсии if not L: # На каждом уровне L становится короче return 0 else: return L[0] + mysum(L[1:])

python машинное обучение курс, машинное обучение python курс, предобработка данных python, нейронные сети python, обработка естественного языка python, machine learning курсы, задача классификации python

Таким образом, благодаря поддержке механизма рекурсии, язык Python обеспечивает разработчика возможностью значительно повышать производительность приложений и экономии времени. Это делает Python универсальным и надежным средством для работы с производительными приложениями, а также с Big Data обработкой.

Рекурсия в питоне что это

Напомним, что в математике факториал числа n определяется как Например, Ясно, что факториал можно легко посчитать, воспользовавшись циклом for. Представим, что нам нужно в нашей программе вычислять факториал разных чисел несколько раз (или в разных местах кода). Конечно, можно написать вычисление факториала один раз, а затем используя Copy-Paste вставить его везде, где это будет нужно.

# вычислим 3! res = 1 for i in range(1, 4): res *= i print(res) # вычислим 5! res = 1 for i in range(1, 6): res *= i print(res)

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

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

Покажем, как написать функцию factorial(), которая принимает один параметр — число, и возвращает значение — факториал этого числа.

def factorial(n): res = 1 for i in range(1, n + 1): res *= i return res print(factorial(3)) print(factorial(5))

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

Далее идет тело функции, оформленное в виде блока, то есть с отступом. Внутри функции вычисляется значение факториала числа n и оно сохраняется в переменной res. Функция завершается инструкцией return res, которая завершает работу функции и возвращает значение переменной res.

Инструкция return может встречаться в произвольном месте функции, ее исполнение завершает работу функции и возвращает указанное значение в место вызова. Если функция не возвращает значения, то инструкция return используется без возвращаемого значения. В функциях, которым не нужно возвращать значения, инструкция return может отсутствовать.

Приведём ещё один пример. Напишем функцию max(), которая принимает два числа и возвращает максимальное из них (на самом деле, такая функция уже встроена в Питон).

10 20
def max(a, b): if a > b: return a else: return b print(max(3, 5)) print(max(5, 3)) print(max(int(input()), int(input())))

Теперь можно написать функцию max3(), которая принимает три числа и возвращает максимальное их них.

def max(a, b): if a > b: return a else: return b def max3(a, b, c): return max(max(a, b), c) print(max3(3, 5, 4))

Встроенная функция max() в Питоне может принимать переменное число аргументов и возвращать максимум из них. Приведём пример того, как такая функция может быть написана.

def max(*a): res = a[0] for val in a[1:]: if val > res: res = val return res print(max(3, 5, 4))

Все переданные в эту функцию параметры соберутся в один кортеж с именем a, на что указывает звёздочка в строке объявления функции.

2. Локальные и глобальные переменные

Внутри функции можно использовать переменные, объявленные вне этой функции

def f(): print(a) a = 1 f()

Здесь переменной a присваивается значение 1, и функция f() печатает это значение, несмотря на то, что до объявления функции f эта переменная не инициализируется. В момент вызова функции f() переменной a уже присвоено значение, поэтому функция f() может вывести его на экран.

Такие переменные (объявленные вне функции, но доступные внутри функции) называются глобальными.

Но если инициализировать какую-то переменную внутри функции, использовать эту переменную вне функции не удастся. Например:

def f(): a = 1 f() print(a)

Получим ошибку NameError: name ‘a’ is not defined . Такие переменные, объявленные внутри функции, называются локальными. Эти переменные становятся недоступными после выхода из функции.

Интересным получится результат, если попробовать изменить значение глобальной переменной внутри функции:

def f(): a = 1 print(a) a = 0 f() print(a)

Будут выведены числа 1 и 0. Несмотря на то, что значение переменной a изменилось внутри функции, вне функции оно осталось прежним! Это сделано в целях “защиты” глобальных переменных от случайного изменения из функции. Например, если функция будет вызвана из цикла по переменной i , а в этой функции будет использована переменная i также для организации цикла, то эти переменные должны быть различными. Если вы не поняли последнее предложение, то посмотрите на следующий код и подумайте, как бы он работал, если бы внутри функции изменялась переменная i.

def factorial(n): res = 1 for i in range(1, n + 1): res *= i return res for i in range(1, 6): print(i, '! = ', factorial(i), sep='')

Если бы глобальная переменная i изменялась внутри функции, то мы бы получили вот что:

5! = 1 5! = 2 5! = 6 5! = 24 5! = 120

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

Более формально: интерпретатор Питон считает переменную локальной для данной функции, если в её коде есть хотя бы одна инструкция, модифицирующая значение переменной, то эта переменная считается локальной и не может быть использована до инициализации. Инструкция, модифицирующая значение переменной — это операторы = , += , а также использование переменной в качестве параметра цикла for . При этом даже если инструкция, модицифицирующая переменную никогда не будет выполнена, интерпретатор это проверить не может, и переменная все равно считается локальной. Пример:

def f(): print(a) if False: a = 0 a = 1 f()

Возникает ошибка: UnboundLocalError: local variable ‘a’ referenced before assignment . А именно, в функции f() идентификатор a становится локальной переменной, т.к. в функции есть команда, модифицирующая переменную a , пусть даже никогда и не выполняющийся (но интерпретатор не может это отследить). Поэтому вывод переменной a приводит к обращению к неинициализированной локальной переменной.

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

def f(): global a a = 1 print(a) a = 0 f() print(a)

В этом примере на экран будет выведено 1 1, так как переменная a объявлена, как глобальная, и ее изменение внутри функции приводит к тому, что и вне функции переменная будет доступна.

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

Например, пусть ваша программа должна посчитать факториал вводимого числа, который вы потом захотите сохранить в переменной f. Вот как это не стоит делать:

def factorial(n): global f res = 1 for i in range(2, n + 1): res *= i f = res n = int(input()) factorial(n) # дальше всякие действия с переменной f

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

Гораздо лучше переписать этот пример так:

# начало куска кода, который можно копировать из программы в программу def factorial(n): res = 1 for i in range(2, n + 1): res *= i return res # конец куска кода n = int(input()) f = factorial(n) # дальше всякие действия с переменной f

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

return [a, b]

Тогда результат вызова функции можно будет использовать во множественном присваивании:

Рекурсивная функция в python

Рекурсию не очень просто понять при первом знакомстве, но без ее понимания в разработке будет тяжело. В этом материале рассмотрим:

  • Рекурсивную функцию поиска факториала.
  • Как рекурсивные функции работают в коде.
  • Действительно ли рекурсивные функции выполняют свои задачи лучше итеративных?

Рекурсивные функции

Рекурсивная функция — это та, которая вызывает сама себя.

В качестве простейшего примера рассмотрите следующий код:

 
def factorial_recursive(n):
if n == 1:
return n
else:
return n*factorial_recursive(n-1)

Вызывая рекурсивную функцию здесь и передавая ей целое число, вы получаете факториал этого числа (n!).

Вкратце о факториалах

Факториал числа — это число, умноженное на каждое предыдущее число вплоть до 1.

Например, факториал числа 7:
7! = 7*6*5*4*3*2*1 = 5040

Вывести факториал числа можно с помощью функции:

 
num = 3
print(f"Факториал это ")

Эта функция выведет: «Факториал 3 это 6». Еще раз рассмотрим эту рекурсивную функцию:

def factorial_recursive(n): . 

По аналогии с обычной функцией имя рекурсивной указывается после def , а в скобках обозначается параметр n :

def factorial_recursive(n): if n == 1: return n else: return n*factorial_recursive(n-1)

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

def factorial_recursive(n): if n == 1: return n else: return n*factorial_recursive(n-1)

В коде выше выделен фрагмент самой рекурсии. В блоке else условной конструкции возвращается произведение n и значения этой же функции с параметром n-1 .

Это и есть рекурсия. В нашем примере это так сработало:

3 * (3-1) * ((3-1)-1) # так как 3-1-1 равно 1, рекурсия остановилась

Детали работы рекурсивной функции

Чтобы еще лучше понять, как это работает, разобьем на этапы процесс выполнения функции с параметром 3.

Для этого ниже представим каждый экземпляр с реальными числами. Это поможет «отследить», что происходит при вызове одной функции со значением 3 в качестве аргумента:

 
# Первый вызов
factorial_recursive(3):
if 3 == 1:
return 3
else:
return 3*factorial_recursive(3-1)

# Второй вызов
factorial_recursive(2):
if 2 == 1:
return 2
else:
return 2*factorial_recursive(2-1)

# Третий вызов
factorial_recursive(1):
if 1 == 1:
return 1
else:
return 1*factorial_recursive(1-1)

Рекурсивная функция не знает ответа для выражения 3*factorial_recursive(3–1) , поэтому она добавляет в стек еще один вызов.

Как работает рекурсия

/\ factorial_recursive(1) - последний вызов || factorial_recursive(2) - второй вызов || factorial_recursive(3) - первый вызов

Выше показывается, как генерируется стек. Это происходит благодаря процессу LIFO (last in, first out, «последним пришел — первым ушел»). Как вы помните, первые вызовы функции не знают ответа, поэтому они добавляются в стек.

Но как только в стек добавляется вызов factorial_recursive(1) , для которого ответ имеется, стек начинает «разворачиваться» в обратном порядке, выполняя все вычисления с реальными значениями. В процессе каждый из слоев выпадает в процессе.

  • factorial_recursive(1) завершается, отправляет 1 в
  • factorial_recursive(2) и выпадает из стека.
  • factorial_recursive(2) завершается, отправляет 2*1 в
  • factorial_recursive(3) и выпадает из стека. Наконец, инструкция else здесь завершается, возвращается 3 * 2 = 6, и из стека выпадает последний слой.

Рекурсия в Python имеет ограничение в 3000 слоев.

 
>>> import sys
>>> sys.getrecursionlimit()
3000

Рекурсивно или итеративно?

Каковы же преимущества рекурсивных функций? Можно ли с помощью итеративных получить тот же результат? Когда лучше использовать одни, а когда — другие?

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

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

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

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

 
def factorial_iterative(num):
factorial = 1
if num < 0:
print("Факториал не вычисляется для отрицательных чисел")
else:
for i in range (1, num + 1):
factorial = factorial*i
print(f"Факториал это ")

Что такое рекурсия в Python и как ее использовать

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

Принцип работы рекурсии

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

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

Факториал числа n ( n! ) — это произведение всех натуральных чисел от 1 до n . Например, 5! = 5 * 4 * 3 * 2 * 1 = 120 . Факториал можно выразить через рекурсию следующим образом:

  1. Базовый случай: если n равно 1, то n! = 1
  2. Рекурсивный случай: если n больше 1, то n! = n * (n-1)!
def factorial(n): if n == 1: return 1 else: return n * factorial(n-1)

Python-разработчик: новая работа через 9 месяцев
Получится, даже если у вас нет опыта в IT

Проблемы с рекурсией и их решение

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

Для решения этой проблемы можно использовать технику «мемоизации» — сохранение результатов рекурсивных вызовов в словаре и повторное использование их при необходимости. Это позволяет избежать повторных вычислений и ускорить выполнение рекурсивных функций.

def memoize(f): cache = <> def memoized_function(*args): if args not in cache: cache[args] = f(*args) return cache[args] return memoized_function @memoize def factorial(n): if n == 1: return 1 else: return n * factorial(n-1)

Когда использовать рекурсию

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

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

�� Теперь вы знаете основы рекурсии в Python и как ее использовать. Не забывайте про базовые и рекурсивные случаи, а также об оптимизации с помощью мемоизации при необходимости. Удачного программирования!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *