Récursivité - est-ce «diviser pour mieux régner» ou «réutiliser le code»

11

La récursivité - comme nous le savons tous - est l'un de ces problèmes - qui vous entoure la tête ressemble à un «jalon» dans votre voyage de programmation.

Mais quand il s'agit de l'utiliser réellement dans des problèmes du monde réel - connaître la mécanique de la récursivité ne suffit pas - il faut également comprendre la nature des problèmes où la récursivité est la solution la plus appropriée.

Voici donc ma question...

  • quels sont les "schémas de problèmes" qui appellent la solution de la récursivité
  • la récursivité est-elle une forme de stratégie "diviser et conquérir" ou une forme de "réutilisation de code" - ou, est un modèle de conception à part entière
  • pouvez-vous nous donner un exemple d'un problème du monde réel où la récursivité vient à l'esprit comme une solution immédiate

-- METTRE À JOUR --

beaucoup de réponses se réfèrent à de "vrais problèmes" comme la traversée d'arbres, de factorielle, etc. Je préférerais "les VRAIS vrais problèmes" - laissez-moi vous donner un exemple ...

Nous avions un GRAND groupe de texte (environ 30 Mo de texte sous forme de liste liée structs), et nous devions en faire un index pour la recherche en texte intégral. Nous devions garder l'index entier en mémoire et réindexer le texte toutes les 10 minutes.

Toutes les 10 minutes, nous comparions le texte entier (deux listes liées, ligne par ligne) avec un morceau de texte nouvellement généré - pour voir quelle ligne a été modifiée - puis nous ré-indexions uniquement cette ligne - de cette façon nous pourrions éviter d'avoir à réindexer le texte ENTIER. N'oubliez pas - nous devions trouver les points de diff entre deux listes liées de 30 Mo.

Un de mes collègues a mis au point un programme fantastique qui utilisait la récursivité LOURDE pour comparer les lignes - puis collecter les positions où les mandrins différaient dans un tableau - oui je sais que cela semble déroutant - comment la récursivité pourrait aider ici - mais ça faisait.

Le point est - comment pourrait-il voir que ce problème pourrait être résolu intelligemment avec une utilisation intensive de la récursivité?

treecoder
la source
Est-ce que 30 Mo sont vraiment importants de nos jours où la plupart des ordinateurs ont Go de RAM et 1 To d'espace disque dur?
JB King
30 Mo ne sont peut-être PAS volumineux - mais compte tenu du type de structure de données dans laquelle notre texte était entassé - c'était vraiment un GRAND groupe de texte à PROCESSER - et DIFF.
treecoder
3
«Traverser une structure de dossiers» n'est-il pas vraiment RÉEL? Et je ne vois absolument pas, dans votre exemple, comment la récursivité devrait être non intuitive ici, et pourquoi son utilisation devrait même être particulièrement remarquable. Votre collègue a conçu un algorithme récursif, comme tout autre algorithme. Vous pourriez aussi bien vous demander comment Hoare a eu l'idée de résoudre le problème de tri de manière récursive.
Konrad Rudolph
2
Ai-je raison de penser que vous vouliez plutôt dire "réutilisation de code" que "exécuter la même série d'opérations un nombre indéterminé de fois"? C'est par opposition à la "réutilisation de code" dans le sens d'écrire du code générique pour une utilisation ailleurs.
Andy Hunt
4
Mais la traversée des arbres est un "VRAI réel problème", que de nombreuses personnes rencontrent presque quotidiennement.
Falcon

Réponses:

16
  • quels sont les "schémas de problèmes" qui appellent la solution de la récursivité

Je ne dirais pas qu'il existe une telle chose comme un modèle de problème pour l'utilisation de la récursivité. Chaque fonction qui peut être implémentée avec la récursivité peut également être implémentée de manière itérative, souvent en poussant et en éclatant une pile.

C'est une question d'expression mais aussi de performance. Les algorithmes itératifs ont souvent de meilleures performances et sont plus faciles à optimiser. Cependant, les algorithmes récursifs bénéficient d'une expression plus claire et sont donc souvent plus faciles à lire, à comprendre et à mettre en œuvre.

Certaines choses ne peuvent même pas être exprimées sans récursivité, des listes infinies par exemple. Les soi-disant langages fonctionnels s'appuient fortement sur la récursivité, car c'est leur mode d'expression naturel. Le dicton est: "La programmation récursive est une programmation fonctionnelle bien faite".

  • la récursivité est-elle une forme de stratégie "diviser et conquérir" ou une forme de "réutilisation de code" - ou, est un modèle de conception à part entière

Je n'appellerais pas cela un modèle de conception. C'est une question d'expression. Parfois, une expression récursive est simplement plus puissante et plus expressive et conduit ainsi à un code meilleur et plus propre.

  • pouvez-vous nous donner un exemple d'un problème du monde réel où la récursivité vient à l'esprit comme une solution immédiate

Tout ce qui doit traverser les arbres sera correctement exprimé par un algorithme récursif.

Faucon
la source
7
"Chaque fonction qui peut être implémentée avec récursivité peut également être implémentée de manière itérative, souvent en poussant et en éclatant une pile." Après tout, dans les langues qui utilisent la mémoire basée sur la pile, vous poussez et sautez déjà les données de fonction sur et hors de la pile lorsque vous utilisez la récursivité.
JAB
Seulement si vous compilez ou interprétez le langage par une machine ;-) De plus, d'un point de vue très élevé, l'expression et le langage sont complètement indépendants de la machine et du matériel et du système d'exploitation, donc, il n'y a pas nécessairement une pile.
Falcon
Ah, oui, vous avez absolument raison. J'aurais dû dire "dans les implémentations de langues / compilateurs de langues qui utilisent la mémoire basée sur la pile".
JAB
De manière générale, vous avez également raison. Je ne voulais pas paraître tatillonne.
Falcon
2
Des listes infinies peuvent être exprimées sans récursivité, du moins sans implémentation récursive. Les générateurs Python peuvent le faire, tout comme les générateurs dans Icon dont Python semble avoir emprunté l'idée. Je crois que F # peut faire ce tour, bien que je ne sois pas sûr. Fondamentalement, les générateurs sont un cas particulier de co-routines (comme le multitâche coopératif) qui sont bien adaptés à la mise en œuvre de listes paresseuses. Chaque fois qu'un générateur "produit" un résultat, l'appelant reprend le contrôle et le générateur reste inactif jusqu'à ce que le résultat suivant soit demandé.
Steve314
8

la récursivité est-elle une forme de stratégie "diviser et conquérir" ou une forme de "réutilisation de code" - ou, est un modèle de conception à part entière

Ni. Divide & conquer utilise la récursivité. Mais la récursivité n'est pas nécessairement diviser pour mieux régner puisque cette dernière signifie diviser un problème en deux (ou plus) parties et résoudre chacune de celles-ci de manière symétrique. En récursivité, vous ne faites pas cela.

La réutilisation du code est complètement indépendante et un modèle de conception entre en jeu à un niveau beaucoup plus élevé. Par exemple, même diviser et conquérir (également un modèle de niveau supérieur à la récursivité) n'est toujours pas considéré comme un modèle de conception - c'est plutôt un modèle algorithmique .

pouvez-vous nous donner un exemple d'un problème du monde réel où la récursivité vient à l'esprit comme une solution immédiate

Traversée des arbres. Ou plus généralement la traversée de graphe. Cela comprend notamment la traversée d'une structure de dossiers.

Et bien sûr, tout ce qui utilise la division et la conquête ou la programmation dynamique, car les deux sont naturellement exprimés en termes de récursivité.

Konrad Rudolph
la source
La programmation dynamique n'est pas toujours naturellement exprimée comme une récursivité. En fait, la programmation dynamique se réfère strictement aux approches tabulaires - à l'exclusion de la mémorisation. Les opinions semblent varier à ce sujet, mais la "programmation" dans la "programmation dynamique" est en fait un terme mathématique, se référant à des approches tabulaires (un morceau de trivia que j'ai repris du cours d'algorithmes MIT opensourceware). Donc, strictement, la programmation dynamique exploite une sous-structure optimale en utilisant ce qui est souvent le plus facilement exprimé sous forme de boucle simple. La mémorisation est beaucoup plus susceptible d'impliquer une récursivité, mais pas nécessairement.
Steve314
1
@ Steve314 Je suis d'accord que l'implémentation pratique de DP (que ce soit dans un programme informatique ou manuellement) utilise rarement la récursivité. Mais l' idée est intrinsèquement basée sur une relation de récurrence - qui n'est qu'une formule récursive! - et un cas de base.
Konrad Rudolph
Je suis d'accord que la "sous-structure optimale" (une solution optimale a des solutions partielles optimales) est une idée récursive. C'est une vision mathématique / informatique de la récursivité, sans rapport direct avec l'implémentation - mais le rôle de la récursivité en informatique est un point important à souligner. Peu d'algorithmes (et probablement aucun modèle de conception) sont des outils importants en informatique - la plupart sont des sujets à étudier plutôt que des outils à utiliser pour étudier autre chose.
Steve314
4
what are the "problem patterns" that call for the solution of recursion

Dérivé de l'auto-similitude des fractales, je dirais que l'auto-égalité ou l'identité de soi (ou comment on l'appelle) est un modèle de problème typique pour la récursivité. C'est-à-dire qu'un problème peut être divisé en sous-problèmes qui ont la même structure que le problème principal.

Dans l'arborescence mentionnée, chaque sous-arbre est un arbre complet en lui-même, tout comme l'arbre principal, et l'arbre principal peut être un sous-arbre dans un autre arbre.

Je suppose donc que votre collègue a découvert les propriétés d'auto-égalité de votre problème d'indexation. Ou bien il a fait l'inverse et a transformé le problème en une représentation à égalité de soi.

Sécurise
la source
1
+1 pour "un problème peut être divisé en sous-problèmes qui ont la même structure que le problème principal"
treecoder
+1 et pour paraphraser: où la solution du problème s'applique aux couches enfants. Mon exemple réel est de trouver des frais de carte de crédit qui contribuent à un «lot». Le logiciel de comptabilité aura les frais individuels et le dépôt par lots sur le compte courant. Mon cas pourrait devenir une question ici car stackoverflow n'était pas trop précis à ce sujet. stackoverflow.com/questions/14719806
Chris K
3

Eh bien, la récursivité peut être facilement comprise si l'on essaie de transformer des boucles impératives en fonctions fonctionnelles. Quoi qu'il en soit, essayons de donner des réponses à toutes les questions:

quels sont les "schémas de problèmes" qui appellent la solution de la récursivité

Si vous avez une structure ou un algorithme en forme d'arbre, vous aurez besoin d'une récursivité. Si votre code impératif concerne une pile, vous aurez besoin d'une récursivité. Si une certaine étape de votre algorithme dépend des étapes précédentes (boucles de réflexion), vous avez besoin d'une récursivité. Le besoin ici doit être interprété comme peut être utilisé.

la récursivité est-elle une forme de stratégie "diviser et conquérir" ou une forme de "réutilisation de code" - ou, est un modèle de conception à part entière

Aucun. Diviser pour régner utilise la récursivité mais peut être implémenté avec des piles. La réutilisation du code fait référence à autre chose. Les modèles de conception sont plus compliqués qu'une simple récursivité.

pouvez-vous nous donner un exemple d'un problème du monde réel où la récursivité vient à l'esprit comme une solution immédiate

Analyse et tout ce qui concerne les structures arborescentes. Même des structures arborescentes implicites.

Mihai Maruseac
la source
3

S'il existe un moyen de le simplifier pour qu'il soit facile, cela peut être l'indice que la récursivité pourrait fonctionner. Vous pouvez prendre le tri et la recherche d'exemples où il existe des solutions récursives telles que Merge Sort et Binary Search respectivement.

Il faut garder à l'esprit comment certains problèmes peuvent être mal résolus avec la récursion comme une factorielle.

En ce qui concerne un exemple du monde réel où j'utiliserais la récursivité, la recherche d'un livre sur une étagère peut être effectuée assez facilement de manière récursive. Je regarde simplement le livre et si ce n'est pas ce que je veux, je passe au suivant. Je m'arrête lorsque je trouve le livre ou que je touche la fin de la ligne. La boucle sur la vérification d'un livre et le passage au suivant pourrait se faire récursivement. C'est peut-être un exemple trop réel.

JB King
la source
2

quels sont les "schémas de problèmes" qui appellent la solution de la récursivité

En termes très généraux, la récursivité est nécessaire lorsque vous résolvez un problème où f (x) = f (g (x)) . Sauf si vous êtes d'accord avec la récursion infinie, g (x) ne devrait pas être évalué à x .

la récursivité est-elle une forme de stratégie "diviser et conquérir" ou une forme de "réutilisation de code" - ou, est un modèle de conception à part entière

Aucune de ces réponses. C'est juste une façon de répéter la même chose, parfois en fonction des variations de l'entrée. Le concept existe depuis bien plus longtemps que les modèles de conception, la réutilisation de code ou même les ordinateurs, d'ailleurs.

pouvez-vous nous donner un exemple d'un problème du monde réel où la récursivité vient à l'esprit comme une solution immédiate

Les factorielles, la séquence de Fibonacci, la traversée des arbres et bien d'autres problèmes peuvent être résolus avec la récusation. La récursivité dans le sens d'une fonction s'appelant elle-même n'est pas nécessairement la meilleure façon d'implémenter ce genre de choses; il existe d'autres façons d'obtenir le même effet (par exemple, une pile et une boucle) qui pourraient être plus souhaitables.

Blrfl
la source
-1

Lorsque vous écrivez un algorithme récursif, vous traduisez généralement une définition récursive du problème dans le code. Ensuite, vous n'avez même pas besoin de savoir comment il sera exécuté.

C'est ce qui se passe dans la programmation fonctionnelle. En fait, vous spécifiez quoi (définition) plutôt que comment . En d'autres termes, vous définissez la base puis définissez votre problème dans le terme d'un sous-problème.

Par exemple, considérez l' Factorialalgorithme

  • Définissez la base: factorielle (1) = 1;
  • Définir factorielle n: factorielle (n) = n * factorielle (n-1);

Ensuite, lorsque vous rencontrez un problème, vous devriez penser si vous pouvez le définir récursivement ou non, si vous pouvez le définir récursivement, vous l'avez presque résolu.

Cependant, toute fonction récursive ne doit pas être une définition récursive. Vous pouvez définir la base et relier (définir) la solution du problème principal à la solution (définition) des sous-problèmes. Mais pour cette relation, vous aurez peut-être besoin d'une procédure.

Un exemple est MergeSortdans lequel mergepourrait être une procédure impérative pour relier la définition ou la solution de tri du tableau entier au tri des sous-tableaux.

Ahmad
la source