web-dev-qa-db-fra.com

Modèles de conception pour convertir des algorithmes récursifs en algorithmes itératifs

Existe-t-il des heuristiques générales, des conseils, des astuces ou des paradigmes de conception courants qui peuvent être utilisés pour convertir un algorithme récursif en algorithme itératif? Je sais que cela peut être fait, je me demande s'il y a des pratiques qui méritent d'être gardées à l'esprit en le faisant.

43
fbrereto

Vous pouvez souvent conserver entièrement la structure d'origine d'un algorithme récursif, mais éviter la pile, en utilisant des appels de queue et en changeant en continuation-passant , comme suggéré par cette entrée de blog . (Je devrais vraiment cuisiner un meilleur exemple autonome.)

30
Brian

Une technique courante que j'utilise lorsque je suis en train de remplacer un algorithme récursif par un algorithme itératif est généralement d'utiliser une pile, en poussant les paramètres qui sont passés à la fonction récursive.

Consultez les articles suivants:

22
CMS

Une pratique courante est pour gérer une LIFO qui conserve une liste courante de ce qui "reste à faire", et pour gérer l'ensemble du processus dans une boucle while qui continue jusqu'à ce que la liste soit vide.
Avec ce modèle, ce qui serait un appel de récursivité dans le vrai modèle de récursivité est remplacé par
- une poussée du "contexte" de la tâche actuelle (partiellement terminée) sur la pile,
- une poussée de la nouvelle tâche (celle qui a provoqué la récursivité) sur la pile
- et pour "continuer" (c'est-à-dire passer au début de) la boucle while. Près de la tête de la boucle, la logique affiche le contexte le plus récemment inséré et commence à travailler sur cette base.

Effectivement cela "déplace" simplement les informations qui auraient autrement été conservées dans des cadres de pile imbriqués sur la pile "système", vers un conteneur de pile géré par l'application. Il s'agit cependant d'une amélioration, car ce conteneur de pile peut être alloué n'importe où (la limite de récursivité est généralement liée aux limites de la pile "système"). Par conséquent, essentiellement, le même travail est effectué, mais la gestion explicite d'une "pile" permet que cela se fasse au sein d'une construction à boucle unique plutôt que d'appels récursifs.

8
mjv

Souvent, la récursivité générale peut être remplacée par la récursivité de queue, en collectant des résultats partiels dans un accumulateur et en les transmettant avec l'appel récursif. La récursivité de la queue est essentiellement itérative, l'appel récursif peut être implémenté comme un saut.

Par exemple, la définition récursive générale standard de factorielle

factorial(n) = if n = 0 then 1 else n * factorial(n - 1)

peut être remplacé par

 factorial(n) = f_iter(n, 1)

et

 f_iter(n, a) = if n = 0 then a else f_iter(n - 1, n * a)

qui est récursif de queue. C'est la même chose que

a = 1;
while (n != 0) {
    a = n * a;
    n = n - 1;
}
return a;
7
starblue

Je commence généralement par le cas de base (chaque fonction récursive en a une) et je recule, stockant les résultats dans un cache (tableau ou table de hachage) si nécessaire.

Votre fonction récursive résout un problème en résolvant des sous-problèmes plus petits et en les utilisant pour résoudre l'instance du problème plus grande. Chaque sous-problème est également décomposé davantage et ainsi de suite jusqu'à ce que le sous-problème soit si petit que la solution soit triviale (c'est-à-dire le cas de base).

L'idée est de commencer par le cas de base (ou les cas de base) et de l'utiliser pour construire la solution pour les cas plus grands, puis de les utiliser pour construire des cas encore plus grands et ainsi de suite, jusqu'à ce que tout le problème soit résolu. Cela ne nécessite pas de pile et peut être effectué avec des boucles.

Un exemple simple (en Python):

#recursive version
def fib(n):
     if n==0 or n==1:
             return n
     else:
             return fib(n-1)+fib(n-2)

#iterative version
def fib2(n):
     if n==0 or n==1:
             return n
     prev1,prev2=0,1 # start from the base case
     for i in xrange(n):
             cur=prev1+prev2 #build the solution for the next case using the previous solutions
             prev1,prev2=cur,prev1
     return cur
4
MAK

Jetez un œil à ces liens pour des exemples de performances

Récursion VS Itération (Boucle): Comparaison Vitesse & Mémoire

et

Remplacer la récursivité par l'itération

et

récursivité vs itération

Q: La version récursive est-elle généralement plus rapide? R: Non - c'est généralement plus lent (en raison des frais généraux liés au maintien de la pile)

  Q: Does the recursive version usually use less memory?
  A: No -- it usually uses more memory (for the stack).

  Q: Then why use recursion??
  A: Sometimes it is much simpler to write the recursive version (but

nous devrons attendre d'avoir discuté des arbres pour voir de très bons exemples ...)

4
Adriaan Stander

Un modèle est Tail Recursion :

Un appel de fonction est dit récursif de queue s'il n'y a rien à faire après le retour de la fonction, sauf renvoyer sa valeur.

Wiki .

3
Mitch Wheat