У меня есть эта хвостовая рекурсивная функция здесь:
def recursive_function(n, sum):
if n < 1:
return sum
else:
return recursive_function(n-1, sum+n)
c = 998
print(recursive_function(c, 0))
Это работает до n=997
, тогда это просто ломается и выплевывает RecursionError: maximum recursion depth exceeded in comparison
. Это просто переполнение стека? Есть ли способ обойти это?
line <n>, in <module>
трассировки в стеке), и для этого кода требуется 2 кадра стекаn=1
(потому что базовый случай естьn < 1
, поэтомуn=1
он все еще рекурсивен). И я предполагаю, что предел рекурсии не является включающим, как в «ошибка, когда вы нажмете 1000», а не «ошибка, если вы превысите 1000 (1001)».997 + 2
меньше 1000, поэтому работает998 + 2
не потому, что достигает предела.recursive_function(997)
работает, это ломает в998
. Когда вы звоните,recursive_function(998)
он использует 999 стековых фреймов, и интерпретатор добавляет 1 фрейм (потому что ваш код всегда выполняется так, как если бы он был частью модуля верхнего уровня), что заставляет его достигать предела 1000.Ответы:
Да, это защита от переполнения стека. Python (точнее, реализация CPython) не оптимизирует хвостовую рекурсию, а необузданная рекурсия вызывает переполнение стека. Вы можете проверить предел рекурсии с помощью
sys.getrecursionlimit
и изменить предел рекурсии с помощьюsys.setrecursionlimit
, но это опасно - стандартный лимит немного консервативный, но стековые рамки Python могут быть довольно большими.Python не является функциональным языком, а хвостовая рекурсия не особенно эффективна. Переписывание алгоритма итеративно, если это возможно, обычно является лучшей идеей.
источник
sys
и вresource
модулях: stackoverflow.com/a/16248113/205521Похоже, вам просто нужно установить большую глубину рекурсии :
источник
Это чтобы избежать переполнения стека. Интерпретатор Python ограничивает глубину рекурсии, чтобы помочь вам избежать бесконечных рекурсий, что приводит к переполнению стека. Попробуйте увеличить предел рекурсии (
sys.setrecursionlimit
) или переписать свой код без рекурсии.Из документации Python :
источник
Если вам часто нужно изменить предел рекурсии (например, при решении задач программирования), вы можете определить простой менеджер контекста, например:
Затем для вызова функции с пользовательским ограничением вы можете сделать:
При выходе из тела
with
оператора предел рекурсии будет восстановлен до значения по умолчанию.источник
resource
. Без этого вы получите ошибку сегментации, и весь процесс Python завершится сбоем, если выsetrecursionlimit
слишком велики и попытаетесь использовать новый лимит (около 8 мегабайт стековых фреймов, что означает ~ 30 000 стековых фреймов с простой функцией, указанной выше, на мой ноутбук).Используйте язык, который гарантирует оптимизацию хвостового вызова. Или используйте итерацию. В качестве альтернативы, будьте милыми с декораторами .
источник
ulimit -s
стековых фреймов, да, это stackoverflow.com/a/50120316resource.setrlimit
также необходимо использовать для увеличения размера стека и предотвращения segfaultЯдро Linux ограничивает стек процессов .
Python хранит локальные переменные в стеке интерпретатора, поэтому рекурсия занимает место в стеке интерпретатора.
Если интерпретатор Python пытается превысить ограничение стека, ядро Linux делает это из-за ошибки сегментации.
Предельный размер стека контролируется с
getrlimit
иsetrlimit
системных вызовов.Python предлагает доступ к этим системным вызовам через
resource
модуль.Конечно, если вы продолжите увеличивать ulimit, ваша ОЗУ будет исчерпана, что либо замедлит работу вашего компьютера из-за безумия свопинга, либо убьет Python через OOM Killer.
Из bash вы можете увидеть и установить ограничение стека (в килобайтах) с помощью:
Значение по умолчанию для меня 8Mb.
Смотрите также:
Протестировано на Ubuntu 16.10, Python 2.7.12.
источник
rlimit_stack
после исправлений Stack Clash может привести к сбою или связанным с этим проблемам. Также смотрите Red Hat Issue 1463241Я понимаю, что это старый вопрос, но для тех, кто читает, я бы рекомендовал не использовать рекурсию для таких проблем - списки намного быстрее и полностью избегают рекурсии. Я бы реализовал это как:
(Используйте n + 1 в xrange, если вы начинаете считать вашу последовательность Фибоначчи от 0 вместо 1.)
источник
xrange
называется простоrange
, в Python 3.Конечно, числа Фибоначчи можно вычислить в O (n), применяя формулу Бине:
Как отмечают комментаторы, это не O (1), а O (n) из-за
2**n
. Разница также в том, что вы получаете только одно значение, в то время как с помощью рекурсии вы получаете все значенияFibonacci(n)
вплоть до этого значения.источник
n
из-за неточности с плавающей запятой - разница между(1+sqrt(5))**n
и(1+sqrt(5))**(n+1)
становится меньше, чем 1 ulp, поэтому вы начинаете получать неправильные результаты.(1+sqrt(5))**n
и ,((1+sqrt(5))**n)+1
что становится меньше 1 ULP! (небольшая опечатка) Кроме того, {@} сначала это не O (1)! Расчет2**n
занимает не менее O (n) времени.2**n
эффективно O (log (n)) с использованием Exponentiattion путем возведения в квадрат .У меня была похожая проблема с ошибкой «Максимальная глубина рекурсии превышена». Я обнаружил, что ошибка была вызвана поврежденным файлом в каталоге, с которым я зацикливался
os.walk
. Если у вас возникли проблемы с решением этой проблемы, и вы работаете с путями к файлам, обязательно сузьте их, так как это может быть поврежденный файл.источник
Если вы хотите получить только несколько чисел Фибоначчи, вы можете использовать матричный метод.
Это быстро, поскольку NumPy использует быстрый алгоритм возведения в степень. Вы получите ответ в O (войдите n). И это лучше, чем формула Бине, потому что она использует только целые числа. Но если вы хотите, чтобы все числа Фибоначчи были до n, то лучше сделать это запоминанием.
источник
Использовать генераторы?
вышеуказанная функция fib () адаптирована из: http://intermediatepythonista.com/python-generators
источник
[fibs().next() for ...]
каждый раз создается новый генератор.Как и @alex предложил , вы можете использовать функцию генератора, чтобы сделать это последовательно, а не рекурсивно.
Вот эквивалент кода в вашем вопросе:
источник
Многие рекомендуют, чтобы увеличение предела рекурсии было хорошим решением, но не потому, что оно всегда будет. Вместо этого используйте итеративное решение.
источник
Я хотел бы привести пример использования запоминания для вычисления Фибоначчи, поскольку это позволит вам вычислять значительно большие числа с помощью рекурсии:
Это все еще рекурсивно, но использует простую хеш-таблицу, которая позволяет повторно использовать ранее вычисленные числа Фибоначчи, а не делать их снова.
источник
источник
Мы можем сделать это, используя
@lru_cache
декоратор иsetrecursionlimit()
метод:Вывод
Источник
functools lru_cache
источник
Мы также могли бы использовать вариант динамического программирования снизу вверх
источник