Utiliser des décorateurs pour optimiser la récursivité de la queue en Python

高洛峰
Libérer: 2017-03-02 10:55:03
original
1648 Les gens l'ont consulté

Ici, nous utilisons la séquence typique de Fibonacci comme exemple pour montrer comment utiliser les décorateurs pour optimiser la récursion de la queue en Python. Les amis dans le besoin peuvent se référer à

Introduction à la récursion de la queue
La récursion de queue signifie que la dernière opération renvoyée par la fonction est un appel récursif, alors la fonction est récursive de queue.
La récursion est linéaire. Par exemple, chaque fois que la fonction factorielle est appelée, une nouvelle pile (dernier entré, premier sorti) est créée en poussant continuellement la pile, ce qui peut facilement conduire à un débordement de pile. La récursion de queue utilise la pile actuelle pour optimiser les fonctions récursives grâce à la couverture des données.
La fonction factorielle factorielle complète la récursivité de la queue en transmettant la valeur calculée. Cependant, Python ne permet pas au compilateur d'optimiser la récursion de queue, donc si la récursion est répétée plusieurs fois, une erreur sera toujours signalée (à des fins d'apprentissage).

par exemple :

def factorial(n, x):
  if n == 0:
    return x
  else:
    return factorial(n-1, n*x)

print factorial(5, 1) # 120
Copier après la connexion

L'optimisation récursive de queue est utilisée ici Les nombres de Fibonacci sont utilisés comme exemple. L'algorithme récursif linéaire a été adopté par nous car il était trop inefficace. Examinons d'abord la méthode d'appel par passe finale :

(n,b1=1,b2=1,c=3):
 if n<3:
  return 1
 else:
  if n==c:
   return b1+b2
  else:
   return Fib(n,b1=b2,b2=b1+b2,c=c+1)
Copier après la connexion
<. 🎜>

Testons ce programme et appelons Fib(1001 ). Le résultat est :

>>> def Fib(n,b1=1,b2=1,c=3):

...  if n<3:

...   return 1

...  else:

...   if n==c:

...    return b1+b2

...   else:

...    return Fib(n,b1=b2,b2=b1+b2,c=c+1)

... 

>>> Fib(1001)

70330367711422815821835254877183549770181269836358732742604905087154537118196933579742249494562611733487750449241765991088186363265450223647106012053374121273867339111198139373125598767690091902245245323403501L

>>>
Copier après la connexion

Si nous utilisons Fib(1002 ), le le résultat est la table basse, comme suit :

 .....

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

 File "<stdin>", line 8, in Fib

RuntimeError: maximum recursion depth exceeded

>>>
Copier après la connexion

Bon, maintenant nous arrivons à l'optimisation récursive

Nous donnons juste au Fib maintenant La fonction ajoute un Décorateur, comme suit :


@tail_call_optimized
def Fib(n,b1=1,b2=1,c=3):
 if n<3:
  return 1
 else:
  if n==c:
   return b1+b2
  else:
   return Fib(n,b1=b2,b2=b1+b2,c=c+1)
Copier après la connexion

Eh bien, c'est ce décorateur @tail_call_optimisé, ce décorateur fait du Python magique Cela brise la limite de la pile d'appels.


Maintenant, même si nous utilisons Fib (20000), nous pouvons exécuter le résultat en 780 ms (780 ms est le résultat du netbook à 2000 yuans mentionné dans le billet de blog précédent)

Ce n'est pas un mensonge , Jetons un coup d'œil à ce code magique :


class TailRecurseException: 
 def __init__(self, args, kwargs): 
 self.args = args 
 self.kwargs = kwargs 
 
def tail_call_optimized(g): 
 """ 
 This function decorates a function with tail call 
 optimization. It does this by throwing an exception 
 if it is it&#39;s own grandparent, and catching such 
 exceptions to fake the tail call optimization. 
 
 This function fails if the decorated 
 function recurses in a non-tail context. 
 """ 
 def func(*args, **kwargs): 
 f = sys._getframe() 
 if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code: 
  raise TailRecurseException(args, kwargs) 
 else: 
  while 1: 
  try: 
   return g(*args, **kwargs) 
  except TailRecurseException, e: 
   args = e.args 
   kwargs = e.kwargs 
 func.__doc__ = g.__doc__ 
 return func
Copier après la connexion

La méthode utilisée a déjà été montrée, ce qui me révèle une révélation. Oui, l'auteur a utilisé la méthode consistant à lancer des exceptions puis à les attraper lui-même pour interrompre la croissance de la pile d'appels, ce qui est tout simplement incroyable. De plus, les problèmes d’efficacité entraînent environ cinq fois plus de temps que la récursion directe de la queue Fib.

Au final, étonnamment, l'objectif d'optimisation de la récursivité de la queue a été atteint.


Pour plus d'articles sur l'utilisation de décorateurs pour optimiser la récursivité de la queue en Python, veuillez faire attention au site Web PHP chinois !

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!