Какова максимальная глубина рекурсии в Python и как ее увеличить?

У меня есть эта рекурсивная функция здесь:

def fib(n, sum):
    if n < 1:
        return sum
    else:
        return fib(n-1, sum+n)

c = 998
print(fib(c, 0))

Он работает до n = 997, затем он просто ломается и плюет "максимальная глубина рекурсии, превышаемая в сравнении" RuntimeError. Это просто переполнение стека? Есть ли способ обойти это?

Ответ 1

Да, это защита от. Python (точнее, реализация CPython) не оптимизирует хвостовую рекурсию, а необузданная рекурсия вызывает переполнение стека. Вы можете проверить лимит рекурсии с помощью sys.getrecursionlimit и изменить лимит рекурсии с помощью sys.setrecursionlimit, но это опасно - стандартный лимит немного консервативен, но стековые рамки Python может быть довольно большим.

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

Ответ 2

Похоже, вам просто нужно установить более высокую глубину рекурсии

sys.setrecursionlimit(1500)

Ответ 3

Чтобы избежать. Интерпретатор Python ограничивает глубину рекурсии, чтобы помочь вам избежать бесконечных рекурсий, что приводит к переполнению стека. Попробуйте увеличить предел рекурсии (sys.setrecursionlimit) или переписать код без рекурсии.

из веб-сайт python:

sys.getrecursionlimit()

Возвращает текущее значение предела рекурсии, максимальную глубину стека интерпретатора Python. Этот предел предотвращает бесконечную рекурсию из-за C и сбой Python. Он может быть установлен setrecursionlimit().

Ответ 4

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

Ответ 5

Если вам часто нужно изменить предел рекурсии (например, при решении задач программирования), вы можете определить простой менеджер контекста, например:

import sys

class recursionlimit:
    def __init__(self, limit):
        self.limit = limit
        self.old_limit = sys.getrecursionlimit()

    def __enter__(self):
        sys.setrecursionlimit(self.limit)

    def __exit__(self, type, value, tb):
        sys.setrecursionlimit(self.old_limit)

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

with recursionlimit(1500):
    print(fib(1000, 0))

При выходе из тела оператора with предел рекурсии будет восстановлен до значения по умолчанию.

Ответ 6

resource.setrlimit также должен использоваться для увеличения размера стека и предотвращения segfault

Ядро Linux ограничивает стек процессов.

Python хранит локальные переменные в стеке интерпретатора, поэтому рекурсия занимает место в стеке интерпретатора.

Если интерпретатор Python пытается превысить ограничение стека, ядро Linux вызывает ошибку сегментации.

Размер предела стека контролируется с помощью системных вызовов getrlimit и setrlimit.

Python предлагает доступ к этим системным вызовам через модуль resource.

import resource
import sys

print resource.getrlimit(resource.RLIMIT_STACK)
print sys.getrecursionlimit()
print

# Will segfault without this line.
resource.setrlimit(resource.RLIMIT_STACK, [0x10000000, resource.RLIM_INFINITY])
sys.setrecursionlimit(0x100000)

def f(i):
    print i
    sys.stdout.flush()
    f(i + 1)
f(0)

Конечно, если вы продолжите увеличивать ulimit, ваша ОЗУ будет исчерпана, что приведет к замедлению работы компьютера из-за безумия подкачки или к уничтожению Python через OOM Killer.

Из bash вы можете увидеть и установить ограничение стека (в килобайтах) с помощью:

ulimit -s
ulimit -s 10000

Значение по умолчанию для меня составляет 8 МБ.

Смотрите также:

Протестировано на Ubuntu 16.10, Python 2.7.12.

Ответ 7

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

def fibonacci(n):
    f = [0,1,1]
    for i in xrange(3,n):
        f.append(f[i-1] + f[i-2])
    return 'The %.0fth fibonacci number is: %.0f' % (n,f[-1])

(Используйте n + 1 в xrange, если вы начинаете считать свою последовательность фибоначчи от 0 вместо 1.)

Ответ 8

Конечно, числа Фибоначчи можно вычислить в O (n), применяя формулу Бине:

from math import floor, sqrt

def fib(n):                                                     
    return int(floor(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5))+0.5))

Как отмечают комментаторы, это не O (1), а O (n) из-за 2**n. Разница также в том, что вы получаете только одно значение, в то время как с помощью рекурсии вы получаете все значения Fibonacci(n) вплоть до этого значения.

Ответ 9

У меня была похожая проблема с ошибкой "Максимальная глубина рекурсии превышена". Я обнаружил, что ошибка вызывается поврежденным файлом в каталоге, который я зацикливал на os.walk. Если у вас возникли проблемы с решением этой проблемы и вы работаете с путями к файлам, обязательно сузьте их, поскольку это может быть поврежденный файл.

Ответ 10

Использовать генераторы?

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fibs = fib() #seems to be the only way to get the following line to work is to
             #assign the infinite generator to a variable

f = [fibs.next() for x in xrange(1001)]

for num in f:
        print num

выше функция fib(), адаптированная из: http://intermediatepythonista.com/python-generators

Ответ 11

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

from numpy import matrix

def fib(n):
    return (matrix('0 1; 1 1', dtype='object') ** n).item(1)

Это быстро, как NumPy использует быстрый алгоритм возведения в степень. Вы получите ответ в O (войдите n). И это лучше, чем формула Бине, потому что она использует только целые числа. Но если вы хотите, чтобы все числа Фибоначчи были до n, то лучше сделать это запоминанием.

Ответ 12

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

def fib(n):
    a,b = 1,1
    for i in range(n-1):
        a,b = b,a+b
    return a
print fib(5)

Ответ 13

Как @alex предложил, вы можете использовать функцию генератора для этого. Здесь эквивалент кода в вашем вопросе:

def fib(n):
    def fibseq(n):
        """ Iteratively return the first n Fibonacci numbers, starting from 0 """
        a, b = 0, 1
        for _ in xrange(n):
            yield a
            a, b = b, a + b

    return sum(v for v in fibseq(n))

print format(fib(100000), ',d')  # -> no recursion depth error

Ответ 14

Я хотел бы привести пример использования запоминания для вычисления Фибоначчи, поскольку это позволит вам вычислять значительно большие числа с помощью рекурсии:

cache = {}
def fib_dp(n):
    if n in cache:
        return cache[n]
    if n == 0: return 0
    elif n == 1: return 1
    else:
        value = fib_dp(n-1) + fib_dp(n-2)
    cache[n] = value
    return value

print(fib_dp(998))

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

Ответ 15

import sys
sys.setrecursionlimit(1500)

def fib(n, sum):
    if n < 1:
        return sum
    else:
        return fib(n-1, sum+n)

c = 998
print(fib(c, 0))