web-dev-qa-db-fra.com

récursivité versus itération

Est-il exact de dire que partout où recursion est utilisé, une boucle for pourrait être utilisée? Et si la récursivité est généralement plus lente, quelle est la raison technique pour laquelle on l'utilise jamais pour l'itération de boucle?

Et s'il est toujours possible de convertir une récursivité en une boucle for, existe-t-il une règle empirique permettant de le faire?

83
Breako Breako

La récursivité est généralement beaucoup plus lente car tous les appels de fonction doivent être stockés dans une pile pour permettre le retour aux fonctions de l'appelant. Dans de nombreux cas, la mémoire doit être allouée et copiée pour implémenter l'isolation de la portée.

Certaines optimisations, comme tail call optimization , accélèrent les récursivités, mais ne sont pas toujours possibles et ne sont pas implémentées dans toutes les langues.

Les principales raisons d'utiliser la récursivité sont 

  • qu'il est plus intuitif dans de nombreux cas lorsqu'il imite notre approche du problème
  • que certaines structures de données telles que des arbres sont plus faciles à explorer en utilisant la récursivité (ou nécessiteraient des piles dans tous les cas)

Bien sûr, chaque récursion peut être modélisée comme une sorte de boucle: c'est ce que le CPU fera en fin de compte. Et la récursion elle-même, plus directement, signifie de placer les appels de fonctions et les étendues dans une pile. Mais changer votre algorithme récursif en boucle nécessitera peut-être beaucoup de travail et rendra votre code moins maintenable: comme pour toute optimisation, elle ne devrait être tentée que si un profilage ou des preuves le prouvaient nécessaire.

121
Denys Séguret

Est-il exact de dire que partout où la récursion est utilisée, une boucle for pourrait être utilisée?

Oui, car la récursivité dans la plupart des processeurs est modélisée avec des boucles et une structure de données de pile.

Et si la récursivité est généralement plus lente, quelle est la raison technique de l’utiliser?

Ce n'est pas "habituellement plus lent": c'est la récursivité appliquée de manière incorrecte qui est plus lente. En plus de cela, les compilateurs modernes sont capables de convertir certaines récursions en boucles sans même demander.

Et s’il est toujours possible de convertir une récursivité en une boucle for, existe-t-il une méthode empirique?

Écrire des programmes itératifs pour que les algorithmes soient mieux compris lorsqu'ils sont expliqués de manière itérative. écrire des programmes récursifs pour des algorithmes mieux expliqués récursivement.

Par exemple, la recherche d'arbres binaires, l'exécution de quicksort et l'analyse d'expressions dans de nombreux langages de programmation sont souvent expliquées de manière récursive. Celles-ci sont également mieux codées de manière récursive. D'autre part, le calcul des factorielles et le calcul des nombres de Fibonacci sont beaucoup plus faciles à expliquer en termes d'itérations. Utiliser la récursion pour eux, c'est comme écraser des mouches avec un marteau: ce n'est pas une bonne idée, même si le marteau le fait très bien.+.


+ J'ai emprunté l'analogie de sledgehammer à "Discipline of Programming" de Dijkstra.

46
dasblinkenlight

Question:

Et si la récursivité est généralement plus lente, quelle est la raison technique pour laquelle on l'utilise jamais pour l'itération de boucle?

Réponse :

Parce que certains algorithmes sont difficiles à résoudre de manière itérative. Essayez de résoudre la recherche en profondeur d'abord de manière récursive et itérative. Vous aurez l’idée qu’il est tout à fait difficile de résoudre DFS avec itération.

Une autre bonne chose à essayer: essayez d'écrire le tri par fusion de manière itérative. Cela vous prendra un certain temps.

Question:

Est-il exact de dire que partout où la récursion est utilisée, une boucle for pourrait être utilisée?

Réponse :

Oui. Ce fil a une très bonne réponse pour cela.

Question:

Et s’il est toujours possible de convertir une récursivité en une boucle for, existe-t-il une méthode empirique?

Réponse :

Croyez-moi. Essayez d’écrire votre propre version pour résoudre la recherche en profondeur d’abord de manière itérative. Vous remarquerez que certains problèmes sont plus faciles à résoudre récursivement. 

Astuce: La récursivité est bonne lorsque vous résolvez un problème qui peut être résolu par diviser et conquérir technique.

22
Thanakron Tandavas

En plus d'être plus lente, la récursivité peut également entraîner des erreurs de débordement de pile, en fonction de la profondeur à laquelle elle va.

4
G. Steigert

Pour écrire une méthode équivalente en utilisant l'itération, nous devons explicitement utiliser une pile. Le fait que la version itérative nécessite une pile pour sa solution indique que le problème est suffisamment difficile pour qu’il puisse tirer parti de la récursion. En règle générale, la récursivité convient mieux aux problèmes qui ne peuvent pas être résolus avec une quantité de mémoire fixe et qui, par conséquent, requièrent une pile lorsqu’ils sont résolus de façon itérative. Pour décider quelle méthode fonctionne le mieux au cas par cas et la meilleure pratique consiste à choisir en fonction du modèle suivi par le problème.

Par exemple, pour trouver le n-ème nombre triangulaire de Séquence triangulaire: 1 3 6 10 15… Programme utilisant un algorithme itératif pour trouver le n-ème nombre triangulaire:

En utilisant un algorithme itératif:

//Triangular.Java
import Java.util.*;
class Triangular {
   public static int iterativeTriangular(int n) {
      int sum = 0;
      for (int i = 1; i <= n; i ++)
         sum += i;
      return sum;
   }
   public static void main(String args[]) {
      Scanner stdin = new Scanner(System.in);
      System.out.print("Please enter a number: ");
      int n = stdin.nextInt();
      System.out.println("The " + n + "-th triangular number is: " + 
                            iterativeTriangular(n));
   }
}//enter code here

En utilisant un algorithme récursif:

//Triangular.Java
import Java.util.*;
class Triangular {
   public static int recursiveTriangular(int n) {
      if (n == 1)
     return 1;  
      return recursiveTriangular(n-1) + n; 
   }

   public static void main(String args[]) {
      Scanner stdin = new Scanner(System.in);
      System.out.print("Please enter a number: ");
      int n = stdin.nextInt();
      System.out.println("The " + n + "-th triangular number is: " + 
                             recursiveTriangular(n)); 
   }
}
3
shirin

La plupart des réponses semblent supposer que iterative = for loop. Si votre boucle for n'est pas restreinte (a la C, vous pouvez faire ce que vous voulez avec votre compteur de boucle), c'est correct. S'il s'agit d'une boucle realfor (comme dans Python ou la plupart des langages fonctionnels où vous ne pouvez pas modifier manuellement le compteur de boucles), il s'agit de pas correct. 

Toutes les fonctions (calculables) peuvent être implémentées de manière récursive et à l'aide de boucles while (ou de sauts conditionnels, qui sont fondamentalement identiques). Si vous vous limitez vraiment à for loops, vous obtiendrez seulement un sous-ensemble de ces fonctions (les primitives récursives, si vos opérations élémentaires sont raisonnables). Certes, il s’agit d’un sous-ensemble assez volumineux qui contient toutes les fonctions que vous êtes susceptible de rencontrer dans la pratique.

Ce qui est beaucoup plus important, c’est que beaucoup de fonctions sont très faciles à implémenter de manière récursive et extrêmement difficiles à implémenter de manière itérative (la gestion manuelle de votre pile d’appels ne compte pas).

1
Jbeuh

Je crois me souvenir que mon professeur d’informatique a déclaré à l’époque que tous les problèmes qui ont des solutions récursives ont aussi des solutions itératives. Il dit qu'une solution récursive est généralement plus lente, mais elles sont fréquemment utilisées lorsqu'il est plus facile de raisonner et de coder que des solutions itératives.

Cependant, dans le cas de solutions récursives plus avancées, je ne pense pas qu'il sera toujours capable de les implémenter en utilisant une simple boucle for.

0

Oui, comme dit par Thanakron Tandavas ,

La récursivité est bonne lorsque vous résolvez un problème qui peut être résolu par la technique diviser pour régner.

Par exemple: Tours de Hanoi

  1. N anneaux de taille croissante
  2. 3 pôles
  3. Les anneaux commencent empilés sur le pôle 1. Le but est de déplacer les anneaux de manière à être empilés sur le pôle 3 ... Mais
    • Ne peut bouger qu'un anneau à la fois. 
    • Vous ne pouvez pas mettre un anneau plus large sur un anneau plus petit.
  4. La solution itérative est «puissante mais laide»; La solution récursive est «élégante».
0
Ramesh Mukkera