sopa cuántica
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
entonces simplemente se rompe y escupe un RecursionError: maximum recursion depth exceeded in comparison
. ¿Es esto solo un desbordamiento de pila? ¿Hay alguna forma de evitarlo?
Tomás Wouters
Es una protección contra un desbordamiento de pila, sí. Python (o más bien, la implementación de CPython) no optimiza la recursividad final, y la recursividad desenfrenada provoca desbordamientos de pila. Puede comprobar el límite de recurrencia con sys.getrecursionlimit
:
import sys
print(sys.getrecursionlimit())
y cambiar el límite de recurrencia con sys.setrecursionlimit
:
sys.setrecursionlimit(1500)
pero hacerlo es peligroso: el límite estándar es un poco conservador, pero los stackframes de Python pueden ser bastante grandes.
Python no es un lenguaje funcional y la recursión de cola no es una técnica particularmente eficiente. Reescribir el algoritmo iterativamente, si es posible, generalmente es una mejor idea.
-
Según mi experiencia, es necesario aumentar el límite tanto en el
sys
y elresource
módulos: stackoverflow.com/a/16248113/205521– Tomas Ahle
28/04/2014 a las 19:10
-
como táctica para convertirlo en una versión iterativa, se podría usar un decorador de optimización de llamadas de cola
– jfs
14/10/2014 a las 18:28
-
puedes usar svn.python.org/projects/python/trunk/Tools/scripts/… para averiguar el límite superior de su sistema operativo
– Ullullu
16/09/2015 a las 13:55
-
Para aquellos interesados en la fuente, el límite de recurrencia predeterminado se establece en 1000 hg.python.org/cpython/file/tip/Python/ceval.c#l691 y se puede cambiar usando la API en hg.python.org/cpython/file/tip/Python/sysmodule.c#l643 que a su vez establece el límite para el nuevo valor en hg.python.org/cpython/file/tip/Python/ceval.c#l703
– Pramod
7 oct 2015 a las 18:51
-
La recursión de cola es una técnica perfectamente eficiente en un lenguaje de programación optimizado para ello. Para el tipo correcto de problema, puede ser considerablemente más expresivo una implementación iterativa. La respuesta probablemente significa “específicamente en Python”, pero eso no es lo que dice
– Pedro R.
6 de marzo de 2017 a las 15:04
david joven
Parece que solo necesitas establecer una mayor profundidad de recursión:
import sys
sys.setrecursionlimit(1500)
-
En mi caso, olvidé la declaración de devolución en el caso base y superó los 1000. Python comenzó a lanzar esta excepción y me sorprendió, porque estaba seguro del no. de pilas que va a crear para ejecutarlo.
– vijayraj34
19 de mayo de 2019 a las 8:07
-
sys.setrecursionlimit(50) o una pequeña cantidad es útil si su programa está entrando en recursividad y desea que el mensaje de error NO sea páginas y páginas del mismo texto. Encontré esto muy útil al depurar (mi) código recursivo incorrecto.
– peawormsworth
22 de febrero de 2020 a las 0:38
-
Tenga en cuenta que esto está sujeto a los límites dependientes de la plataforma
– Sólido
19 de febrero a las 20:36
Eugene Yarmash
Si a menudo necesita cambiar el límite de recurrencia (por ejemplo, al resolver problemas de programación), puede definir un simple administrador de contexto como esto:
import sys
class recursionlimit:
def __init__(self, limit):
self.limit = limit
def __enter__(self):
self.old_limit = sys.getrecursionlimit()
sys.setrecursionlimit(self.limit)
def __exit__(self, type, value, tb):
sys.setrecursionlimit(self.old_limit)
Luego, para llamar a una función con un límite personalizado, puede hacer:
with recursionlimit(1500):
print(fib(1000, 0))
A la salida del cuerpo del with
instrucción, el límite de recurrencia se restaurará al valor predeterminado.
PD: es posible que también desee aumentar el tamaño de la pila del proceso de Python para valores grandes del límite de recurrencia. Eso se puede hacer a través de la ulimit
shell incorporado o limits.conf(5)
archivo, por ejemplo.
-
También desea aumentar el límite de recurrencia del proceso con
resource
. Sin él, obtendrá una falla de segmentación y todo el proceso de Python fallará sisetrecursionlimit
demasiado alto e intente usar el nuevo límite (alrededor de 8 megabytes de marcos de pila, lo que se traduce en ~ 30,000 marcos de pila con la función simple anterior, en mi computadora portátil).– Boris Verjovskiy
28 de diciembre de 2019 a las 19:35
-
@Boris: eso podría agregarse al administrador de contexto, sin embargo, aumentar el límite de tamaño de la pila solo funcionará para la raíz (superusuario).
– Eugene Yarmash
9 de noviembre de 2021 a las 21:41
Scharron
Es para evitar un desbordamiento de pila. El intérprete de Python limita la profundidad de la recursividad para ayudarlo a evitar recurrencias infinitas, lo que resulta en desbordamientos de pila. Intente aumentar el límite de recurrencia (sys.setrecursionlimit
) o reescribiendo su código sin recursividad.
Desde el Documentación de Python:
sys.getrecursionlimit()
Devuelve el valor actual del límite de recurrencia, la profundidad máxima de la pila del intérprete de Python. Este límite evita que la recurrencia infinita provoque un desbordamiento de la pila C y bloquee Python. Se puede establecer por
setrecursionlimit()
.
Ciro Santilli OurBigBook.com
resource.setrlimit
también debe usarse para aumentar el tamaño de la pila y evitar fallas de segmento
El núcleo de Linux limita la pila de procesos.
Python almacena variables locales en la pila del intérprete, por lo que la recursividad ocupa espacio en la pila del intérprete.
Si el intérprete de Python intenta sobrepasar 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 el getrlimit
y setrlimit
llamadas del sistema.
Python ofrece acceso a esas llamadas al sistema a través del resource
módulo.
sys.setrecursionlimit
mencionado, por ejemplo, en https://stackoverflow.com/a/3323013/895245 solo aumenta el límite que el propio intérprete de Python impone sobre su propio tamaño de pila, pero no toca el límite impuesto por el kernel de Linux en el proceso de Python.
Programa de ejemplo:
principal.py
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)
Por supuesto, si sigue aumentando setrlimit
su RAM eventualmente se agotará, lo que ralentizará su computadora debido a la locura de intercambio, o matará a Python a través del OOM Killer.
Desde bash, puede ver y establecer el límite de pila (en kb) con:
ulimit -s
ulimit -s 10000
El valor predeterminado para mí es 8Mb.
Ver también:
- Configuración del tamaño de pila en un script de python
- ¿Cuál es el límite estricto de recursividad para Linux, Mac y Windows?
Probado en Ubuntu 16.10, Python 2.7.12.
-
Intentando establecer
rlimit_stack
después Choque de pila las soluciones pueden resultar en fallas o problemas relacionados. Véase también Sombrero rojo Problema 1463241– jww
21 de junio de 2017 a las 16:35
-
Utilicé esto (la parte de recursos de Python) para ayudar a mi implementación del algoritmo de Kosaraju en el conjunto de datos medio (enorme) del profesor Tim Roughgarden. Mi implementación funcionó en conjuntos pequeños, ciertamente el problema con un conjunto de datos grande era el límite de recursividad/apilado… ¿O no? Bueno, ¡sí lo fue! ¡Gracias!
– nilo
28 de enero de 2019 a las 8:04
-
Gracias Ciro gran respuesta detalles!
– X0-usuario-0X
26 de junio de 2022 a las 15:23
marcelo cantos
Utilice un lenguaje que garantice la optimización de la cola de llamadas. O usa la iteración. Alternativamente, ponte lindo con decoradores.
-
Intentando establecer
rlimit_stack
después Choque de pila las soluciones pueden resultar en fallas o problemas relacionados. Véase también Sombrero rojo Problema 1463241– jww
21 de junio de 2017 a las 16:35
-
Utilicé esto (la parte de recursos de Python) para ayudar a mi implementación del algoritmo de Kosaraju en el conjunto de datos medio (enorme) del profesor Tim Roughgarden. Mi implementación funcionó en conjuntos pequeños, ciertamente el problema con un conjunto de datos grande era el límite de recursividad/apilado… ¿O no? Bueno, ¡sí lo fue! ¡Gracias!
– nilo
28 de enero de 2019 a las 8:04
-
Gracias Ciro gran respuesta detalles!
– X0-usuario-0X
26 de junio de 2022 a las 15:23
Daniel
Me doy cuenta de que esta es una pregunta antigua, 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 recursividad por completo. Yo implementaría esto como:
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])
(Use n+1 en xrange si comienza a contar su secuencia de Fibonacci desde 0 en lugar de 1).
-
¿Por qué usar el espacio O(n) cuando puedes usar O(1)?
– Janus Troelsen
12 de marzo de 2014 a las 9:11
-
En caso de que el comentario del espacio O(n) fuera confuso: no use una lista. La lista mantendrá todos los valores cuando todo lo que necesite sea el valor n. Un algoritmo simple sería mantener los dos últimos números de Fibonacci y sumarlos hasta llegar al que necesita. También hay mejores algoritmos.
– milimétrico
14/07/2014 a las 19:12
-
@Mathime:
xrange
se llama simplementerange
en Python 3.– Eric O. Lebigot
3 de agosto de 2016 a las 9:50
-
@EOL Soy consciente de esto
– Mathime
3 de agosto de 2016 a las 9:51
-
@Mathime Estaba haciendo las cosas explícitas para aquellos que leen estos comentarios.
– Eric O. Lebigot
3 de agosto de 2016 a las 9:54
Ver también stackoverflow.com/questions/5061582/…
– Tomas Ahle
28 de abril de 2014 a las 19:09
memorización podría acelerar su función y aumentar su profundidad recursiva efectiva al hacer que los valores calculados previamente terminen en lugar de aumentar el tamaño de la pila.
– Cyoce
11 de enero de 2016 a las 18:47
El límite de recurrencia suele ser 1000.
– Boris Verjovskiy
24 de abril de 2019 a las 7:29
@tonix el intérprete agrega un marco de pila (el
line <n>, in <module>
en seguimientos de pila) y este código toma 2 marcos de pila paran=1
(porque el caso base esn < 1
así que paran=1
todavía recurre). Y supongo que el límite de recurrencia no es inclusivo, ya que es “error cuando llegas a 1000” no “error si superas 1000 (1001)”.997 + 2
es menos de 1000 por lo que funciona998 + 2
no lo hace porque llega al límite.– Boris Verjovskiy
15 dic 2019 a las 19:00
@tonix No.
recursive_function(997)
funciona, se rompe en998
. Cuando usted llamarecursive_function(998)
utiliza 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 alcance el límite de 1000.– Boris Verjovskiy
15 de diciembre de 2019 a las 19:49