J'ai cette fonction récursive de queue ici:
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))
Ça marche n=997
, puis ça casse et crache a RecursionError: maximum recursion depth exceeded in comparison
. Est-ce juste un débordement de pile? Y a-t-il un moyen de le contourner?
line <n>, in <module>
traces dans la pile) et ce code prend 2 cadres de pile pourn=1
(parce que le cas de base l'estn < 1
, doncn=1
il revient toujours). Et je suppose que la limite de récursivité n'est pas inclusive, comme dans son "erreur lorsque vous frappez 1000" et non "erreur si vous dépassez 1000 (1001)".997 + 2
est inférieur à 1000, donc cela998 + 2
ne fonctionne pas car il atteint la limite.recursive_function(997)
ça marche, ça casse998
. Lorsque vous l'appelez,recursive_function(998)
il utilise 999 cadres de pile et 1 cadre est ajouté par l'interpréteur (car votre code est toujours exécuté comme s'il faisait partie du module de niveau supérieur), ce qui le fait atteindre la limite de 1000.Réponses:
C'est une protection contre un débordement de pile, oui. Python (ou plutôt, l'implémentation CPython) n'optimise pas la récursivité de queue, et la récursion débridée provoque des débordements de pile. Vous pouvez vérifier la limite de récursivité avec
sys.getrecursionlimit
et modifier la limite de récursivité avecsys.setrecursionlimit
, mais cela est dangereux - la limite standard est un peu conservatrice, mais les stackframes Python peuvent être assez gros.Python n'est pas un langage fonctionnel et la récursivité de la queue n'est pas une technique particulièrement efficace. La réécriture itérative de l'algorithme, si possible, est généralement une meilleure idée.
la source
sys
lesresource
modules et: stackoverflow.com/a/16248113/205521Il semble que vous ayez juste besoin de définir une profondeur de récursivité plus élevée :
la source
C'est pour éviter un débordement de pile. L'interpréteur Python limite les profondeurs de récursivité pour vous aider à éviter les récursions infinies, entraînant des débordements de pile. Essayez d'augmenter la limite de récursivité (
sys.setrecursionlimit
) ou de réécrire votre code sans récursivité.Dans la documentation Python :
la source
Si vous devez souvent modifier la limite de récursivité (par exemple lors de la résolution d'énigmes de programmation), vous pouvez définir un gestionnaire de contexte simple comme celui-ci:
Ensuite, pour appeler une fonction avec une limite personnalisée, vous pouvez faire:
À la sortie du corps de l'
with
instruction, la limite de récursivité sera restaurée à la valeur par défaut.la source
resource
. Sans cela, vous obtiendrez une erreur de segmentation et l'ensemble du processus Python se bloquera si vous êtessetrecursionlimit
trop élevé et essayez d'utiliser la nouvelle limite (environ 8 mégaoctets de cadres de pile, ce qui se traduit par ~ 30 000 cadres de pile avec la fonction simple ci-dessus, sur mon ordinateur portable).Utilisez un langage qui garantit l'optimisation des appels de queue. Ou utilisez l'itération. Alternativement, soyez mignon avec des décorateurs .
la source
ulimit -s
cadres de pile, oui c'est stackoverflow.com/a/50120316resource.setrlimit
doit également être utilisé pour augmenter la taille de la pile et éviter les erreurs de segmentationLe noyau Linux limite la pile de processus .
Python stocke des variables locales sur la pile de l'interpréteur, et donc la récursivité occupe l'espace de pile de l'interpréteur.
Si l'interpréteur Python essaie de dépasser la limite de pile, le noyau Linux lui fait un défaut de segmentation.
La taille limite de pile est contrôlée avec les appels système
getrlimit
etsetrlimit
.Python offre un accès à ces appels système via le
resource
module.Bien sûr, si vous continuez à augmenter ulimit, votre RAM s'épuisera, ce qui ralentira votre ordinateur à l'arrêt en raison de la folie des échanges, ou tuera Python via OOM Killer.
Depuis bash, vous pouvez voir et définir la limite de pile (en Ko) avec:
La valeur par défaut pour moi est de 8 Mo.
Voir également:
Testé sur Ubuntu 16.10, Python 2.7.12.
la source
rlimit_stack
après les corrections de Stack Clash peut entraîner une défaillance ou des problèmes associés. Voir également le numéro 1463241 deJe me rends compte que c'est une vieille question mais pour ceux qui lisent, je recommanderais de ne pas utiliser la récursivité pour des problèmes comme celui-ci - les listes sont beaucoup plus rapides et évitent complètement la récursivité. Je mettrais cela en œuvre comme:
(Utilisez n + 1 dans xrange si vous commencez à compter votre séquence fibonacci à partir de 0 au lieu de 1.)
la source
xrange
s'appelle simplementrange
, en Python 3.Bien sûr, les nombres de Fibonacci peuvent être calculés en O (n) en appliquant la formule de Binet:
Comme les commentateurs le notent, ce n'est pas O (1) mais O (n) à cause de
2**n
. Une différence est également que vous n'obtenez qu'une seule valeur, tandis qu'avec la récursivité, vous obtenez toutes les valeursFibonacci(n)
jusqu'à cette valeur.la source
n
raison de l'imprécision en virgule flottante - la différence entre(1+sqrt(5))**n
et(1+sqrt(5))**(n+1)
devient inférieure à 1 ulp, donc vous commencez à obtenir des résultats incorrects.(1+sqrt(5))**n
et((1+sqrt(5))**n)+1
qui devient moins de 1 ulp! (petite faute de frappe) Aussi, {@} rwst Ce n'est pas O (1)! Le calcul2**n
prend au moins O (n) temps.2**n
est en fait O (log (n)) en utilisant l' exponentiattion par quadrature .J'ai eu un problème similaire avec l'erreur "Profondeur de récursivité maximale dépassée". J'ai découvert que l'erreur était déclenchée par un fichier corrompu dans le répertoire avec lequel je faisais une boucle
os.walk
. Si vous rencontrez des problèmes pour résoudre ce problème et que vous travaillez avec des chemins de fichier, assurez-vous de le réduire, car il peut s'agir d'un fichier corrompu.la source
Si vous ne souhaitez obtenir que quelques nombres de Fibonacci, vous pouvez utiliser la méthode matricielle.
C'est rapide car numpy utilise un algorithme d'exponentiation rapide. Vous obtenez la réponse en O (log n). Et c'est mieux que la formule de Binet car elle n'utilise que des entiers. Mais si vous voulez tous les nombres de Fibonacci jusqu'à n, alors il vaut mieux le faire par mémorisation.
la source
Utilisez des générateurs?
ci-dessus fonction fib () adaptée de: http://intermediatepythonista.com/python-generators
la source
[fibs().next() for ...]
créerait un nouveau générateur à chaque fois.Comme l'a suggéré @alex , vous pouvez utiliser une fonction de générateur pour le faire de manière séquentielle plutôt que récursive.
Voici l'équivalent du code dans votre question:
la source
Beaucoup recommandent que l'augmentation de la limite de récursivité soit une bonne solution mais ce n'est pas parce qu'il y aura toujours une limite. Utilisez plutôt une solution itérative.
la source
Je voulais vous donner un exemple d'utilisation de la mémorisation pour calculer Fibonacci car cela vous permettra de calculer des nombres beaucoup plus importants en utilisant la récursivité:
C'est encore récursif, mais utilise une table de hachage simple qui permet la réutilisation des nombres de Fibonacci précédemment calculés au lieu de les refaire.
la source
la source
Nous pouvons le faire en utilisant le
@lru_cache
décorateur et lasetrecursionlimit()
méthode:Production
La source
functools lru_cache
la source
Nous pourrions également utiliser une variante de l'approche ascendante de la programmation dynamique
la source