web-dev-qa-db-fra.com

Comment fonctionne la fonction récursive fibonacci?

Je suis nouveau dans Javascript et je le lisais quand je suis arrivé à un chapitre qui décrivait la récursivité des fonctions. Il a utilisé un exemple de fonction pour trouver le nième numéro de la séquence de Fibonacci. Le code est comme suit:

function fibonacci(n) {
   if (n < 2){
     return 1;
   }else{
     return fibonacci(n-2) + fibonacci(n-1);
   }
}

console.log(fibonacci(7));
//Returns 21

J'ai du mal à comprendre exactement ce que fait cette fonction. Quelqu'un peut-il expliquer ce qui se passe ici? Je suis bloqué sur la 5ème ligne, où la fonction s'appelle. Qu'est-ce qu'il se passe ici?

58
opes

Vous définissez une fonction en fonction d'elle-même. En général, fibonnaci(n) = fibonnaci(n - 2) + fibonnaci(n - 1). Nous représentons simplement cette relation dans le code. Ainsi, pour fibonnaci(7) nous pouvons observer:

  • fibonacci(7) est égal à fibonacci(6) + fibonacci(5)
  • fibonacci(6) est égal à fibonacci(5) + fibonacci(4)
  • fibonacci(5) est égal à fibonacci(4) + fibonacci(3)
  • fibonacci(4) est égal à fibonacci(3) + fibonacci(2)
  • fibonacci(3) est égal à fibonacci(2) + fibonacci(1)
  • fibonacci(2) est égal à fibonacci(1) + fibonacci(0)
  • fibonacci(1) est égal à 1
  • fibonacci(0) est égal à 1

Nous avons maintenant toutes les pièces nécessaires pour évaluer fibonacci(7), ce qui était notre objectif initial. Notez que le cas de base - return 1 quand n < 2 - c'est ce qui rend cela possible. C'est ce qui arrête la récursivité, afin que nous puissions commencer le processus de déroulement de la pile et de sommation des valeurs que nous retournons à chaque étape. Sans cette étape, nous continuerions d'appeler fibonacci sur des valeurs de plus en plus petites jusqu'à ce que le programme plante définitivement.

Il peut être utile d'ajouter quelques instructions de journalisation qui illustrent cela:

function fibonacci(n, c) {
    var indent = "";
    for (var i = 0; i < c; i++) {
        indent += " ";
    }
    console.log(indent + "fibonacci(" + n + ")");
    if (n < 2) {
        return 1;
    } else {
        return fibonacci(n - 2, c + 4) + fibonacci(n - 1, c + 4);
    }
}

console.log(fibonacci(7, 0));

Sortie:

fibonacci(7)
    fibonacci(5)
        fibonacci(3)
            fibonacci(1)
            fibonacci(2)
                fibonacci(0)
                fibonacci(1)
        fibonacci(4)
            fibonacci(2)
                fibonacci(0)
                fibonacci(1)
            fibonacci(3)
                fibonacci(1)
                fibonacci(2)
                    fibonacci(0)
                    fibonacci(1)
    fibonacci(6)
        fibonacci(4)
            fibonacci(2)
                fibonacci(0)
                fibonacci(1)
            fibonacci(3)
                fibonacci(1)
                fibonacci(2)
                    fibonacci(0)
                    fibonacci(1)
        fibonacci(5)
            fibonacci(3)
                fibonacci(1)
                fibonacci(2)
                    fibonacci(0)
                    fibonacci(1)
            fibonacci(4)
                fibonacci(2)
                    fibonacci(0)
                    fibonacci(1)
                fibonacci(3)
                    fibonacci(1)
                    fibonacci(2)
                        fibonacci(0)
                        fibonacci(1)

Les valeurs au même niveau d'indentation sont additionnées pour produire le résultat du niveau d'indentation précédent.

87
Wayne Burkett

Il y a beaucoup de bonnes réponses ici, mais j'ai fait ce diagramme qui aide à mieux expliquer le résultat de la fonction. Les seules valeurs qui seront renvoyées sont 1 ou 0 (votre exemple renvoie 1 pour n <2, mais devrait plutôt renvoyer n).

Cela signifie que chaque appel récursif finira par renvoyer un 0 ou un 1. Ceux-ci finissent par être "mis en cache" dans la pile et "reportés" dans l'invocation d'origine et ajoutés ensemble.

Donc, si vous dessiniez ce même diagramme pour chaque valeur de 'n', vous pourriez trouver manuellement la réponse.

Ce diagramme illustre approximativement comment chaque fonction est renvoyée pour fib (5).

![Fibonacci Javascript Tree Diagram

Cela montre le flux de contrôle, c'est-à-dire l'ordre d'exécution des fonctions. N'oubliez pas que le code est toujours exécuté gauche-> droite et haut-> bas. Ainsi, chaque fois qu'une nouvelle fonction est appelée, elle est suspendue, puis l'invocation suivante se produit.

Ce qui suit illustre le flux de contrôle réel basé sur votre message d'origine. Veuillez noter que la condition de base est if (n <= 0) {return 0} else if (n <= 2) {return 1;} pour simplifier:

1. fib(5) {
    return fib(4) + fib(3);
2.   fib(4) {
      return fib(3) + fib(2);
3.     fib(3) {
        return fib(2) + fib(1);
4.       fib(2) {
A=        return 1;
         };
5.       fib(1) {
B=        return 1;
         };
C=      return 2; // (1 + 1)
       };
6.     fib(2) {
D=      return 1;
       };
E=    return 3; // (2 + 1)
     };
7.   fib(3) {
      return fib(2) + fib(1);
8.     fib(2) {
F=      return 1;
       };
9.     fib(1) {
G=      return 1;
       };
H=    return 2; // (1 + 1)
     };
I=  return 5; // (3 + 2)
   };
28
Jeff Callahan

Étape 1) Lorsque fibonacci(7) est appelée, imaginez ce qui suit (remarquez comment j'ai changé tous les n en 7):

function fibonacci(7) {
    if (7 < 2){
        return 1;
    }else{
        return fibonacci(7-2) + fibonacci(7-1);
    }
}

Étape 2) Puisque (7 < 2) Est manifestement faux, nous allons à fibonacci(7-2) + fibonacci(7-1); qui se traduit par fibonacci(5) + fibonacci(6); Puisque fibonacci(5) vient en premier, qui est appelé (change les n à 5 cette fois):

function fibonacci(5) {
    if (5 < 2){
        return 1;
    }else{
        return fibonacci(5-2) + fibonacci(5-1);
    }
}

Étape 3) Et ou bien fibonacci(6) est également appelé, donc ce qui s'est passé est que tout le monde appelle fibonacci 2 nouveaux fibonacci sont appelés.

Visualisation:

      fibonacci(7)
      ____|_____
     |          |
fibonacci(5)  fibonacci(6)
____|____     ____|_____
|        |    |         |
fib(3)  fib(4) fib(4)   fib(5)

Vous voyez comment ça se ramifie? Quand est-ce que ça va s'arrêter? Lorsque n devient inférieur à 2, c'est pourquoi vous avez if (n < 2). À ce stade, la ramification s'arrête et tout est ajouté.

20
Jesse Good

Espérons que ce qui suit aide. Appel:

fibonacci(3)

arrivera à la ligne 5 et fera:

return fibonacci(1) + fibonacci(2);

la première expression rappelle la fonction et renvoie 1 (puisque n < 2).

Le second rappelle la fonction, arrive à la 5ème ligne et fait:.

return fibonacci(0) + fibonacci(1);

les deux expressions renvoient 1 (puisque n < 2 pour les deux), donc cet appel à la fonction renvoie 2.

La réponse est donc 1 + 2, ce qui est 3.

5
RobG

Je pense que ces deux fonctions m'ont donné une explication beaucoup plus claire de la récursivité (à partir de cela article de blog ):

function fibDriver(n) {
  return n === 0 ? 0 : fib(0, 1, n);
}

function fib(a, b, n) {
  return n === 1 ? b : fib(b, a + b, n-1);
}
3
Sl4rtib4rtf4st
 
 /*[.____.▶* Étapes de récursivité de Fibonacci 
 * 1) 3 est passé. (3 est imprimé à l'écran pendant cet appel) 
 * 2) Fibonacci A obtient des diminutions de 2 et la récursivité se produit en passant 1 comme paramètre. (1 est imprimé à l'écran pendant cet appel) 
 * 3) Fibonacci A frappe le cas de base en retournant 1 et il "se déroule". (Pas de récursion ici) 
 * 4) Fibonacci B est appelé, décrémentant la valeur précédente de n (3 était la valeur précédente de n avant que A ne fasse l'appel de retour) à 2. (2 est imprimé à l'écran pendant cet appel) 
 * 5) Fibonacci A est appelé à nouveau en soustrayant 2 de n (2-2 = 0) et passe 0 en paramètre. (1 est imprimé à l'écran pendant cet appel car il est converti de 0) 
 * 6) Fibonacci A frappe le cas de base et se "déroule" (pas de récursion ici) 
 * 7) Fibonacci B est appelé en soustrayant 1 de 2 (2 était la valeur précédente de n avant que A ne fasse l'appel de retour) et passe 1 en paramètre. (1 est imprimé à l'écran pendant cet appel) 
 * 7) Fibonacci B frappe maintenant le cas de base, en retournant 1 et "se déroule" (pas de récursion ici) 
 * 8) Fibonacci B retrace c'est recule à travers tous les appels de fonction précédents et les valeurs de n (n = 2 dans notre cas) et les ajoute à la copie de n = 1 stockée dans sa portée locale 
 * 9) Une fois que Fibonacci B a terminé le " déroulant ", il renvoie la valeur calculée à l'appelant d'origine (pas de récursion ici) 
 
 Remarque * 
 Chaque instance de récursivité Fibonacci crée sa propre portée et stocke la valeur retournée dans une copie de n (dans notre cas 1). 
 Comme la fonction "déroule", elle exécute le code suivant qui reçoit la valeur de n à ce moment. (toutes les fonctions qui appellent d'autres fonctions "se déroulent" lors des appels précédents une fois qu'elles reviennent) 
 
 Dans le dernier appel de notre exemple Fibonacci, Fibonacci B reçoit la valeur de n = 2 comme Fibonaccci A déroule "puisque c'était la dernière valeur avant de faire l'appel de retour. 
 Une fois que Fibonacci B a atteint le cas de base et" déroulé ", il est revenu sur toutes les valeurs précédentes de n (dans notre cas, juste n = 2) et les a ajoutés à sa copie locale de n = 1. 
 
 * Le résultat en passant le chiffre 3 est: 
 3 
 1 
 2 
 1 
 1 
 (3) 
 */
var div = document.getElementById('fib');

function fib( n, c ) {
  var indent = "";
  for (var i = 0; i < c; i++) {
    indent += " ";
}
  var v = n===0 ? 1 : n
  var el = document.createElement('div'),
  text = indent + "fibonacci(" + v + ")";
  el.innerHTML = text;
  div.appendChild(el);
  if(n<2){
     return 1;
  } 
  return fib(n-2, c + 4)  + fib(n-1, c + 4);

}

2
Bill Pope

Pour calculer le nième nombre de fibonacci, la relation est F(n) = F(n-2) + F (n-1).

Si nous implémentons la relation dans le code, pour le nième nombre, nous calculons le (n-2) ème et (n-1) ème numéro en utilisant la même méthode.

Chaque numéro suivant est la somme des deux numéros précédents. Ainsi, le septième nombre est la somme des sixième et cinquième nombres. Plus généralement, le nième nombre est la somme de n - 2 et n - 1, tant que n> 2. Comme les fonctions récursives ont besoin d'une condition d'arrêt pour arrêter la récurrence, ici n <2 est la condition.

f (7) = F(6) + F (5);

à son tour, F(6) = F(5) + F (4)

F (5) = F(4) + F (3) ... ça continue jusqu'à n <2

F (1) renvoie 1

2
Sunil

La fonction s'appelle elle-même. C'est simplement la définition d'une fonction récursive. Dans la 5ème ligne, il transfère l'exécution à lui-même en passant des paramètres qui aboutiront à une valeur.

Pour s'assurer qu'une fonction récursive ne se transforme pas en boucle sans fin, il doit y avoir une sorte de condition qui ne le fait pas s'appelle elle-même. Le but de votre code dans la question est d'effectuer les calculs d'une séquence de fibonacci.

1
user596075

Partage d'un code plus simple pour fib dans JS/ES6 en utilisant la récursivité.

   function fib(n, first = 1, second = 1) {
    if (n <= 2) return 1;
    [first, second] = [second, first + second];
    return (n - 2 === 1) ? second : fib(n - 1, first, second);
    }

console.log(fib(10));
0
Sumer

Algorithme de Fibonacci avec fonction récursive basée sur ES6

const fibonacci = ( n, k = 1, fib2 = 0, fib1 = 1 ) => {
  return k === n ? 
    (() => { return fib1; })() 
    : 
    (() => {
      k++;
      return fibonacci(n, k, fib1, fib1 + fib2);
    })();  
}
console.info(' fibonacci ' + 11 + ' = ' + fibonacci(11));
0
Roman

regardez, fib est

t (n) = t (n - 1) + n;

si n = 0 alors 1

alors voyons comment fonctionne la récursivité, je remplace simplement n dans t(n) par n-1 et ainsi de suite. ça ressemble:

t (n-1) = t (n - 2) + n + 1;

t (n-1) = t (n - 3) + n + 1 + n;

t (n-1) = t (n - 4) + n + 1 + n + 2 + n;

.

.

.

t (n) = t (n-k) + ... + (n-k-3) + (n-k-2) + (n-k-1) + n;

nous savons que t(0)=(n-k) est égal à 1 alors n-k=0 donc n=k nous remplaçons k par n:

t (n) = t (n-n) + ... + (n-n + 3) + (n-n + 2) + (n-n + 1) + n;

si nous omettons n-n alors:

t (n) = t (0) + ... + 3 + 2 + 1 + (n-1) + n;

donc 3+2+1+(n-1)+n est un nombre naturel. il calcule comme Σ3+2+1+(n-1)+n = n(n+1)/2 => n²+n/2

le résultat pour fib est: O(1 + n²) = O(n²)

C'est la meilleure façon de comprendre la relation récursive

0