Exemples concrets de récursivité [fermé]

96

Quels sont les problèmes du monde réel où une approche récursive est la solution naturelle en plus de la recherche en profondeur d'abord (DFS)?

(Je ne considère pas la tour de Hanoi , le nombre de Fibonacci ou les problèmes factoriels du monde réel. Ils sont un peu artificiels dans mon esprit.)

Peter Mortensen
la source
2
Merci pour toutes les suggestions, mais tout le monde suggère des traversées d'arbres / réseaux. Les thèses sont essentiellement tous des exemples de Depth-First-Search (ou BFS je suppose). Je cherchais d'autres algorithmes / problèmes bien motivés.
redfood
10
J'aime cette question! "Dites-moi toutes les utilisations de la technique X, SAUF l'utilisation pratique principale de la technique X"
Justin Standard
1
J'utilise la récursivité tout le temps, mais généralement pour les maths et les graphiques. J'essaye de chercher des exemples de récursion qui seraient significatifs pour les non-programmeurs.
redfood
6
Choisissez vos propres romans d'aventures! Je veux tout lire, et la récursivité est la meilleure façon de le faire.
Andres
Il n'y a pas de récursion dans le monde réel. La récursivité est une abstraction mathématique. Vous pouvez modéliser beaucoup de choses en utilisant la récursivité. En ce sens, Fibonacci est absolument réel, car il y a pas mal de problèmes du monde réel qui peuvent être modélisés de cette façon. Si vous pensez que Fibonacci n'est pas du monde réel, je dirais que tous les autres exemples sont également des abstractions, pas des exemples du monde réel.
Zane

Réponses:

41

Il y a beaucoup d'exemples mathématiques ici, mais vous vouliez un exemple du monde réel , donc avec un peu de réflexion, c'est peut-être le meilleur que je puisse offrir:

Vous trouvez une personne qui a contracté une infection contagieuse donnée, qui n'est pas mortelle, et se corrige rapidement (type A), sauf pour une personne sur 5 (nous les appellerons de type B) qui en devient définitivement infectée et ne montre aucun symptômes et agit simplement comme un épandeur.

Cela crée des vagues de ravages assez ennuyeux chaque fois que le type B infecte une multitude de types A.

Votre tâche est de retrouver tous les types B et de les vacciner pour arrêter l'épine dorsale de la maladie. Malheureusement, vous ne pouvez pas administrer un remède national à tous, car les personnes de type A sont également allergiques mortelles au remède qui fonctionne pour le type B.

La façon dont vous le feriez serait la découverte sociale, étant donné qu'une personne infectée (type A), choisit tous ses contacts la semaine dernière, en marquant chaque contact sur un tas. Lorsque vous testez qu'une personne est infectée, ajoutez-la à la file d'attente de «suivi». Lorsqu'une personne est de type B, ajoutez-la au «suivi» en tête (car vous voulez arrêter ce jeûne).

Après avoir traité une personne donnée, sélectionnez la personne en tête de la file d'attente et appliquez la vaccination si nécessaire. Obtenez tous leurs contacts précédemment non consultés, puis testez pour voir s'ils sont infectés.

Répétez jusqu'à ce que la file d'attente des personnes infectées devienne 0, puis attendez une autre épidémie.

(Ok, c'est un peu itératif, mais c'est une façon itérative de résoudre un problème récursif, dans ce cas, une première traversée d'une population en essayant de découvrir des chemins probables vers des problèmes, et en plus, les solutions itératives sont souvent plus rapides et plus efficaces , et j'enlève de manière compulsive la récursion partout à tel point qu'elle devient instinctive. .... bon sang!)

Kent Fredric
la source
2
Merci - il s'agit toujours d'une traversée de graphes, mais elle est bien motivée et a du sens pour les non-programmeurs.
redfood
Je pense que trouver le patient 0 serait un meilleur exemple. Déterminez toutes les interactions qui auraient pu causer une infection. Répétez pour toutes les personnes impliquées qui étaient contagieuses au moment de l'interaction jusqu'à ce qu'aucune contagieuse ne soit trouvée
William FitzPatrick
4
cet exemple du monde réel semble si familier maintenant :(
haroldolivieri
109

Un exemple réel de récursivité

Un tournesol

Hans Sjunnesson
la source
12
codé avec récursion par l'architecte Matrix :)
Marcel
3
Comment est-ce récursif? Bien sûr, c'est joli. Mais récursif? Un chou fractal aurait bien fonctionné, mais je ne vois pas d'auto-similitudes dans cette fleur.
Clément
1
Eh bien, c'est un peu langue dans la joue, mais c'est un exemple de phyllotaxie, qui peut être décrite avec la séquence de Fibonacci, qui est généralement implémentée par récursivité.
Hans Sjunnesson le
1
"couramment implémenté par récursivité" ne signifie pas nécessairement que la fleur le fait. C'est peut-être le cas; Je ne suis pas assez biologiste moléculaire pour le savoir, mais sans une explication sur la façon dont cela fonctionne, je ne vois pas cela comme particulièrement utile. Le vote négatif. Si vous souhaitez ajouter une description (qu'elle soit biologiquement exacte ou non, cela pourrait donner un aperçu) pour l'accompagner, je reviendrai volontiers sur le vote.
lindes
65

Que diriez-vous de tout ce qui concerne une structure de répertoires dans le système de fichiers. Recherche récursive de fichiers, suppression de fichiers, création de répertoires, etc.

Voici une implémentation Java qui imprime de manière récursive le contenu d'un répertoire et de ses sous-répertoires.

import java.io.File;

public class DirectoryContentAnalyserOne implements DirectoryContentAnalyser {

    private static StringBuilder indentation = new StringBuilder();

    public static void main (String args [] ){
        // Here you pass the path to the directory to be scanned
        getDirectoryContent("C:\\DirOne\\DirTwo\\AndSoOn");
    }

    private static void getDirectoryContent(String filePath) {

        File currentDirOrFile = new File(filePath);

        if ( !currentDirOrFile.exists() ){
            return;
        }
        else if ( currentDirOrFile.isFile() ){
            System.out.println(indentation + currentDirOrFile.getName());
            return;
        }
        else{
            System.out.println("\n" + indentation + "|_" +currentDirOrFile.getName());
            indentation.append("   ");

            for ( String currentFileOrDirName : currentDirOrFile.list()){
                getPrivateDirectoryContent(currentDirOrFile + "\\" + currentFileOrDirName);
            }

            if (indentation.length() - 3 > 3 ){
                indentation.delete(indentation.length() - 3, indentation.length());
            }
        }       
    }

}
Adrien Be
la source
2
Un système de fichiers fournit de la motivation (ce qui est bien, merci) mais c'est un exemple spécifique de DFS.
redfood
4
Je n'ai pas reçu l'acronyme «DFS» - cela faisait un moment que je ne me suis pas assis dans une salle de classe.
Matt Dillard
5
recherche en profondeur d'abord: dfs (nœud) {pour chaque enfant dans le nœud {visite (enfant); }}
Haoest
Pour un exemple de code simple, voir par exemple stackoverflow.com/questions/126756/…
Jonik
Y a-t-il une erreur dans ce code? GetPrivateDirectoryContent () ne devrait-il pas être remplacé par getDirectoryContent ()?
Shn_Android_Dev
16

L'exemple de Matt Dillard est bon. Plus généralement, toute marche dans un arbre peut généralement être gérée par récursivité très facilement. Par exemple, compiler des arbres d'analyse, parcourir XML ou HTML, etc.

Cody Brocious
la source
Je trouve que "compiler des arbres d'analyse" est une réponse sensée, qui implique des arbres mais n'est toujours pas un problème de recherche, comme le souhaite le demandeur. Il pourrait être généralisé à une notion générale de compilation ou d'interprétation d'une langue. Il peut également s'agir d '«interpréter» (comprendre, écouter) une langue naturelle, par exemple l'anglais.
imz - Ivan Zakharyaschev
16

La récursivité est souvent utilisée dans les implémentations de l' algorithme Backtracking . Pour une application "réelle" de ceci, que diriez-vous d'un solveur de Sudoku ?

bkane
la source
Un tableau d'état et un bas peuvent accélérer cela.
BCS
13

La récursivité est appropriée chaque fois qu'un problème peut être résolu en le divisant en sous-problèmes, qui peuvent utiliser le même algorithme pour les résoudre. Les algorithmes sur les arbres et les listes triées sont un ajustement naturel. De nombreux problèmes de géométrie informatique (et de jeux 3D) peuvent être résolus de manière récursive en utilisant des arbres de partitionnement d'espace binaire (BSP), des subdivisions grasses ou d'autres moyens de diviser le monde en sous-parties.

La récursivité est également appropriée lorsque vous essayez de garantir l'exactitude d'un algorithme. Étant donné une fonction qui prend des entrées immuables et renvoie un résultat qui est une combinaison d'appels récursifs et non récursifs sur les entrées, il est généralement facile de prouver que la fonction est correcte (ou non) en utilisant une induction mathématique. Il est souvent impossible de faire cela avec une fonction itérative ou avec des entrées qui peuvent muter. Cela peut être utile lorsqu'il s'agit de calculs financiers et d'autres applications où l'exactitude est très importante.

Sam
la source
11

Sûrement que de nombreux compilateurs utilisent fortement la récursivité. Les langages informatiques sont eux-mêmes intrinsèquement récursifs (c'est-à-dire que vous pouvez incorporer des instructions «if» dans d'autres instructions «if», etc.).

Martin Côté
la source
Intégré si les instructions ne sont pas récursives.
John Meagher le
Mais leur analyse nécessite une récursivité, John.
Apocalisp le
2
John, le fait que vous puissiez imbriquer des instructions if signifie que la définition du langage (et probablement l'analyseur de langage) est récursif.
Derek Park
La descente récursive est l'un des moyens les plus simples de coder manuellement un compilateur. Pas aussi simple que d'utiliser un outil comme yacc, mais plus facile à comprendre comment cela fonctionne. Toutes les machines à états pilotées par table peuvent être expliquées, mais finissent généralement par être des boîtes noires.
Eclipse
La réponse de Cody Brocious mentionnant "la compilation d'arbres d'analyse" a également pointé vers ce domaine: l'analyse du langage / l'interprétation / la compilation.
imz - Ivan Zakharyaschev
9

Désactivation / définition de la lecture seule pour tous les contrôles enfants dans un contrôle conteneur. J'avais besoin de le faire parce que certains des contrôles enfants étaient eux-mêmes des conteneurs.

public static void SetReadOnly(Control ctrl, bool readOnly)
{
    //set the control read only
    SetControlReadOnly(ctrl, readOnly);

    if (ctrl.Controls != null && ctrl.Controls.Count > 0)
    {
        //recursively loop through all child controls
        foreach (Control c in ctrl.Controls)
            SetReadOnly(c, readOnly);
    }
}
chitza
la source
8

Célèbre cycle d' évaluation / application du SICP

texte alternatif
(source: mit.edu )

Voici la définition de eval:

(define (eval exp env)
  (cond ((self-evaluating? exp) exp)
        ((variable? exp) (lookup-variable-value exp env))
        ((quoted? exp) (text-of-quotation exp))
        ((assignment? exp) (eval-assignment exp env))
        ((definition? exp) (eval-definition exp env))
        ((if? exp) (eval-if exp env))
        ((lambda? exp)
         (make-procedure (lambda-parameters exp)
                         (lambda-body exp)
                         env))
        ((begin? exp) 
         (eval-sequence (begin-actions exp) env))
        ((cond? exp) (eval (cond->if exp) env))
        ((application? exp)
         (apply (eval (operator exp) env)
                (list-of-values (operands exp) env)))
        (else
         (error "Unknown expression type - EVAL" exp))))

Voici la définition de appliquer:

(define (apply procedure arguments)
  (cond ((primitive-procedure? procedure)
         (apply-primitive-procedure procedure arguments))
        ((compound-procedure? procedure)
         (eval-sequence
           (procedure-body procedure)
           (extend-environment
             (procedure-parameters procedure)
             arguments
             (procedure-environment procedure))))
        (else
         (error
          "Unknown procedure type - APPLY" procedure))))

Voici la définition de eval-sequence:

(define (eval-sequence exps env)
  (cond ((last-exp? exps) (eval (first-exp exps) env))
        (else (eval (first-exp exps) env)
              (eval-sequence (rest-exps exps) env))))

eval-> apply-> eval-sequence->eval

jfs
la source
7

La récursivité est utilisée dans des choses comme les arbres BSP pour la détection de collision dans le développement de jeux (et dans d'autres domaines similaires).

marque
la source
7

Les gens trient souvent des piles de documents en utilisant une méthode récursive. Par exemple, imaginez que vous triez 100 documents avec des noms dessus. Placez d'abord les documents en piles par la première lettre, puis triez chaque pile.

La recherche de mots dans le dictionnaire est souvent effectuée par une technique de type recherche binaire, qui est récursive.

Dans les organisations, les patrons donnent souvent des commandes aux chefs de service, qui à leur tour donnent des commandes aux gestionnaires, etc.

Tkerwin
la source
5

Exigence du monde réel que j'ai récemment reçue:

Condition A: implémentez cette fonctionnalité après avoir bien compris l'exigence A.

dragon
la source
4

Les analyseurs et compilateurs peuvent être écrits dans une méthode de descente récursive. Ce n'est pas la meilleure façon de le faire, car des outils comme lex / yacc génèrent des analyseurs plus rapides et plus efficaces, mais conceptuellement simples et faciles à implémenter, ils restent donc courants.

davenpcj
la source
4

La récursivité est appliquée aux problèmes (situations) où vous pouvez le diviser (le réduire) en parties plus petites, et chaque partie ressemble au problème d'origine.

De bons exemples de choses qui contiennent des parties plus petites similaires à lui-même sont:

  • structure arborescente (une branche est comme un arbre)
  • listes (une partie d'une liste est toujours une liste)
  • conteneurs (poupées russes)
  • séquences (une partie d'une séquence ressemble à la suivante)
  • groupes d'objets (un sous-groupe est encore un groupe d'objets)

La récursivité est une technique pour continuer à décomposer le problème en morceaux de plus en plus petits, jusqu'à ce que l'un de ces morceaux devienne suffisamment petit pour être un morceau de gâteau. Bien sûr, après les avoir décomposés, vous devez ensuite «assembler» les résultats dans le bon ordre pour former une solution totale à votre problème initial.

Certains algorithmes de tri récursifs, algorithmes de marche dans les arbres, algorithmes de cartographie / réduction, division-pour-conquérir sont tous des exemples de cette technique.

En programmation informatique, la plupart des langages de type retour d'appel basés sur la pile ont déjà les capacités intégrées pour la récursivité: ie

  • décomposer le problème en plus petits morceaux ==> s'appeler sur un sous-ensemble plus petit des données d'origine),
  • suivre la façon dont les pièces sont divisées ==> pile d'appels,
  • recoudre les résultats ==> retour basé sur la pile
Stephen Chung
la source
4

J'ai un système qui utilise la récursivité de queue pure à quelques endroits pour simuler une machine à états.

Peter Mortensen
la source
4

Quelques bons exemples de récursivité se trouvent dans les langages de programmation fonctionnels . Dans les langages de programmation fonctionnels ( Erlang , Haskell , ML / OCaml / F # , etc.), il est très courant que tout traitement de liste utilise la récursivité.

Lorsqu'il s'agit de listes dans des langages de style OOP impératifs typiques, il est très courant de voir des listes implémentées sous forme de listes liées ([item1 -> item2 -> item3 -> item4]). Cependant, dans certains langages de programmation fonctionnelle, vous constatez que les listes elles-mêmes sont implémentées de manière récursive, où la "tête" de la liste pointe vers le premier élément de la liste, et la "queue" pointe vers une liste contenant le reste des éléments ( [élément1 -> [élément2 -> [élément3 -> [élément4 -> []]]]]). C'est assez créatif à mon avis.

Cette gestion des listes, lorsqu'elle est combinée avec la correspondance de modèles, est TRÈS puissante. Disons que je veux additionner une liste de nombres:

let rec Sum numbers =
    match numbers with
    | [] -> 0
    | head::tail -> head + Sum tail

Cela dit essentiellement "si nous avons été appelés avec une liste vide, retournez 0" (nous permettant de casser la récursivité), sinon retournez la valeur de head + la valeur de Sum appelée avec les éléments restants (d'où notre récursivité).

Par exemple, je pourrais avoir une liste d' URL , je pense séparer toutes les URL vers lesquelles chaque URL renvoie, puis je réduis le nombre total de liens vers / depuis toutes les URL pour générer des "valeurs" pour une page (une approche que Google prend avec PageRank et que vous pouvez trouver défini dans l'article original de MapReduce ). Vous pouvez également le faire pour générer le nombre de mots dans un document. Et beaucoup, beaucoup, beaucoup d'autres choses aussi.

Vous pouvez étendre ce modèle fonctionnel à n'importe quel type de code MapReduce où vous pouvez prendre une liste de quelque chose, le transformer et renvoyer quelque chose d'autre (que ce soit une autre liste ou une commande zip sur la liste).

Jason Olson
la source
3

XML, ou traversant tout ce qui est un arbre. Bien que, pour être honnête, je n'utilise pratiquement jamais la récursivité dans mon travail.

Charles Graham
la source
Pas même la récursivité de la queue?
Apocalisp le
J'ai utilisé la récursivité une fois dans ma carrière, et quand le cadre a changé, je m'en suis débarrassé. 80% de ce que nous faisons est CRUD.
Charles Graham
1
Mentionner «XML» en premier lieu est assez étrange. Ce n'est pas une chose naturelle, pas quelque chose qu'une personne ordinaire à qui vous allez enseigner doit gérer dans la vie de tous les jours. Mais l'idée est bien sûr tout à fait sensée.
imz - Ivan Zakharyaschev
3

Boucles de rétroaction dans une organisation hiérarchique.

Le supérieur hiérarchique demande aux dirigeants de recueillir les commentaires de tous les membres de l'entreprise.

Chaque cadre rassemble ses subordonnés directs et leur dit de recueillir les commentaires de leurs subordonnés directs.

Et sur toute la ligne.

Les personnes sans rapport direct - les nœuds feuilles de l'arborescence - donnent leur avis.

Les commentaires remontent dans l'arborescence, chaque gestionnaire ajoutant ses propres commentaires.

Finalement, tous les commentaires reviennent au boss supérieur.

C'est la solution naturelle car la méthode récursive permet un filtrage à chaque niveau - le classement des doublons et la suppression des retours offensants. Le supérieur hiérarchique pourrait envoyer un e-mail global et demander à chaque employé de lui faire rapport directement, mais il y a les problèmes «vous ne pouvez pas gérer la vérité» et «vous êtes viré», donc la récursivité fonctionne mieux ici.

marque
la source
2

Supposons que vous construisez un CMS pour un site Web, où vos pages sont dans une structure arborescente, la racine étant par exemple la page d'accueil.

Supposons également que votre {utilisateur | client | client | patron} vous demande de placer un fil d'Ariane sur chaque page pour montrer où vous vous trouvez dans l'arborescence.

Pour n'importe quelle page n donnée, vous voudrez peut-être marcher jusqu'au parent de n, et à son parent, et ainsi de suite, de manière récursive pour construire une liste de nœuds jusqu'à la racine de l'arborescence des pages.

Bien sûr, vous frappez la base de données plusieurs fois par page dans cet exemple, vous voudrez peut-être utiliser un alias SQL où vous recherchez page-table comme a, et page-table à nouveau comme b, et joignez a.id avec b.parent pour que la base de données effectue les jointures récursives. Cela fait un moment, donc ma syntaxe n'est probablement pas utile.

Là encore, vous souhaiterez peut-être ne calculer cela qu'une seule fois et le stocker avec l'enregistrement de page, en le mettant à jour uniquement si vous déplacez la page. Ce serait probablement plus efficace.

Bref, c'est mon 0,02 $

Sam McAfee
la source
2

Vous avez une arborescence d'organisation de N niveaux de profondeur. Plusieurs nœuds sont vérifiés et vous souhaitez étendre uniquement les nœuds qui ont été vérifiés.

C'est quelque chose que j'ai réellement codé. C'est agréable et facile avec la récursivité.

MagicKat
la source
2

Dans mon travail, nous avons un système avec une structure de données générique qui peut être décrite comme un arbre. Cela signifie que la récursivité est une technique très efficace pour travailler avec les données.

Le résoudre sans récursivité nécessiterait beaucoup de code inutile. Le problème avec la récursivité est qu'il n'est pas facile de suivre ce qui se passe. Vous devez vraiment vous concentrer lorsque vous suivez le déroulement de l'exécution. Mais quand cela fonctionne, le code est élégant et efficace.

Tommy
la source
2

Calculs pour la finance / physique, tels que les moyennes composées.

Apocalisp
la source
2
  • Analyse d'un fichier XML .
  • Recherche efficace dans des espaces multidimensionnels. Par exemple. quad-arbres en 2D, oct-arbres en 3D, kd-arbres, etc.
  • Classification hiérarchique.
  • À bien y penser, traverser toute structure hiérarchique se prête naturellement à la récursivité.
  • La métaprogrammation de modèle en C ++, où il n'y a pas de boucles et la récursivité est le seul moyen.
Dima
la source
«XML» n'est pas essentiel pour l'idée de cette réponse (et mentionner spécifiquement XML peut être dégoûtant / ennuyeux pour les personnes que vous enseignez). N'importe quel langage typique (un langage informatique ou un langage naturel) servirait d'exemple pour un problème d'analyse récursive.
imz - Ivan Zakharyaschev
L'affiche demandait "des problèmes du monde réel où une approche récursive est la solution naturelle". L'analyse d'un fichier xml est certainement un problème du monde réel, et elle se prête naturellement à la récursivité. Le fait que vous sembliez avoir une étrange aversion pour XML ne change rien au fait qu'il est très largement utilisé.
Dima
2

Le meilleur exemple que je connaisse est le tri rapide , c'est beaucoup plus simple avec la récursivité. Jeter un coup d'œil à:

shop.oreilly.com/product/9780596510046.do

www.amazon.com/Beautiful-Code-Leading-Programmers-Practice/dp/0596510047

(Cliquez sur le premier sous-titre sous le chapitre 3: "Le plus beau code que j'aie jamais écrit").

Fabio Ceconello
la source
1
Et MergeSort est également plus simple avec la récursivité.
Matthew Schinckel
1
Le lien est rompu. Pouvez-vous ajouter le titre du livre?
Peter Mortensen
@PeterMortensen, le livre est Beautiful Code de Greg Wilson et Andy Oram. J'ai mis à jour le lien, bien qu'il semble qu'O'Reilly ne permette plus d'y jeter un coup d'œil. Mais vous pouvez jeter un œil à Amazon.
Fabio Ceconello
1

Les entreprises de téléphonie et de câble maintiennent un modèle de leur topologie de câblage, qui est en fait un grand réseau ou un graphique. La récursivité est un moyen de parcourir ce modèle lorsque vous souhaitez rechercher tous les éléments parents ou tous les éléments enfants.

Étant donné que la récursivité est coûteuse du point de vue du traitement et de la mémoire, cette étape n'est généralement effectuée que lorsque la topologie est modifiée et le résultat est stocké dans un format de liste pré-ordonné modifié.

Steve Moyer
la source
1

Le raisonnement inductif, le processus de formation de concept, est de nature récursive. Votre cerveau le fait tout le temps, dans le monde réel.

Apocalisp
la source
1

Idem le commentaire sur les compilateurs. Les nœuds de l'arborescence de syntaxe abstraite se prêtent naturellement à la récursivité. Toutes les structures de données récursives (listes chaînées, arbres, graphiques, etc.) sont également plus faciles à gérer avec la récursivité. Je pense que la plupart d'entre nous n'utilisons pas beaucoup la récursivité une fois que nous sommes sortis de l'école à cause des types de problèmes du monde réel, mais il est bon d'en être conscient comme une option.

origine
la source
1

La multiplication des nombres naturels est un exemple réel de récursivité:

To multiply x by y
  if x is 0
    the answer is 0
  if x is 1
    the answer is y
  otherwise
    multiply x - 1 by y, and add x
Apocalisp
la source