374 votes

La récursion est-elle toujours plus rapide que le bouclage ?

Je sais que la récursion est parfois beaucoup plus propre que le bouclage, et je ne demande rien sur le moment où je devrais utiliser la récursion plutôt que l'itération, je sais qu'il y a déjà beaucoup de questions à ce sujet.

Ce que je demande est, est-ce que la récursion jamais plus vite qu'une boucle ? Pour moi, il me semble qu'il serait toujours possible d'affiner une boucle et de la faire fonctionner plus rapidement qu'une fonction récursive parce que la boucle n'est pas constamment en train de créer de nouveaux cadres de pile.

Je cherche spécifiquement à savoir si la récursion est plus rapide dans les applications où la récursion est la bonne façon de traiter les données, comme dans certaines fonctions de tri, dans les arbres binaires, etc.

5 votes

Il faut parfois des siècles pour trouver des procédures itératives ou des formules en forme fermée pour certaines récurrences. Je pense que c'est seulement dans ces moments-là que la récursion est plus rapide :) lol

53 votes

En ce qui me concerne, je préfère de loin l'itération ;-)

0 votes

Duplication possible de Récursion ou itération ?

447voto

Dietrich Epp Points 72865

Cela dépend de la langue utilisée. Vous avez écrit "language-agnostic", je vais donc vous donner quelques exemples.

En Java, C et Python, la récursion est assez coûteuse par rapport à l'itération (en général) car elle nécessite l'allocation d'un nouveau cadre de pile. Dans certains compilateurs C, on peut utiliser un drapeau de compilation pour éliminer cette surcharge, ce qui transforme certains types de récursion (en fait, certains types d'appels de queue) en sauts au lieu d'appels de fonction.

Dans les implémentations des langages de programmation fonctionnelle, l'itération peut parfois être très coûteuse et la récursion très bon marché. Dans beaucoup, la récursion est transformée en un simple saut, mais en changeant la variable de la boucle (qui est mutable) parfois nécessite des opérations relativement lourdes, en particulier sur les implémentations qui prennent en charge plusieurs threads d'exécution. La mutation est coûteuse dans certains de ces environnements en raison de l'interaction entre le mutateur et le ramasseur de déchets, si les deux peuvent être exécutés en même temps.

Je sais que dans certaines implémentations de Scheme, la récursion est généralement plus rapide que le bouclage.

En bref, la réponse dépend du code et de l'implémentation. Utilisez le style que vous préférez. Si vous utilisez un langage fonctionnel, la récursion pourrait être plus rapide. Si vous utilisez un langage impératif, l'itération est probablement plus rapide. Dans certains environnements, les deux méthodes aboutiront à la génération du même assemblage (mettez cela dans votre pipe et fumez-le).

Addendum : Dans certains environnements, la meilleure alternative n'est ni la récursion ni l'itération, mais plutôt les fonctions d'ordre supérieur. Il s'agit notamment de "map", "filter" et "reduce" (qui est également appelé "fold"). Non seulement ces fonctions sont le style préféré, non seulement elles sont souvent plus propres, mais dans certains environnements, ces fonctions sont les premières (ou les seules) à bénéficier de la parallélisation automatique - elles peuvent donc être nettement plus rapides que l'itération ou la récursion. Data Parallel Haskell est un exemple d'un tel environnement.

Les compréhensions de listes sont une autre alternative, mais elles ne sont généralement que du sucre syntaxique pour l'itération, la récursion ou les fonctions d'ordre supérieur.

69 votes

Je +1 cela, et je voudrais commenter que la "récursion" et les "boucles" sont juste le nom que les humains donnent à leur code. Ce qui compte pour les performances n'est pas la façon dont vous nom mais plutôt la façon dont elles sont compilées/interprétées. La récursion, par définition, est un concept mathématique, et n'a pas grand-chose à voir avec les cadres de pile et les trucs d'assemblage.

2 votes

De plus, la récursion est, en général, l'approche la plus naturelle dans les langages fonctionnels, et l'itération est normalement plus intuitive dans les langages impératifs. Il est peu probable que la différence de performance soit perceptible, alors utilisez simplement ce qui vous semble le plus naturel pour ce langage particulier. Par exemple, vous ne voudrez probablement pas utiliser l'itération en Haskell alors que la récursion est beaucoup plus simple.

4 votes

En général, la récursion est compilé aux boucles, les boucles étant une construction de niveau inférieur. Pourquoi ? Parce que la récursion est typiquement bien fondée sur une certaine structure de données, induisant une F-algèbre initiale et vous permet de prouver certaines propriétés sur la terminaison ainsi que des arguments inductifs sur la structure du calcul (récursif). Le processus par lequel la récursivité est compilée en boucles est l'optimisation par appel de queue.

50voto

starblue Points 29696

La récursion peut être plus rapide lorsque l'alternative consiste à gérer explicitement une pile, comme dans les algorithmes de tri ou d'arbre binaire que vous mentionnez.

Il m'est arrivé que la réécriture d'un algorithme récursif en Java le rende plus lent.

La bonne approche consiste donc à l'écrire d'abord de la manière la plus naturelle, à ne l'optimiser que si le profilage montre qu'elle est critique, puis à mesurer l'amélioration supposée.

8 votes

+1 pour " écrivez-le d'abord de la manière la plus naturelle possible " et surtout " n'optimiser que si le profilage montre que c'est critique "

8 votes

+1 pour reconnaître que la pile matérielle peut être plus rapide qu'une pile logicielle, implémentée manuellement, in-heap. Montrant efficacement que toutes les réponses négatives sont incorrectes.

0 votes

Cela suppose toutefois que l'algorithme que vous avez écrit était effectivement l'algorithme itératif le plus rapide. Un mauvais remaniement/optimisation est également possible ici.

15voto

mkorpela Points 1441

Récursion de la queue est aussi rapide que le bouclage. De nombreux langages fonctionnels intègrent la récursion de queue.

41 votes

Récursion de la queue peut être aussi rapidement que le bouclage, lorsqu'une optimisation des appels de queue est mise en œuvre : c2.com/cgi/wiki?TailCallOptimization

12voto

Pasi Savolainen Points 1489

Considérez ce qui doit absolument être fait pour chacun, l'itération et la récursion.

  • itération : un saut au début de la boucle
  • récursion : un saut au début de la fonction appelée

Vous voyez qu'il n'y a pas beaucoup de place pour les différences ici.

(Je suppose que la récursion est un appel de queue et que le compilateur est conscient de cette optimisation).

3voto

Kilian Foth Points 8619

Dans tout système réaliste, non, la création d'un cadre de pile sera toujours plus coûteuse qu'un INC et un JMP. C'est pourquoi les très bons compilateurs transforment automatiquement la récursion de queue en un appel au même cadre, c'est-à-dire sans la surcharge, de sorte que vous obtenez la version source plus lisible et la version compilée plus efficace. A vraiment, vraiment Un bon compilateur devrait même être capable de transformer la récursion normale en récursion de queue lorsque cela est possible.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X