Quelle est la récursivité et quand dois-je utiliser?

voix
121

L'un des sujets qui semble venir régulièrement sur les listes de diffusion et des discussions en ligne est le bien-fondé (ou non) de faire une science informatique Degree. Un argument qui semble venir maintes et maintes fois pour la partie négative est qu'ils ont été le codage depuis un certain nombre d'années et ils ont jamais utilisé récursivité.

La question est donc:

  1. Qu'est-ce que récursion?
  2. Quand dois-je utiliser récursion?
  3. Pourquoi ne pas que les gens utilisent récursion?
Créé 06/08/2008 à 03:29
source utilisateur
Dans d'autres langues...                            


40 réponses

voix
86

Il y a un certain nombre de bonnes explications de récursion sur ce sujet, cette réponse est de savoir pourquoi vous ne devriez pas l' utiliser dans la plupart des langues. * Dans la plupart des grandes implémentations de langage impératif (c. -à chaque mise en œuvre majeure de C, C ++, Basic, Python , Ruby, Java et C #) itération est largement préférable de récursivité.

Pour voir pourquoi, à travers les étapes que les langues ci-dessus utilisent pour appeler une fonction:

  1. l' espace est découpé sur la pile pour les arguments et les variables locales de la fonction
  2. Les arguments de la fonction sont copiés dans ce nouvel espace
  3. le contrôle passe à la fonction
  4. Les pistes de code de la fonction
  5. Le résultat de la fonction est copiée dans une valeur de retour
  6. la pile est rembobinée à sa position précédente
  7. le contrôle passe à l'endroit où la fonction a été appelée

Faire toutes ces étapes prend du temps, généralement un peu plus qu'il ne faut pour itérer une boucle. Cependant, le vrai problème est à l' étape 1. Lorsque de nombreux programmes commencent, ils allouent une seule partie de la mémoire pour leur pile, et quand ils manquent de cette mémoire (souvent, mais pas toujours en raison de récursivité), le programme se bloque en raison d'un débordement de pile .

Donc, dans ces langues récursion est plus lent et il vous rend vulnérable à écraser. Il y a encore quelques arguments pour l'utiliser bien. En général, le code écrit récursive est plus courte et un peu plus élégant, une fois que vous savez comment le lire.

Il y a une technique qui implémenteurs linguistiques peuvent utiliser appelé l' optimisation des appels de queue qui peut éliminer certaines classes de débordement de pile. Succinctement: si un retour de la fonction expression est simplement le résultat d'un appel de fonction, vous n'avez pas besoin d'ajouter un nouveau niveau sur la pile, vous pouvez réutiliser l'actuel pour la fonction appelée. Malheureusement, peu de langues-implémentations impératives ont optimisation des appels arrière intégré.

* J'adore récursivité. Ma langue statique préférée ne pas utiliser des boucles du tout, récursivité est la seule façon de faire quelque chose à plusieurs reprises. Je ne pense pas que la récursivité est généralement une bonne idée dans les langues qui ne sont pas accordées pour elle.

** Par ailleurs Mario, le nom typique de votre fonction ArrangeString est « rejoindre », et je serais surpris si votre langue de choix ne possède pas déjà une mise en œuvre de celui-ci.

Créé 06/08/2008 à 06:09
source utilisateur

voix
63

Exemple simple anglais de récursivité.

A child couldn't sleep, so her mother told her a story about a little frog,
    who couldn't sleep, so the frog's mother told her a story about a little bear,
         who couldn't sleep, so the bear's mother told her a story about a little weasel... 
            who fell asleep.
         ...and the little bear fell asleep;
    ...and the little frog fell asleep;
...and the child fell asleep.
Créé 04/05/2010 à 17:38
source utilisateur

voix
49

Dans le plus élémentaire sens de la science informatique, récursivité est une fonction qui appelle elle-même. Disons que vous avez une structure de liste chaînée:

struct Node {
    Node* next;
};

Et vous voulez savoir combien de temps une liste chaînée est que vous pouvez le faire avec récursion:

int length(const Node* list) {
    if (!list->next) {
        return 1;
    } else {
        return 1 + length(list->next);
    }
}

(Cela pourrait bien sûr être fait avec une boucle aussi bien, mais il est utile comme une illustration du concept)

Créé 04/05/2010 à 12:25
source utilisateur

voix
46

A chaque fois qu'une fonction elle-même appelle, créant une boucle, puis c'est récursivité. Comme avec tout ce qu'il ya de bonnes utilisations et les mauvaises utilisations pour récursivité.

L'exemple le plus simple récursion queue où la dernière ligne de la fonction est un appel à lui-même:

int FloorByTen(int num)
{
    if (num % 10 == 0)
        return num;
    else
        return FloorByTen(num-1);
}

Cependant, ceci est un exemple boiteux, presque inutile, car il peut facilement être remplacé par itération plus efficace. Après tout, récursivité souffre de surcharge d'appel de fonction, qui, dans l'exemple ci-dessus pourrait être importante par rapport à l'opération à l'intérieur de la fonction elle-même.

Ainsi , toute raison de le faire récursion plutôt que l' itération devrait être de tirer parti de la pile d'appel à faire des trucs intelligents. Par exemple, si vous appelez une fonction plusieurs fois avec des paramètres différents à l' intérieur de la même boucle alors c'est une façon d'accomplir la ramification . Un exemple classique est le triangle de Sierpinski .

entrez la description d'image ici

Vous pouvez dessiner un de ces très simplement avec récursion, où les branches de la pile d'appels dans 3 directions:

private void BuildVertices(double x, double y, double len)
{
    if (len > 0.002)
    {
        mesh.Positions.Add(new Point3D(x, y + len, -len));
        mesh.Positions.Add(new Point3D(x - len, y - len, -len));
        mesh.Positions.Add(new Point3D(x + len, y - len, -len));
        len *= 0.5;
        BuildVertices(x, y + len, len);
        BuildVertices(x - len, y - len, len);
        BuildVertices(x + len, y - len, len);
    }
}

Si vous essayez de faire la même chose avec l'itération, je pense que vous trouverez cela prend beaucoup plus de code à accomplir.

D'autres cas d'utilisation commune peuvent inclure des hiérarchies, le site Web de traversant, par exemple robots d'exploration, des comparaisons de répertoires, etc.

Conclusion

Sur le plan pratique, récursivité fait le plus de sens à chaque fois que vous avez besoin de branchement itératif.

Créé 04/05/2010 à 14:33
source utilisateur

voix
28

Récursion est une méthode de résolution de problèmes en fonction de la mentalité de diviser pour mieux régner. L'idée de base est que vous prenez le problème d'origine et le diviser en petits cas (plus facilement) de résoudre lui-même, résoudre les cas plus petites (généralement en utilisant le même algorithme à nouveau), puis les réassembler dans la solution finale.

L'exemple canonique est une routine pour générer la factorielle de n. La factorielle de n est calculée en multipliant l'ensemble des nombres compris entre 1 et n. Une solution itérative en C # ressemble à ceci:

public int Fact(int n)
{
  int fact = 1;

  for( int i = 2; i <= n; i++)
  {
    fact = fact * i;
  }

  return fact;
}

Il n'y a rien d'étonnant à la solution itérative et il devrait donner un sens à quiconque est familier avec C #.

La solution récursif se trouve en reconnaissant que la n-ième factorielle est n * Fact (n-1). Ou pour le dire d'une autre façon, si vous savez ce qu'est un nombre factoriel particulier est que vous pouvez calculer la suivante. Voici la solution récursive en C #:

public int FactRec(int n)
{
  if( n < 2 )
  {
    return 1;
  }

  return n * FactRec( n - 1 );
}

La première partie de cette fonction est connue sous le nom d' un scénario de référence (ou parfois la Garde article) et est ce qui empêche l'algorithme de courir pour toujours. Il retourne juste la valeur 1 lorsque la fonction est appelée avec une valeur de 1 ou moins. La deuxième partie est plus intéressante et est connu comme l' étape récursive . Nous appelons ici la même méthode avec un paramètre légèrement modifié (nous décrémentez 1), puis multiplier le résultat avec notre copie n.

Lors de la première rencontre, cela peut être une sorte de confusion il est instructif d'examiner comment cela fonctionne lors de son exécution. Imaginez que nous appelons FactRec (5). Nous entrons dans la routine, ne sont pas repris par le cas de base et nous finissons comme ceci:

// In FactRec(5)
return 5 * FactRec( 5 - 1 );

// which is
return 5 * FactRec(4);

Si nous ré-entrer dans la méthode avec le paramètre 4, nous sommes à nouveau pas arrêté par la clause de garde et nous finissons à:

// In FactRec(4)
return 4 * FactRec(3);

Si nous substituons cette valeur de retour dans la valeur de retour ci-dessus, nous obtenons

// In FactRec(5)
return 5 * (4 * FactRec(3));

Cela devrait vous donner un indice quant à la façon dont la solution finale est arrivée à nous allons donc Accélérez et montrer chaque étape sur le chemin vers le bas:

return 5 * (4 * FactRec(3));
return 5 * (4 * (3 * FactRec(2)));
return 5 * (4 * (3 * (2 * FactRec(1))));
return 5 * (4 * (3 * (2 * (1))));

Cette substitution finale se produit lorsque le scénario de base est déclenchée. À ce stade, nous avons une formule simple algrebraic pour résoudre ce qui équivaut directement à la définition de factorielles en premier lieu.

Il est intéressant de noter que chaque appel à la méthode des résultats dans les deux cas de base étant déclenché ou un appel à la même méthode où les paramètres sont plus proches d'un cas de base (souvent appelé un appel récursif). Si ce n'est pas le cas, alors la méthode fonctionnera toujours.

Créé 06/08/2008 à 03:54
source utilisateur

voix
12

Récursion résout un problème avec une fonction qui appelle elle-même. Un bon exemple de cela est une fonction factoriel. Factoriel est un problème de mathématiques où factoriel 5, par exemple, est 5 * 4 * 3 * 2 * 1. Cette fonction permet de résoudre ce en C # pour les entiers positifs (non testé - il peut y avoir un bug).

public int Factorial(int n)
{
    if (n <= 1)
        return 1;

    return n * Factorial(n - 1);
}
Créé 04/05/2010 à 12:29
source utilisateur

voix
9

Considérons un vieux problème bien connu :

En mathématiques, le plus grand commun diviseur (pgcd) ... de deux ou plusieurs nombres entiers non nuls, est le plus grand nombre entier positif qui divise les nombres sans un reste.

La définition de GCD est étonnamment simple:

définition GCD

où mod est l' opérateur modulo (qui est, le reste après la division entière).

En anglais, cette définition dit le plus grand diviseur commun de tout nombre et zéro est ce nombre, et le plus grand diviseur commun de deux nombres m et n est le plus grand commun diviseur de n et le reste de la division m par n .

Si vous souhaitez savoir pourquoi cela fonctionne, consultez l'article de Wikipédia sur l' algorithme d' Euclide .

Calculons GCD (10, 8) à titre d'exemple. Chaque étape est égale à celle juste avant:

  1. gcd (10, 8)
  2. gcd (10, 10 mod 8)
  3. gcd (8, 2)
  4. gcd (8, 8 mod 2)
  5. gcd (2, 0)
  6. 2

Dans la première étape, 8 ne pas égal à zéro, de sorte que la seconde partie de la définition est applicable. 10 mod 8 = 2 parce que 8 va en 10 fois avec un reste de 2. A l'étape 3, la deuxième partie applique à nouveau, mais cette fois 8 mod 2 = 0 parce que 2 divise 8 sans reste. A l'étape 5, le second argument est 0, donc la réponse est 2.

Avez - vous remarqué que GCD apparaît sur les côtés gauche et à droite du signe égal? Un mathématicien dirait cette définition est récursive parce que l'expression que vous définissez revient dans sa définition.

définitions récursives ont tendance à être élégant. Par exemple, une définition récursive pour la somme d'une liste est

sum l =
    if empty(l)
        return 0
    else
        return head(l) + sum(tail(l))

headle premier élément dans une liste et tailest le reste de la liste. Notez que sumRecurs dans sa définition à la fin.

Peut-être vous préférez la valeur maximale dans une liste à la place:

max l =
    if empty(l)
        error
    elsif length(l) = 1
        return head(l)
    else
        tailmax = max(tail(l))
        if head(l) > tailmax
            return head(l)
        else
            return tailmax

Vous pouvez définir la multiplication des entiers non négatifs récursive pour la transformer en une série d'additions:

a * b =
    if b = 0
        return 0
    else
        return a + (a * (b - 1))

Si ce bit à transformer la multiplication en une série d'additions n'a pas de sens, décompacter quelques exemples simples pour voir comment cela fonctionne.

Tri par fusion a une belle définition récursive:

sort(l) =
    if empty(l) or length(l) = 1
        return l
    else
        (left,right) = split l
        return merge(sort(left), sort(right))

Définitions récursives sont tout autour si vous savez ce qu'il faut chercher. Remarquez comment toutes ces définitions ont des cas de base très simples, par exemple , GCD (m, 0) = m. Le cas récursives rogner le problème de passer aux choses les réponses faciles.

Grâce à cette compréhension, vous pouvez maintenant apprécier les autres algorithmes dans l'article de Wikipédia sur récursion !

Créé 04/05/2010 à 14:58
source utilisateur

voix
9

Récursion fait référence à une méthode qui permet de résoudre un problème en résolvant une version plus petite du problème et en utilisant ce résultat, plus un autre calcul pour formuler la réponse au problème initial. Souvent, dans le processus de résolution de la version plus petite, la méthode résoudront une version encore plus petite du problème, et ainsi de suite, jusqu'à ce qu'il atteigne un « scénario de base » qui est trivial à résoudre.

Par exemple, pour calculer un factoriel pour le nombre X, on peut le représenter comme X times the factorial of X-1. Ainsi, la méthode « récursivement » pour trouver le factoriel X-1, et multiplie ensuite ce qu'il a obtenu par Xdonner une réponse définitive. Bien sûr, pour trouver le factoriel X-1, il va d' abord calculer le factoriel X-2, et ainsi de suite. Le cas de base serait quand Xest 0 ou 1, auquel cas il sait retourner 1depuis 0! = 1! = 1.

Créé 04/05/2010 à 12:26
source utilisateur

voix
9
  1. Une fonction qui appelle elle-même
  2. Lorsqu'une fonction peut être (facilement) décomposé en une simple opération plus la même fonction sur une plus petite partie du problème. Je devrais dire plutôt que cela en fait un bon candidat pour la récursivité.
  3. Ils font!

L'exemple canonique est le factoriel qui ressemble à:

int fact(int a) 
{
  if(a==1)
    return 1;

  return a*fact(a-1);
}

En général, la récursivité est pas nécessairement rapide (frais généraux d'appel de fonction tend à être élevée parce que les fonctions récursives ont tendance à être faible, voir ci-dessus) et peuvent souffrir de certains problèmes (tout le monde de débordement de la pile?). Certains disent qu'ils ont tendance à être difficiles à obtenir « droit » dans les cas non triviales, mais je ne l'achetez pas vraiment dans cela. Dans certaines situations, récursivité fait le plus de sens et est la façon la plus élégante et claire d'écrire une fonction particulière. Il convient de noter que certaines langues favorisent des solutions récursives et optimiser beaucoup plus les (LISP vient à l'esprit).

Créé 06/08/2008 à 03:35
source utilisateur

voix
7

Une fonction récursive est un qui appelle lui-même. La raison la plus courante que j'ai trouvé à l'utiliser est une structure parcourait arbre. Par exemple, si j'ai un TreeView avec des cases à cocher (pensez l'installation d'un nouveau programme, « choisir les fonctionnalités à installer » la page), je pourrais avoir besoin d'un bouton « vérifier tous » qui serait quelque chose comme ça (pseudo-code):

function cmdCheckAllClick {
    checkRecursively(TreeView1.RootNode);
}

function checkRecursively(Node n) {
    n.Checked = True;
    foreach ( n.Children as child ) {
        checkRecursively(child);
    }
}

Ainsi, vous pouvez voir que le checkRecursively vérifie d'abord le noeud qui il est passé, puis appelle lui-même pour chacun des enfants de ce nœud.

Vous avez besoin d'être un peu prudent avec récursivité. Si vous entrez dans une boucle récursive infinie, vous obtiendrez une exception Stack Overflow :)

Je ne peux pas penser à une raison pour laquelle les gens ne devraient pas l'utiliser, le cas échéant. Il est utile dans certaines circonstances, et non dans d'autres.

Je pense que parce qu'il est une technique intéressante, certains codeurs finissent peut-être en utilisant plus souvent qu'ils ne le devraient, sans justification réelle. Cela a donné récursion une mauvaise réputation dans certains milieux.

Créé 06/08/2008 à 03:44
source utilisateur

voix
5

La récursivité est une expression faisant référence directement ou indirectement elle-même.

Considérez les acronymes récursifs comme un exemple simple:

  • GNU signifie Not Unix GNU
  • PHP signifie PHP: Hypertext Preprocessor
  • YAML signifie YAML est pas Markup Language
  • VIN signifie vin est pas un émulateur
  • VISA signifie Visa International Service Association

D'autres exemples sur Wikipédia

Créé 04/05/2010 à 12:56
source utilisateur

voix
5

Voici un exemple simple: combien d'éléments dans un ensemble. (Il y a de meilleures façons de compter les choses, mais c'est un bel exemple simple récursive.)

Tout d'abord, nous avons besoin de deux règles:

  1. si l'ensemble est vide, le nombre d'éléments dans l'ensemble est égal à zéro (duh!).
  2. si l'ensemble est pas vide, le compte est un plus le nombre d'éléments dans l'ensemble après un élément est supprimé.

Supposons que vous ayez un jeu comme celui-ci: [xxx]. nous allons compter le nombre d'articles il y a.

  1. l'ensemble est [xxx] qui n'est pas vide, donc nous appliquons la règle 2. le nombre d'éléments est un plus le nombre d'éléments dans [xx] (par exemple, nous avons supprimé un article).
  2. l'ensemble est [xx], donc nous appliquons la règle 2 fois: un + nombre d'éléments dans [x].
  3. l'ensemble est [x], qui correspond encore la règle 2: un numéro de + d'articles dans [].
  4. Maintenant, le jeu est [], ce qui correspond à la règle 1: le nombre est égal à zéro!
  5. Maintenant que nous connaissons la réponse à l'étape 4 (0), nous pouvons résoudre l'étape 3 (1 + 0)
  6. De même, maintenant que nous connaissons la réponse à l'étape 3 (1), nous pouvons résoudre l'étape 2 (1 + 1)
  7. Et enfin, maintenant que nous connaissons la réponse à l'étape 2 (2), nous pouvons résoudre l'étape 1 (1 + 2) et obtenir le nombre d'éléments dans [xxx], qui est 3. Hourra!

Nous pouvons représenter cela comme:

count of [x x x] = 1 + count of [x x]
                 = 1 + (1 + count of [x])
                 = 1 + (1 + (1 + count of []))
                 = 1 + (1 + (1 + 0)))
                 = 1 + (1 + (1))
                 = 1 + (2)
                 = 3

Lors de l'application d'une solution récursive, vous avez généralement au moins 2 règles:

  • la base, le cas simple qui stipule ce qui se passe quand vous avez « épuisé » toutes vos données. Ceci est habituellement une variante de « si vous êtes hors de données à traiter, votre réponse est X »
  • la règle récursive, qui stipule ce qui se passe si vous avez encore des données. Ceci est généralement une sorte de règle qui dit « faire quelque chose pour rendre votre ensemble de données plus petites, et réappliquer vos règles à l'ensemble de données plus petites. »

Si nous traduisons ci-dessus à pseudocode, nous obtenons:

numberOfItems(set)
    if set is empty
        return 0
    else
        remove 1 item from set
        return 1 + numberOfItems(set)

Il y a beaucoup plus d'exemples utiles (un arbre traversant, par exemple) que je suis sûr que d'autres couvriront.

Créé 06/08/2008 à 04:12
source utilisateur

voix
5

Récursion fonctionne mieux avec ce que j'aime appeler « problèmes fractals », où vous faites affaire avec une grande chose qui a fait des versions plus petites de cette grande chose, dont chacun est une version encore plus petite de la grande chose, et ainsi de suite. Si jamais vous avez à parcourir ou rechercher par quelque chose comme un arbre ou imbriqués structures identiques, vous avez un problème qui pourrait être un bon candidat pour la récursivité.

Les gens évitent récursion pour plusieurs raisons:

  1. La plupart des gens (moi y compris) ont réduit leur dents de programmation sur la programmation procédurale ou orientée objet, par opposition à la programmation fonctionnelle. Pour ces personnes, l'approche itérative (typiquement en utilisant des boucles) se sent plus naturel.

  2. Ceux d'entre nous qui a coupé nos dents de programmation sur la programmation procédurale ou orientée objet ont souvent été dit d'éviter récursion parce qu'il est sujette aux erreurs.

  3. On dit souvent que la récursivité est lente. L'appel et le retour d'une routine implique de façon répétée beaucoup de pile à pousser et à éclater, ce qui est plus lent que la boucle. Je pense que certaines langues gèrent mieux que d'autres, et ces langues sont très probablement pas celles où le paradigme dominant est d'ordre procédural ou orienté objet.

  4. Pour au moins deux langages de programmation que j'ai utilisé, je me souviens avoir entendu les recommandations de ne pas utiliser la récursivité si elle obtient au-delà d'une certaine profondeur, car sa pile est pas profonde.

Créé 06/08/2008 à 04:12
source utilisateur

voix
4

1.) Un procédé est récursive si elle peut appeler lui-même; soit directement:

void f() {
   ... f() ... 
}

ou indirectement:

void f() {
    ... g() ...
}

void g() {
   ... f() ...
}

2.) Quand utiliser récursion

Q: Does using recursion usually make your code faster? 
A: No.
Q: Does using recursion usually use less memory? 
A: No.
Q: Then why use recursion? 
A: It sometimes makes your code much simpler!

3.) Les gens utilisent récursion seulement quand il est très complexe d'écrire du code itératif. Par exemple, les techniques traversal des arbres comme pré-commande, postorder peuvent être faites à la fois itérative et récursive. Mais généralement, nous utilisons récursive en raison de sa simplicité.

Créé 11/03/2014 à 10:47
source utilisateur

voix
4

Une déclaration récurrente est celle dans laquelle vous définissez le processus de ce qu'il faut faire ensuite comme une combinaison des entrées et ce que vous avez déjà fait.

Par exemple, prendre factoriel:

factorial(6) = 6*5*4*3*2*1

Mais il est facile de voir factoriel (6) est également:

6 * factorial(5) = 6*(5*4*3*2*1).

Donc, en général:

factorial(n) = n*factorial(n-1)

Bien sûr, la chose la plus délicate sur récursion est que si vous voulez définir les choses en termes de ce que vous avez déjà fait, il doit y avoir un endroit pour commencer.

Dans cet exemple, nous venons de faire un cas particulier en définissant factoriel (1) = 1.

Maintenant, nous voyons de bas en haut:

factorial(6) = 6*factorial(5)
                   = 6*5*factorial(4)
                   = 6*5*4*factorial(3) = 6*5*4*3*factorial(2) = 6*5*4*3*2*factorial(1) = 6*5*4*3*2*1

Depuis que nous avons défini factoriel (1) = 1, on atteint le « bas ».

D'une manière générale, les procédures récursives ont deux parties:

1) La partie récursive, qui définit une procédure en termes de nouveaux intrants combinés avec ce que vous avez « déjà fait » par la même procédure. (ie factorial(n) = n*factorial(n-1))

2) Une partie de base, ce qui fait en sorte que le processus ne se répète pas toujours en lui donnant un endroit pour commencer (c. -à factorial(1) = 1)

Il peut être un peu déroutant pour obtenir autour de votre tête au début, mais il suffit de regarder un tas d'exemples et il faut tous ensemble. Si vous voulez une compréhension beaucoup plus profonde du concept, étudier l'induction mathématique. De plus, sachez que certaines langues pour optimiser les appels récursifs tandis que d'autres ne le font pas. Il est assez facile de faire des fonctions récursives lent si vous êtes follement ne faites pas attention, mais il y a aussi des techniques pour les rendre dans la plupart des cas performants.

J'espère que cela t'aides...

Créé 04/05/2010 à 14:30
source utilisateur

voix
4

J'aime cette définition:
Dans récursion, une routine permet de résoudre une petite partie d'un problème lui - même, divise le problème en petits morceaux, puis appelle lui - même pour résoudre chacun des morceaux plus petits.

J'aime aussi Steve discussion McConnells de récursion dans le code complet où il critique les exemples utilisés dans les livres Computer Sciences sur Recursion.

Ne pas utiliser la récursivité pour factorielles ou les nombres de Fibonacci

Un problème avec les manuels de sciences informatique est qu'ils présentent des exemples stupides de récursivité. Les exemples typiques sont le calcul d'une factorielle ou le calcul d'une suite de Fibonacci. Récursion est un outil puissant, et il est vraiment stupide de l'utiliser dans l'un de ces cas. Si un programmeur qui a travaillé pour moi utilisé récursion pour calculer un factoriel, j'embaucher quelqu'un d'autre.

Je pensais que c'était un point très intéressant de soulever et peut-être une raison pour laquelle récursion est souvent mal compris.

EDIT: Ce ne fut pas une fouille à la réponse de Dav - Je ne l'avais pas vu cette réponse quand je posté

Créé 04/05/2010 à 12:29
source utilisateur

voix
3

Un exemple: Une définition récursive d'un escalier est: Un escalier se compose de: - une seule étape et un escalier (récursion) - ou seulement une seule étape (terminaison)

Créé 04/05/2010 à 14:34
source utilisateur

voix
3

Eh bien, c'est une définition assez décent que vous avez. Et wikipedia a une bonne définition aussi. Je vais ajouter un autre (probablement pire) définition pour vous.

Quand les gens se réfèrent, ils parlent généralement « récursivité » sur une fonction qu'ils ont écrit ce qui se dit à plusieurs reprises jusqu'à ce qu'il soit fait avec son travail. Récursion peut être utile lors de la traversée des hiérarchies dans les structures de données.

Créé 04/05/2010 à 12:27
source utilisateur

voix
3

Pour récursif sur un problème résolu: ne rien faire, vous avez terminé.
Pour récursif sur un problème ouvert: faire l'étape suivante, récursif puis sur le reste.

Créé 06/08/2008 à 04:32
source utilisateur

voix
2

Ceci est une vieille question, mais je veux ajouter une réponse du point de vue logistique (non du point de vue ou d'un point de vue de la performance correct de l'algorithme).

J'utilise Java pour le travail, et Java ne supporte pas la fonction imbriquée. En tant que tel, si je veux faire récursion, je pourrais avoir à définir une fonction externe (qui existe seulement parce que mon code cogne contre la domination bureaucratique de Java), ou je pourrais avoir à factoriser le code tout à fait (que je déteste vraiment faire).

Ainsi, j'évite souvent récursivité et le fonctionnement de la pile d'utilisation à la place, parce que récursion lui-même est essentiellement une opération de pile.

Créé 30/08/2014 à 11:09
source utilisateur

voix
2

Une fonction récursive est une fonction qui contient un appel à lui-même. Une structure récursive est une structure qui contient une instance de lui-même. Vous pouvez combiner les deux comme une classe récursive. La partie clé d'un élément récurrent est qu'il contient une instance / appel lui-même.

Considérons deux miroirs face à face. Nous avons vu l'effet de l'infini propre qu'ils font. Chaque réflexion est une instance d'un miroir, qui est contenu dans une autre instance d'un miroir, etc. Le miroir contenant un reflet de lui-même est récursivité.

Un arbre de recherche binaire est un bon exemple de programmation de la récursivité. La structure est récursive à chaque noeud contenant 2 instances d'un noeud. Fonctions pour travailler sur un arbre de recherche binaire sont également récursive.

Créé 04/05/2010 à 17:46
source utilisateur

voix
2

En anglais simple: Supposons que vous pouvez faire 3 choses:

  1. Prenez une pomme
  2. Notez les marques de pointage
  3. Nombre de marques de pointage

Vous avez beaucoup de pommes devant vous sur une table et que vous voulez savoir combien de pommes il y a.

start
  Is the table empty?
  yes: Count the tally marks and cheer like it's your birthday!
  no:  Take 1 apple and put it aside
       Write down a tally mark
       goto start

Le processus de répéter la même chose jusqu'à ce que vous avez terminé est appelé récursivité.

J'espère que c'est la réponse « anglais simple » que vous recherchez!

Créé 04/05/2010 à 14:09
source utilisateur

voix
1

La récursion est de « auto-référence » définition la plus simple. Une fonction qui fait référence à lui-même, par exemple appelle lui-même est récursive. La chose la plus importante à garder à l'esprit, est qu'une fonction récursive doit avoir un « scénario de base », soit une condition que si elle est vraie, il cause de ne pas s'appeler et mettre fin ainsi la récursivité. Sinon, vous aurez une récursion infinie:

récursion http://cart.kolix.de/wp-content/uploads/2009/12/infinite-recursion.jpg

Créé 04/05/2010 à 17:10
source utilisateur

voix
1

Récursion est quand vous avez une opération qui utilise lui-même. Il aura probablement un point d'arrêt, sinon il irait pour toujours.

Disons que vous voulez rechercher un mot dans le dictionnaire. Vous avez une opération appelée « consultation » à votre disposition.

Votre ami dit: « Je ne pouvais vraiment une cuillère en pudding maintenant! » Tu ne sais pas ce qu'il veut dire, si vous regardez « cuillère » dans le dictionnaire, et il se lit comme suit quelque chose comme ceci:

Cuillère: nom - un ustensile avec une boule ronde à la fin. Cuillère: verbe - utiliser une cuillère sur quelque chose cuillère: verbe - faire des câlins près derrière

Maintenant, étant que vous êtes vraiment pas bon avec l'anglais, cela vous fait dans la bonne direction, mais vous avez besoin de plus d'informations. Donc, vous sélectionnez « ustensile » et « câlins » pour rechercher pour un peu plus d'informations.

Câlins: verbe - pour se blottir Ustensile: nom - un outil, souvent un ustensile manger

Hey! Vous savez ce que blottir est, et il n'a rien à voir avec le pudding. Vous savez aussi que le pudding est quelque chose que vous mangez, donc il est logique maintenant. Votre ami doit vouloir manger le pudding avec une cuillère.

D'accord, d'accord, ce fut un exemple très boiteux, mais il illustre (peut-être mal) les deux parties principales de récursivité. 1) Il utilise lui-même. Dans cet exemple, vous n'avez pas vraiment regardé un mot de façon significative jusqu'à ce que vous le comprenez, et cela pourrait signifier la recherche en plus de mots. Cela nous amène au point deux, 2) Il arrête quelque part. Il doit avoir une sorte de cas de base. Sinon, vous devriez juste finir par chaque mot dans le dictionnaire, ce qui est probablement pas trop utile. Notre base cas est que vous avez obtenu suffisamment d'informations pour établir une connexion entre ce que vous avez déjà fait et ne comprenait pas.

L'exemple classique qui est donné est factoriel, où 5 factoriel est 1 * 2 * 3 * 4 * 5 (qui est de 120). Le cas de base serait de 0 (ou 1, en fonction). Donc, pour un nombre entier n, vous effectuez les opérations suivantes

n est égal à 0? retourner une autre, revenir n * (factorielle de n-1)

nous allons le faire avec l'exemple de 4 (que nous savons à l'avance est 1 * 2 * 3 * 4 = 24).

factoriel 4 ... 0 est-il? non, donc il doit être 4 * factoriel de 3, mais ce qui est factoriel de 3? il est 3 * factoriel de 2 factoriel de 2 est 2 * factoriel de 1 factoriel de 1 est 1 * factoriel de 0 et nous savons factoriel de 0! :-D il est 1, c'est le factoriel de définition de 1 est 1 * factoriel de 0, ce qui était de 1 ... donc 1 * 1 = 1 factoriel 2 est 2 * factoriel de 1, ce qui était de 1 ... donc 2 * 1 = 2 factoriel 3 est 3 * factorielle de 2, ce qui était 2 ... alors 3 * 2 = 6 factorielle de 4 (enfin !!) est 4 * factorielle de 3, ce qui était 6 ... 4 * 6 est 24

Factoriel est un simple cas de « cas de base et utilise lui-même ».

Maintenant, remarquez que nous travaillions encore sur factoriel de 4 tout le chemin vers le bas ... Si nous voulions factoriel 100, nous aurions dû aller tout le chemin jusqu'à 0 ... ce qui pourrait avoir beaucoup de frais généraux à elle. De la même manière, si nous trouvons un mot obscur pour rechercher dans le dictionnaire, il pourrait prendre regardant d'autres mots et l'analyse des indices de contexte jusqu'à trouver une connexion que nous connaissons. méthodes récursives peuvent prendre beaucoup de temps pour se frayer un chemin à travers. Cependant, quand ils sont utilisés correctement, et compris, ils peuvent faire le travail complexe étonnamment simple.

Créé 04/05/2010 à 17:04
source utilisateur

voix
1

Un grand nombre de problèmes peuvent être considérés en deux types de pièces:

  1. cas de base, qui sont des choses élémentaires que vous pouvez résoudre simplement en regardant les, et
  2. cas récursives, qui construisent un problème plus important de petits morceaux (élémentaires ou autres).

Alors, quelle est une fonction récursive? Eh bien, c'est là que vous avez une fonction qui est définie en termes de lui-même, directement ou indirectement. OK, cela semble ridicule jusqu'à ce que vous vous rendez compte qu'il est raisonnable pour les problèmes du type décrit ci-dessus: vous résolvez les cas de base directement et de traiter les cas récurrents en utilisant des appels récursifs pour résoudre les petits morceaux du problème incorporé dans.

L'exemple classique vraiment de l'endroit où vous avez besoin récursion (ou quelque chose qui sent très bien comme ça) est quand vous avez affaire à un arbre. Les feuilles de l'arbre sont le cas de base, et les branches sont le cas récursif. (Dans pseudo-C.)

struct Tree {
    int leaf;
    Tree *leftBranch;
    Tree *rightBranch;
};

La façon la plus simple d'imprimer ceci afin d'utiliser est récursion:

function printTreeInOrder(Tree *tree) {
    if (tree->leftBranch) {
        printTreeInOrder(tree->leftBranch);
    }
    print(tree->leaf);
    if (tree->rightBranch) {
        printTreeInOrder(tree->rightBranch);
    }
}

Il est mort facile de voir que cela va fonctionner, car il est très clair. (L'équivalent non récurrent est tout à fait beaucoup plus complexe, ce qui nécessite une structure de pile interne pour gérer la liste des choses à traiter.) Eh bien, en supposant que personne ne fait une connexion circulaire bien sûr.

Mathématiquement, le truc à montrer que la récursivité est apprivoisé est de se concentrer sur la recherche d'une mesure de la taille des arguments. Pour notre exemple d'arbre, le plus métrique est la profondeur maximale de l'arbre en dessous du nœud courant. A feuilles, il est égal à zéro. Lors d'une branche avec des feuilles seulement au-dessous, il est l'un, etc. Ensuite, vous pouvez simplement montrer qu'il ya séquence strictement ordonnée sur la taille des arguments que la fonction est invoquée dans le but de traiter l'arbre; les arguments aux appels récursifs sont toujours « moindre » au sens de la métrique que l'argument à l'appel global. Avec une mesure cardinale strictement décroissante, vous triées.

Il est également possible d'avoir une récursion infinie. C'est en désordre et dans de nombreuses langues ne fonctionnera pas parce que la pile explose. (Lorsqu'il fonctionne, le moteur linguistique doit être déterminé que la fonction ne en quelque sorte retourne pas et peut donc d'optimiser l'écart de la tenue de la pile des choses Tricky en général;. Queue récursion est juste la façon la plus triviale de le faire .)

Créé 04/05/2010 à 16:29
source utilisateur

voix
1

La récursivité est la technique de définition d'une fonction, d'un ensemble ou d'un algorithme en fonction de lui-même.

Par exemple

n! = n(n-1)(n-2)(n-3)...........*3*2*1

Maintenant, il peut être défini comme récursive: -

n! = n(n-1)!   for n>=1

En termes de programmation, lorsqu'une fonction ou méthode elle-même appelle à plusieurs reprises, jusqu'à ce qu'une condition spécifique se satisfait, ce processus est appelé récursivité. Mais il doit y avoir une condition de terminaison et de la fonction ou méthode doit pas entrer dans une boucle infinie.

Créé 04/05/2010 à 16:22
source utilisateur

voix
1

Récursion dans le calcul est une technique utilisée pour calculer un effet de résultat ou sur le côté après le retour normal à partir d'une fonction unique (méthode, procédure ou bloc) invocation.

La fonction récursive, par définition, doit avoir la capacité de se prévaloir soit directement, soit indirectement (par l'intermédiaire d'autres fonctions) en fonction d'une condition de sortie ou les conditions ne sont pas remplies. Si une condition de sortie est satisfaite des rendements particuliers d'invocation à sa appelant. Cela continue jusqu'à ce que l'invocation initiale est renvoyée à partir, au moment où l'effet de résultat ou sur le côté désiré sera disponible.

À titre d'exemple, voici une fonction pour exécuter l'algorithme Quicksort à Scala ( copié à partir de l'entrée Wikipedia pour Scala )

def qsort: List[Int] => List[Int] = {
  case Nil => Nil
  case pivot :: tail =>
    val (smaller, rest) = tail.partition(_ < pivot)
    qsort(smaller) ::: pivot :: qsort(rest)
}

Dans ce cas, la condition de sortie est une liste vide.

Créé 04/05/2010 à 15:14
source utilisateur

voix
1

fonction elle-même appeler ou utiliser sa propre définition.

Créé 04/05/2010 à 14:59
source utilisateur

voix
1

Tout algorithme présente structure récursivité sur un type de données se compose essentiellement d'un commutateur déclaration d'un cas pour chaque cas du type de données.

par exemple, lorsque vous travaillez sur un type

  tree = null 
       | leaf(value:integer) 
       | node(left: tree, right:tree)

un algorithme récursif structurel aurait la forme

 function computeSomething(x : tree) =
   if x is null: base case
   if x is leaf: do something with x.value
   if x is node: do something with x.left,
                 do something with x.right,
                 combine the results

ce qui est vraiment le moyen le plus évident d'écrire une algorith qui fonctionne sur une structure de données.

maintenant, quand on regarde les nombres entiers (bien, les nombres naturels) tels que définis à l'aide des axiomes Peano

 integer = 0 | succ(integer)

vous voyez qu'un algorithme récursif structurel sur des entiers ressemble à ceci

 function computeSomething(x : integer) =
   if x is 0 : base case
   if x is succ(prev) : do something with prev

la fonction factoriel trop bien connu est de l'exemple le plus trivial de cette forme.

Créé 04/05/2010 à 14:53
source utilisateur

voix
1

hey, désolé si mon opinion est d'accord avec quelqu'un, je vais juste essayer d'expliquer récursivité en anglais plaine.

supposons que vous avez trois gestionnaires - Jack, John et Morgan. Jack gère 2 programmeurs, John - 3, et Morgan - 5. vous allez donner à chaque gestionnaire 300 $ et que vous voulez savoir quel serait le coût. La réponse est évidente - mais si deux des employés Morgan-s sont également responsables?

ICI est la récursion. vous commencez par le haut de la hiérarchie. le coût est de 0 $ estivaux. vous commencez avec Jack, vérifiez ensuite s'il a des gestionnaires comme employés. si vous trouvez un d'entre eux sont, vérifier s'ils ont des gestionnaires comme employés et ainsi de suite. Ajouter 300 $ au coût d'été chaque fois que vous trouverez un gestionnaire. lorsque vous avez terminé avec Jack, allez à John, ses employés et à Morgan.

Vous ne saurez jamais, comment cycles beaucoup vous allez avant d'obtenir une réponse, mais vous savez combien de gestionnaires que vous avez et combien de budget pouvez-vous dépenser.

Récursion est un arbre, avec des branches et des feuilles, appelées les parents et les enfants respectivement. Lorsque vous utilisez un algorithme récursif, vous êtes plus ou moins consciemment la construction d'un arbre à partir des données.

Créé 04/05/2010 à 13:50
source utilisateur

voix
1

En clair, récursivité signifie répéter someting encore et encore.

Dans la programmation est un exemple d'appeler la fonction en elle-même.

Regardez l'exemple suivant de calcul factoriel d'un nombre:

public int fact(int n)
{
    if (n==0) return 1;
    else return n*fact(n-1)
}
Créé 04/05/2010 à 13:48
source utilisateur

voix
1

Récursion est le processus par lequel un appel de méthode iself pour être en mesure d'exécuter une tâche. Il réduit redundency de code. La plupart des fonctions recurssive ou méthodes doivent avoir un condifiton pour briser le recussive appeler-à-dire l'empêcher de s'appeler si une condition est remplie - ce qui empêche la création d'une boucle infinie. Toutes les fonctions sont adaptées à être utilisées de manière récursive.

Créé 04/05/2010 à 13:42
source utilisateur

voix
1

sa façon de faire des choses encore et indéfiniment de telle sorte que chaque option est utilisée.

par exemple, si vous vouliez obtenir tous les liens sur une page html vous voulez avoir récurrences parce que quand vous obtenez tous les liens à la page 1, vous voulez obtenir tous les liens sur chacun des liens figurant sur la première page. puis pour chaque lien vers un NewPage vous voulez que ces liens et ainsi de suite ... en d'autres termes, il est une fonction qui appelle elle-même à l'intérieur de lui-même.

quand vous faites cela, vous avez besoin d'un moyen de savoir quand arrêter, ou bien vous serez dans une boucle sans fin si vous ajoutez un entier à param la fonction de suivre le nombre de cycles.

en C #, vous aurez quelque chose comme ceci:

private void findlinks(string URL, int reccursiveCycleNumb)    {
   if (reccursiveCycleNumb == 0)
        {
            return;
        }

        //recursive action here
        foreach (LinkItem i in LinkFinder.Find(URL))
        {
            //see what links are being caught...
            lblResults.Text += i.Href + "<BR>";

            findlinks(i.Href, reccursiveCycleNumb - 1);
        }

        reccursiveCycleNumb -= reccursiveCycleNumb;
}
Créé 04/05/2010 à 13:02
source utilisateur

voix
1

« Si j'ai un marteau, tout faire ressembler à un clou. »

Récursion est une stratégie de résolution de problèmes pour d' énormes problèmes, où chaque étape à juste « tourner 2 petites choses dans une plus grande chose » , chaque fois avec le même marteau.

Exemple

Supposons que votre bureau est couvert d'un désordre désorganisé de 1024 papiers. Comment faites-vous une pile soignée, propre des papiers du désordre, en utilisant récursion?

  1. Diviser: Etaler toutes les feuilles, donc vous avez juste une feuille dans chaque « pile ».
  2. Conquérir:
    1. Faites le tour, mettre chaque feuille sur le dessus d'une autre feuille. Vous avez maintenant des piles de 2.
    2. Faire le tour, en mettant chaque 2-pile au-dessus d'un autre 2-pile. Vous avez maintenant des piles de 4.
    3. Faire le tour, en mettant chaque 4-pile au-dessus d'un autre 4-pile. Vous avez maintenant des piles de 8.
    4. ... encore et encore ...
    5. Vous avez maintenant une énorme pile de feuilles 1024!

Notez que ceci est assez intuitif, en dehors de tout comptage (qui est pas strictement nécessaire). Vous ne pouvez pas aller tout le chemin vers les piles 1 feuilles, en réalité, mais vous pouvez et il fonctionne encore. L'important est le marteau: Avec vos bras, vous pouvez toujours mettre une pile au-dessus de l'autre pour faire une plus grande pile, et il n'a pas d'importance (à raison) la taille de la pile est soit.

Créé 04/05/2010 à 12:54
source utilisateur

voix
1

Récursivité qu'il applique à la programmation appelle essentiellement une fonction à l'intérieur de sa propre définition (à l'intérieur lui-même), avec des paramètres différents de manière à accomplir une tâche.

Créé 04/05/2010 à 12:25
source utilisateur

voix
1

Vous voulez l'utiliser quand vous avez une structure arborescente. Il est très utile dans la lecture de XML.

Créé 21/08/2008 à 14:18
source utilisateur

voix
1

Mario, je ne comprends pas pourquoi vous avez utilisé récursion pour cet exemple .. boucle Pourquoi ne pas simplement par chaque entrée? Quelque chose comme ça:

String ArrangeString(TStringList* items, String separator)
{
    String result = items->Strings[0];

    for (int position=1; position < items->count; position++) {
        result += separator + items->Strings[position];
    }

    return result;
}

La méthode ci-dessus serait plus rapide et plus simple. Il n'y a pas besoin d'utiliser la récursivité en place d'une simple boucle. Je pense que ce genre d'exemples est la raison pour laquelle récursion obtient une mauvaise réputation. Même l'exemple de fonction canonique factoriel est mieux mis en œuvre avec une boucle.

Créé 06/08/2008 à 04:19
source utilisateur

voix
0

En fait, la meilleure solution pour récursive factoriel doit être:

int factorial_accumulate(int n, int accum) {
    return (n < 2 ? accum : factorial_accumulate(n - 1, n * accum));
}

int factorial(int n) {
    return factorial_accumulate(n, 1);
}

Parce que cette version est la queue récursive

Créé 21/08/2008 à 14:39
source utilisateur

voix
0

J'utilise récursivité. Qu'est-ce que cela a à voir avec ayant un degré CS ... (que je ne le fais pas, par ailleurs)

Utilisations courantes J'ai trouvé:

  1. Sitemaps - récursifs par système de fichiers à partir de la racine du document
  2. araignées - rampants à travers un site Web pour trouver l' adresse e - mail, des liens, etc.
  3. ?
Créé 06/08/2008 à 04:13
source utilisateur

voix
0

J'ai créé une fonction récursive pour concaténer une liste de chaînes avec un séparateur entre eux. Je l' utilise principalement pour créer des expressions SQL, en passant une liste de champs comme les « éléments » et une « virgule + espace » comme séparateur. Voici la fonction (Il utilise certains types de données natives Borland Builder, mais peut être adapté à tout autre environnement):

String ArrangeString(TStringList* items, int position, String separator)
{
  String result;

  result = items->Strings[position];

  if (position <= items->Count)
    result += separator + ArrangeString(items, position + 1, separator);

  return result;
}

Je l'appelle de cette façon:

String columnsList;
columnsList = ArrangeString(columns, 0, ", ");

Imaginez que vous avez un tableau nommé « champs » avec ces données à l' intérieur: « albumName », « ReleaseDate », « labelId ». Ensuite , vous appelez la fonction:

ArrangeString(fields, 0, ", ");

Comme la fonction commence à fonctionner, la variable « résultat » reçoit la valeur de la position 0 de la matrice, qui est « albumName .

Ensuite, il vérifie si la position qu'il a affaire est le dernier. Comme il n'est pas, alors il concaténer le résultat avec le séparateur et le résultat d'une fonction, qui, ô Dieu, est-ce même fonction. Mais cette fois-ci, le vérifier, il s'appeler en ajoutant 1 à la position.

ArrangeString(fields, 1, ", ");

Il ne cesse de répéter, la création d'une pile LIFO, jusqu'à ce qu'il atteigne un point où la position traitée est la dernière, de sorte que la fonction ne retourne que l'élément de cette position sur la liste, pas plus concaténer. Ensuite, la pile est concaténé en arrière.

Je l'ai? Si vous ne le faites pas, j'ai une autre façon de l'expliquer. : O)

Créé 06/08/2008 à 04:00
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more