Eu tenho essa função recursiva da cauda aqui:
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))
Funciona até n=997
, então apenas quebra e cospe a RecursionError: maximum recursion depth exceeded in comparison
. Isso é apenas um estouro de pilha? Existe uma maneira de contornar isso?
line <n>, in <module>
rastreamentos na pilha) e esse código leva 2 quadros de pilha paran=1
(porque o caso base én < 1
, portanton=1
ainda é recorrente). E acho que o limite de recursão não é inclusivo, como no erro "erro ao atingir 1000" não "se você exceder 1000 (1001)".997 + 2
é menor que 1000, portanto,998 + 2
não funciona porque atinge o limite.recursive_function(997)
funciona, quebra em998
. Quando você chama,recursive_function(998)
ele usa 999 quadros de pilha e 1 quadro é adicionado pelo intérprete (porque seu código é sempre executado como se fosse parte do módulo de nível superior), o que faz com que atinja o limite de 1000.Respostas:
É uma proteção contra um estouro de pilha, sim. Python (ou melhor, a implementação do CPython) não otimiza a recursão de cauda, e a recursão desenfreada causa estouros de pilha. Você pode verificar o limite de recursão
sys.getrecursionlimit
e alterar o limite de recursão comsys.setrecursionlimit
, mas isso é perigoso - o limite padrão é um pouco conservador, mas os quadros de pilha Python podem ser bastante grandes.Python não é uma linguagem funcional e a recursão da cauda não é uma técnica particularmente eficiente. Reescrever o algoritmo iterativamente, se possível, geralmente é uma idéia melhor.
fonte
sys
e osresource
módulos: stackoverflow.com/a/16248113/205521Parece que você só precisa definir uma profundidade de recursão mais alta :
fonte
É para evitar um estouro de pilha. O intérprete Python limita a profundidade da recursão para ajudar a evitar recursões infinitas, resultando em estouros de pilha. Tente aumentar o limite de recursão (
sys.setrecursionlimit
) ou reescreva seu código sem recursão.Na documentação do Python :
fonte
Se você precisar alterar frequentemente o limite de recursão (por exemplo, ao resolver quebra-cabeças de programação), poderá definir um gerenciador de contexto simples como este:
Em seguida, para chamar uma função com um limite personalizado, você pode:
Ao sair do corpo da
with
instrução, o limite de recursão será restaurado para o valor padrão.fonte
resource
. Sem ele, você receberá uma falha de segmentação e todo o processo Python falhará se você forsetrecursionlimit
alto demais e tentar usar o novo limite (cerca de 8 megabytes de quadros de pilha, que se traduz em ~ 30.000 quadros de pilha com a função simples acima, em meu notebook).Use uma linguagem que garanta otimização de chamada de cauda. Ou use a iteração. Como alternativa, fique fofo com os decoradores .
fonte
ulimit -s
de quadros de pilha, sim, é stackoverflow.com/a/50120316resource.setrlimit
também deve ser usado para aumentar o tamanho da pilha e evitar segfaultO kernel do Linux limita a pilha de processos .
O Python armazena variáveis locais na pilha do intérprete e, portanto, a recursão ocupa o espaço da pilha do intérprete.
Se o intérprete Python tentar ultrapassar o limite da pilha, o kernel do Linux fará uma falha na segmentação.
O tamanho do limite da pilha é controlado com as chamadas
getrlimit
e dosetrlimit
sistema.O Python oferece acesso a essas chamadas do sistema através do
resource
módulo.Obviamente, se você continuar aumentando o ulimit, sua RAM acabará, o que reduzirá a velocidade do computador devido a uma loucura de troca ou matará o Python pelo OOM Killer.
No bash, você pode ver e definir o limite da pilha (em kb) com:
O valor padrão para mim é 8Mb.
Veja também:
Testado no Ubuntu 16.10, Python 2.7.12.
fonte
rlimit_stack
após as correções do Stack Clash pode resultar em falha ou problemas relacionados. Veja também a Edição 1463241 daSei que essa é uma pergunta antiga, mas para aqueles que lêem, eu recomendaria não usar a recursão para problemas como este - as listas são muito mais rápidas e evitam a recursão por completo. Eu implementaria isso como:
(Use n + 1 em xrange se começar a contar sua sequência de fibonacci de 0 em vez de 1.)
fonte
xrange
é chamado simplesmenterange
, em Python 3.Obviamente, os números de Fibonacci podem ser calculados em O (n) aplicando a fórmula Binet:
Como observam os comentaristas, não é O (1), mas O (n) por causa de
2**n
. A diferença também é que você obtém apenas um valor, enquanto que com a recursão você obtém todos os valoresFibonacci(n)
até esse valor.fonte
n
causa da imprecisão do ponto flutuante - a diferença entre(1+sqrt(5))**n
e(1+sqrt(5))**(n+1)
se torna menor que 1 ulp, para que você comece a obter resultados incorretos.(1+sqrt(5))**n
e((1+sqrt(5))**n)+1
isso se torna menor que 1 ulp! (erro de digitação pequeno) Além disso, {@} rwst Isso não é O (1)! O cálculo2**n
leva pelo menos O (n) tempo.2**n
é efetivamente O (log (n)) usando a exponenciação ao quadrado .Eu tive um problema semelhante com o erro "Profundidade máxima de recursão excedida". Descobri que o erro estava sendo acionado por um arquivo corrompido no diretório em que eu estava repetindo
os.walk
. Se você tiver problemas para resolver esse problema e estiver trabalhando com caminhos de arquivo, reduza-o, pois pode ser um arquivo corrompido.fonte
Se você deseja obter apenas alguns números de Fibonacci, pode usar o método de matriz.
É rápido como numpy usa o algoritmo de exponenciação rápida. Você obtém resposta em O (log n). E é melhor que a fórmula de Binet, porque usa apenas números inteiros. Mas se você deseja que todos os números de Fibonacci sejam n, é melhor fazê-lo por memorização.
fonte
Use geradores?
função fib () acima adaptada de: http://intermediatepythonista.com/python-generators
fonte
[fibs().next() for ...]
criaria um novo gerador a cada vez.Como o @alex sugeriu , você pode usar uma função geradora para fazer isso sequencialmente, em vez de recursivamente.
Aqui está o equivalente do código na sua pergunta:
fonte
Muitos recomendam que aumentar o limite de recursão seja uma boa solução, mas não é porque sempre haverá limite. Em vez disso, use uma solução iterativa.
fonte
Eu queria dar um exemplo para usar a memorização para calcular Fibonacci, pois isso permitirá que você calcule números significativamente maiores usando recursão:
Isso ainda é recursivo, mas usa uma hashtable simples que permite a reutilização de números de Fibonacci calculados anteriormente, em vez de fazê-los novamente.
fonte
fonte
Podemos fazer isso usando o
@lru_cache
decorador e osetrecursionlimit()
método:Resultado
Fonte
functools lru_cache
fonte
Também poderíamos usar uma variação da abordagem dinâmica de baixo para cima para programação
fonte