Tengo esta función recursiva de cola aquí:
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 hasta n=997
, luego simplemente se rompe y escupe a RecursionError: maximum recursion depth exceeded in comparison
. ¿Es esto solo un desbordamiento de pila? ¿Hay alguna forma de evitarlo?
line <n>, in <module>
trazas en la pila) y este código toma 2 cuadros de pilan=1
(porque el caso base esn < 1
, porn=1
lo que aún se repite). Y supongo que el límite de recursión no es inclusivo, ya que en "error cuando alcanza 1000" no "error si excede 1000 (1001)".997 + 2
es inferior a 1000, por lo998 + 2
que no funciona porque llega al límite.recursive_function(997)
funciona, se rompe a las998
. Cuando lo llamarecursive_function(998)
, usa 999 marcos de pila y el intérprete agrega 1 marco (porque su código siempre se ejecuta como si fuera parte del módulo de nivel superior), lo que hace que llegue al límite de 1000.Respuestas:
Es una protección contra un desbordamiento de pila, sí. Python (o más bien, la implementación de CPython) no optimiza la recursión de cola, y la recursión desenfrenada provoca desbordamientos de pila. Puede verificar el límite de recursión con
sys.getrecursionlimit
y cambiar el límite de recursión consys.setrecursionlimit
, pero hacerlo es peligroso: el límite estándar es un poco conservador, pero los marcos de pila de Python pueden ser bastante grandes.Python no es un lenguaje funcional y la recursividad de cola no es una técnica particularmente eficiente. Reescribir el algoritmo iterativamente, si es posible, generalmente es una mejor idea.
fuente
sys
como en losresource
módulos: stackoverflow.com/a/16248113/205521Parece que solo necesita establecer una mayor profundidad de recursión :
fuente
Es para evitar un desbordamiento de pila. El intérprete de Python limita las profundidades de recursión para ayudarlo a evitar infinitas recursiones, lo que resulta en desbordamientos de pila. Intenta aumentar el límite de recursión (
sys.setrecursionlimit
) o reescribe tu código sin recurrencia.De la documentación de Python :
fuente
Si a menudo necesita cambiar el límite de recurrencia (por ejemplo, mientras resuelve acertijos de programación), puede definir un administrador de contexto simple como este:
Luego, para llamar a una función con un límite personalizado, puede hacer:
Al salir del cuerpo de la
with
declaración, el límite de recursión se restaurará al valor predeterminado.fuente
resource
. Sin él, obtendrá una falla de segmentación y todo el proceso de Python se bloqueará si essetrecursionlimit
demasiado alto e intente usar el nuevo límite (aproximadamente 8 megabytes de cuadros de pila, que se traduce en ~ 30,000 cuadros de pila con la función simple anterior, en mi portátil).Utilice un lenguaje que garantice la optimización de las llamadas de cola. O usa la iteración. Alternativamente, ponte lindo con los decoradores .
fuente
ulimit -s
marcos de pila, sí, es stackoverflow.com/a/50120316resource.setrlimit
también se debe usar para aumentar el tamaño de la pila y evitar la falla predeterminadaEl kernel de Linux limita la pila de procesos .
Python almacena variables locales en la pila del intérprete, por lo que la recursión ocupa espacio en la pila del intérprete.
Si el intérprete de Python intenta superar el límite de la pila, el kernel de Linux lo convierte en un error de segmentación.
El tamaño límite de la pila se controla con las
getrlimit
ysetrlimit
llamadas al sistema.Python ofrece acceso a esas llamadas del sistema a través del
resource
módulo.Por supuesto, si sigue aumentando ulimit, su RAM se agotará, lo que ralentizará su computadora debido a la locura del intercambio o matará a Python a través del OOM Killer.
Desde bash, puede ver y establecer el límite de la pila (en kb) con:
El valor predeterminado para mí es 8Mb.
Ver también:
Probado en Ubuntu 16.10, Python 2.7.12.
fuente
rlimit_stack
después de las correcciones de Choque de pila puede provocar fallas o problemas relacionados. También vea Red Hat Issue 1463241Me doy cuenta de que esta es una vieja pregunta, pero para aquellos que leen, recomendaría no usar la recursividad para problemas como este: las listas son mucho más rápidas y evitan la recursión por completo. Implementaría esto como:
(Use n + 1 en xrange si comienza a contar su secuencia de Fibonacci desde 0 en lugar de 1.)
fuente
xrange
se llama simplementerange
, en Python 3.Por supuesto, los números de Fibonacci se pueden calcular en O (n) aplicando la fórmula de Binet:
Como señalan los comentaristas, no es O (1) sino O (n) debido a
2**n
. También una diferencia es que solo obtienes un valor, mientras que con la recursividad obtienes todos los valores deFibonacci(n)
hasta ese valor.fuente
n
debido a la imprecisión de coma flotante: la diferencia entre(1+sqrt(5))**n
y se(1+sqrt(5))**(n+1)
convierte en menos de 1 ulp, por lo que comienza a obtener resultados incorrectos.(1+sqrt(5))**n
y((1+sqrt(5))**n)+1
eso se convierte en menos de 1 ulp! (error tipográfico pequeño) Además, {@} rwst ¡Eso no es O (1)! El cálculo2**n
lleva al menos O (n) tiempo.2**n
es efectivamente O (log (n)) usando Exponentiattion mediante cuadratura .Tuve un problema similar con el error "Profundidad máxima de recursión excedida" Descubrí que el error estaba siendo desencadenado por un archivo corrupto en el directorio con el que estaba pasando
os.walk
. Si tiene problemas para resolver este problema y está trabajando con rutas de archivos, asegúrese de reducirlo, ya que podría ser un archivo corrupto.fuente
Si desea obtener solo unos pocos números de Fibonacci, puede usar el método de matriz.
Es rápido ya que numpy usa un algoritmo de exponenciación rápida. Obtiene respuesta en O (log n). Y es mejor que la fórmula de Binet porque solo usa números enteros. Pero si desea todos los números de Fibonacci hasta n, entonces es mejor hacerlo memorizando.
fuente
¿Usar generadores?
función fib () anterior adaptada de: http://intermediatepythonista.com/python-generators
fuente
[fibs().next() for ...]
cada vez se crearía un nuevo generador.Como sugirió @alex , podría usar una función de generador para hacer esto secuencialmente en lugar de recursivamente.
Aquí está el equivalente del código en su pregunta:
fuente
Muchos recomiendan que aumentar el límite de recursión sea una buena solución, sin embargo, no lo es porque siempre habrá un límite. En su lugar, use una solución iterativa.
fuente
Quería darle un ejemplo para usar la memorización para calcular Fibonacci, ya que esto le permitirá calcular números significativamente mayores utilizando la recursividad:
Esto todavía es recursivo, pero utiliza una tabla hash simple que permite la reutilización de números de Fibonacci calculados previamente en lugar de volver a hacerlos.
fuente
fuente
Podemos hacerlo usando
@lru_cache
decorador ysetrecursionlimit()
método:Salida
Fuente
functools lru_cache
fuente
También podríamos usar una variación del enfoque ascendente de programación dinámica
fuente