Je suis juste nouveau dans cette récursion et je sais au moins que c'est une technique par laquelle une méthode s'appelle pendant l'exécution mais je suis assez confus sur la façon dont elle
D'après le livre à l'école, la mise en œuvre de la fonction factorielle est le premier exemple cela a été donné.
//int n = 10;
private static void printNumbers(int n) {
if(n >= 1) {
printNumbers(n - 1);
System.out.print(n + " ");
}
}
Je comprends comment cette factorielle fonctionne d'une manière ou d'une autre, mais je ne suis pas complètement sûr de l'avoir bien compris. Pour chaque appel auquel la méthode se réfère, n est multiplié par le paramètre factoriel n - 1 jusqu'à atteindre 1 .
C'est ainsi que je trace cette récursion dans mon esprit
5 * factorial(5 - 1) = 20 //first call 20 * factorial(4 - 1) = 60 //second call 60 * factorial(3 - 1) = 120 //third call 120 * factorial(2 - 1) = still 120 // fourth call 120 is the last returned value;
Et un autre exemple qui a été donné est l'impression des nombres de 1 à n en utilisant la récursivité au lieu d'utiliser la boucle ordinaire.
//int n = 5;
private static int factorial(int n) {
if(n == 0) {
return 1;
}
else
return n * factorial(n - 1);
}
Ceci renvoie: 1 2 3 4 5 6 7 8 9 10
Et maintenant, le truc me confond ici est pourquoi la sortie commence à 1 ? Parce que d'après ce que je comprends, la sortie doit commencer à 9 jusqu'à 1.
Parce que d'après ce que j'ai compris, au début le n = 10 puis 10> 1 donc bien sûr il s'appellera à nouveau puis 10 - 1 puis imprimera 9 puis appelle lui-même et 9 - 1 puis imprimez 8 et ainsi de suite ...
Quelqu'un peut-il me clarifier cela simplement et corriger ma compréhension erronée pour les exemples que j'ai mentionnés, je suis un peu confus ici et la plupart des articles que je peux voir ici sur StackOverflow sur la récursivité n'aident pas vraiment (mais s'il y a une réponse vraiment bonne et claire sur la récursivité ici que vous connaissez, donnez-moi le lien).
Merci ...
3 Réponses :
Parce qu'en fait rien ne se passe avant que le dernier appel à factorial () ne renvoie 1 . En interne, tous les résultats intermédiaires sont «empilés» sur la pile puis «empilés» lorsque la fonction revient.
Donc en fait, un appel à factorial (3) fait
factorial(0) = 1 factorial(1) = 1 * (1) factorial(2) = 2 * (1 * (1)) factorial(3) = 3 * (2 * (1 * (1))) factorial(4) = 4 * (3 * (2 * (1 * (1)))) ...
Une fois que la factorielle (0) est finalement revenue, toutes les multiplications sont cumulées:
1 * 1 * 2 * 3 = 6
Ou écrites dans l'autre sens:
factorial(3) = 3 * factorial(2) -->> 3 on the heap factorial(2) = 2 * factorial(1) -->> 2,3 on the heap factorial(1) = 1 * factorial(0) -->> 1,2,3 on the heap factorial(0) = 1
Chaque parenthèse ouvrante est un appel de fonction qui pousse une valeur sur la pile, et les multiplications ne peuvent se produire que lorsque les valeurs gauche et droite sont connues, ce qui c'est quand il atteint 0 et renvoie 1.
Sur la pile .
@ user207421 Je savais que c'était l'inverse! Merci.
La récursivité n'est en aucun cas un concept facile - la plupart des gens ont des problèmes avec elle, vous n'êtes donc pas seul.
Penser récursivement à un problème signifie que certains calculs sont répétés jusqu'à ce que le problème soit résolu (ie le cas de base est atteint ou la méthode est épuisée. Pour le Factoriel, c'est quand n = 0, pour le nombre print, la méthode est épuisé [ne fait rien]). Lorsque cela se produit, le calcul le plus récent est renvoyé au niveau de récursivité jusqu'à ce qu'il ne reste plus de niveaux. Ainsi, le problème est résolu.
Voici une analogie: pensez à ce processus comme si vous faisiez du minage. Le calcul (ou la procédure) pour le minage (en gros) consiste à creuser jusqu'à ce que vous trouviez ce que vous voulez (disons des gemmes). Vous commencez à la surface de la Terre. Y a-t-il des gemmes? Cette question est votre cas de base: vous arrêterez de miner lorsque vous trouverez vos gemmes. S'il n'y a pas de gemmes, vous devez creuser plus profondément (descendre d'un niveau), vous appelez donc la procédure d'extraction. Vous vous demandez à nouveau, y a-t-il des gemmes? S'il n'y a pas de gemmes, vous devez creuser plus profondément, etc. Cela se répétera, ou se répétera, jusqu'à ce que vous puissiez répondre à la question par «Oui! J'ai trouvé des pierres précieuses. Une fois le scénario de base atteint, vous êtes au niveau le plus bas du processus, n'est-ce pas? Lors de l'exploitation minière, une fois que vous avez trouvé les gemmes, vous ne pouvez pas rester au fond de la mine. Vous devez retourner au sommet avec les gemmes que vous avez.
C'est la récursion en un mot. Maintenant, pour répondre à votre compréhension:
Vous avez raison avec le conditionnel, 10> = 1, alors appelez printNumbers (10 - 1), et ainsi de suite. Cependant, il imprime avec 1 car c'était la première chose retournée. Si vous regardez le code, vous remarquerez que printNumbers (10 - 1) est appelé avant System.out.println (n + "") ;. Cela signifie qu'avant l'impression de 10, printNumbers exécute 9. Lorsque nous arrivons à printNumbers (1), voici ce qui se passe:
C'est vraiment tout ce qu'il y a à faire. En cas de confusion, faites-le moi savoir!
Ok, alors que s'est-il passé après printNumbers (1) ? Comment a-t-il imprimé (2) après cela? Parce que je pensais que les paramètres printNumbers () devraient être n - 1 , alors pourquoi a-t-il été incrémenté à nouveau à deux?
@robert le paramètre printNumbers () est n-1; cependant, la raison pour laquelle il semble incrémenter (distinction importante - il n'incrémente pas réellement) est qu'après printNumbers (1) [aka printNumbers (2 - 1) - donc n = 2 ici] est terminé, nous devons encore terminer la code pour quand n = 2. Une fois que printNumbers (2 - 1) est appelé, le système imprime n sur la console. Ce cycle se répète jusqu'à ce que le premier n entré (n = 10) soit imprimé, à quel point il n'y a plus rien à faire. Cela vous a-t-il aidé?
Je suppose que cette question concerne plus l'ordre des calculs et pas tant la compréhension de l'idée de récursivité.
Avec la récursivité, les choses se passent à l'inverse de ce que vous avez répertorié. Cela fonctionne comme une pile au lieu d'une file d'attente. Si vous avez joué à des jeux de cartes comme MTG, cela fonctionne comme dans les jeux. La dernière chose "activée" est faite en premier.
En gros, la première fois que vous appelez la fonction factorielle, elle se bloque à ce stade:
//int n = 10;
private static void printNumbers(int n) {
if(n >= 1) {
printNumbers(n - 1);
System.out.print(n + " ");
}
}
Parce que votre Le programme attend maintenant le résultat de factoriel (n - 1) pour continuer.
Il continuera d'appeler des sous-programmes (qui, s'ils sont assez grands, peut provoquer une exception de dépassement de pile, le nom de ce site après).
Finalement, n sera égal à 0, et cette branche exécutera:
return n * factorial(n - 1); //been waiting forever...
Donc, en haut de votre stack, vous aurez factorial(1) en attente du résultat de factoriel (0) .
return 3 * 2; //2 replaces factorial(2), now ready to return.
Maintenant que 0 est pris en charge, 1 est prêt à renvoyer sa valeur.
return 2 * 1; //ready to return!
Nous sommes maintenant à N = 2, toujours coincé au même endroit.
return 2 * factorial(1); //need factorial(1) to continue!
Après le retour factoriel (1), nous obtenons:
return 1 * 1; //1 replaces factorial(0), now we can return!
Puis à N = 3 (ou factorial(3))
return 1 * factorial(0); //come on hurry up factorial(0)!
... et ainsi de suite jusqu'à
return 1;
Je n'ai plus de questions concernant le premier exemple sur les factorielles, je pense que je l'ai un peu mieux compris maintenant, mais ce que je suis vraiment confus, c'est à propos du deuxième exemple que j'ai écrit ...
@robert c'est la même idée. La récursivité fonctionne dans le sens inverse. Chaque fois que vous appelez PrintNumbers (N), vous devez d'abord remplir PrintNumbers (N - 1). Aucune magie impliquée, vous avez déclaré dans le corps de la méthode que, pour compléter la méthode, il faut d'abord faire PrintNumbers (N-1). Essayez de mettre l'instruction d'impression au-dessus de PrintNumbers (N-1) et elle fera ce que vous attendez.
ouais j'ai déjà essayé de mettre l'instruction print avant l'appel récursif mais je suis juste curieux de savoir si la mettre après un appel récursif, et j'ai pensé au début qu'elle s'imprimerait à partir de 9 à 1 , mais comme la plupart des gars ici qui ont répondu ont dit qu'il terminerait l'appel récursif avant de passer à l'instruction d'impression, alors je me demande comment cela s'est-il passé de 1 à 2 et ainsi activé puisque le paramètre est n - 1 ?
@robert comme mon explication pour les factorielles. Si vous compreniez vraiment l'exemple factoriel, vous ne seriez pas confus maintenant. Je vous propose de passer par l'exécution du code ligne par ligne. J'ai également modifié ma réponse.
Parcourez le code ligne par ligne. Où est l'instruction d'impression et où est l'appel récursif?
Souvenez-vous que ces appels de méthode s'empilent, le premier ne revient pas de l'appel récursif tant que tous les appels imbriqués ne sont pas terminés.
Si vous rencontrez des problèmes de récursivité, lisez simplement ceci Depuis le début.
votre trace doit être
factorielle (5) = 5 * factorielle (5-1);factoriel (5) = 5 * (4 * factoriel (4-1));factorial (5) = 5 * (4 * (3 * factorial (3-1))et ainsi de suite@chrylis dans le deuxième exemple, comme je l'ai dit, je pensais que
ncommence à10puis effectue son appel récursif puis-1et le La ligne suivante est l'instruction d'impression dont le résultat est9. Ou appelle-t-il à nouveau la méthode et omet l'instruction d'impression jusqu'à ce qu'elle atteigne1? Je ne suis pas vraiment sûr, je suis un peu confus sur le deuxième exemple lolil appelle d'abord
printNumbers (9-1)avant d'imprimer le9; mais, encore une fois,printNumbers (8)appelleprintNumbers (8-1)avant d'imprimer le7; et ainsi de suite jusqu'àsiéchoue@ElliottFrisch Je viens de comprendre votre point: D
@CarlosHeuberger vous dérange d'élaborer le deuxième exemple dans une réponse plutôt ici en commentaire? Merci
Je pense qu'il y a suffisamment de bonnes réponses à la question