Suite de Fibonacci et nombres de Lucas

Bonjour !
La suite de Fibonacci notée F et la suite des nombres de Lucas notée L, on peut exprimer F ainsi :

Fn+x = Fn * (2Fx-1 + 2Fx) + (Fn-x2 / Fn-x)

= Fn * Lx + (Fn-x2 / Fn-x)

P. S : L'élévation au carré de y, puis sa division par y, a pour seul effet de transformer y en nombre positif si y était un nombre négatif.

Réponses

  • Bonjour,

    Est-ce une conjecture ? Un travail à vérifier ? Une découverte ?
    As-tu une question ?

    J’ai un lien qui est dans ce sujet : http://mathematiques.ac-dijon.fr/sources/seances_tuic/fibonacci_lucas.pdf

    Cordialement

    Dom
  • Bonjour,

    $F_n=\dfrac{L_n+2\,L_{n-1}}{5}$

    $L_n$ est le $n$ième nombre de Lucas (leur suite est 2, 1, 3, 4, 7, 11, ...)
    $F_n$ est le $n$ième nombre de Fibonacci (leur suite est 0, 1, 1, 2, 3, 5, ...)
  • Une recherche sur Wikipédia permet de trouver l'expression fonctionnelle de la suite de Fibonacci, c'est-à-dire une expression, nommée formule de Binet, telle que le calcul de $F_n$ pour $n$ donné ne requiert pas la connaissance des termes qui le précèdent :

    $F_n=\dfrac{\phi^n-\phi'\,^n}{\sqrt{5}}\:,\,avec\;\phi=\dfrac{\sqrt{5}+1}{2}\:et\;\phi'=-\dfrac{1}{\phi}$

    Source
  • Bonsoir,

    Dom : Je n'avais simplement pas trouvé de propriété qui permettait de calculer le n+x-ième terme, alors j'ai cherché à en faire une car calculer n+1 n'était vraiment pas optimal.

    Wilfrid : ta première formule pour obtenir Fn est intéressante, mais malheureusement elle se sert de Fn pour définir Fn... Étant donné que on peut remplacer Ln par 2Fn + 2Fn-1.
    Autrement dit, elle n'est utile que si on dispose de la suite L jusqu'à n, sinon elle revient à calculer Fn via Fn entre autres opérations.

    Par contre la formule de Binet est très bien, magnifique même, et du reste bien plus efficace que la propriété que j'ai trouvé. Merci

    Amicalement
  • Arckz a écrit:
    la formule de Binet est très bien, magnifique même, et du reste bien plus efficace que la propriété que j'ai trouvé.

    Si tu as aimé la formule de Binet tu aimeras sans doute cette version simplifiée :

    $F_n=\dfrac{(1+\sqrt{5})^n-(1-\sqrt{5})^n}{2^n\,\sqrt{5}}$

    Ou encore :

    $F_n=\text{round}\left(\dfrac{3.236068\,^n-(-1.236068)^n}{2^n\times 2.236068}\right)$
  • Même pour $n$ (très) grand ?

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • @ev,
    Il semble que non. Je viens d'essayer avec $n=123456789$ dans Mathematica, mais il renvoie le message suivant :

    1/(4543170558802696801122117464338363959885422126968813033367047830453
    742071806061727508951786<<37164014>>503369939849482723619490243515696
    79249904251945699453963897651175685705197588355204473946112)
    is too small to be represented as a normalized machine number. Precision may be lost.

    J'ai ensuite essayé avec la formule originelle de Binet, et voici ce que j'ai obtenu :

    1/(3.23607^123456789) is too small to be represented as a normalized machine number. Precision may be lost.

    Donc dans tous les cas ça ne fonctionne pas avec $n$ (très) grand.

    PS : dans ces formules, originelle et simplifiée, $n\ge 0$, par conséquent il ne s'agit pas réellement de $n$ en tant que rang de $F_n$ dans sa suite, sauf si on considère comme certains que la suite de Fibonacci débute par 1, 1, ... et que $F_0=0$, ce qui fait de $0$ une valeur particulière.
  • Je fais joujou. Je vais faire calculer $X^n $ modulo $X^2 -(X+1)$, ce qui fournira $F_nX + F_{n-1}$. On commence petit.
    [color=#000000]> Q := RationalField() ;        
    > QX<X> := PolynomialRing(Q) ;
    > F := X^2 - (X+1) ;
    > Modexp(X, 7, F) ;  // X^7 modulo F
    13*X + 8
    > Fibonacci(7), Fibonacci(7-1) ;
    13 8
    [/color]
    
    Même chose avec $n = 10^4$. Je ne montre pas le résultat $F_n$ mais seulement son nombre de chiffres en base 10
    [color=#000000]> time G := Modexp(X, 10^4, F) ;
    Time: 0.000
    > time Coefficient(G,1) eq Fibonacci(10^4) ;
    true
    Time: 0.000
    > Ilog(10, Z!Coefficient(G,1)) ;            
    2089
    [/color]
    
    Play again with $n = 10^7$
    [color=#000000]> time G := Modexp(X, 10^7, F) ;            
    Time: 0.550
    > time Coefficient(G,1) eq Fibonacci(10^7) ;
    true
    Time: 0.340
    > Ilog(10, Z!Coefficient(G,1)) ;            
    2089876
    [/color]
    
    Voici le $n$ du post précédent et son nombre de chiffres décimaux.
    [color=#000000]> n ;
    123456789
    > Ilog(10, n) ;                             
    8
    [/color]
    
    Est ce que c'est vraiment raisonnable de vouloir faire calculer $F_n$ ? Qui c'est qui paie ?
    [color=#000000]> time Fn := Fibonacci(n) ;
    Time: 6.010
    > time Ilog(10, Fn) ;
    25800942
    Time: 10.620
    [/color]
    
    Tiens, c'est plus rapide de calculer $F_n$ que de calculer le nombre de chiffres décimaux de $F_n$. Rappel : je suis en mode ``joujou'' donc, en principe, je ne peux pas être exclus pour m'être incrusté dans ce fil. Je dis bien en principe.
  • En fait, aucune machine ne peut gérer un entier plus grand que 2^(le nombre de bits qu'elle utilise pour le représenter)-1. Dans Mathematica j'ai pu calculer $F_{77}=5 527 939 700 884 758$. A partir de $F_{78}$ j'ai un message d'erreur. Il n'est donc pas indispensable de se demander si ça fonctionne avec de grandes valeurs de $n$, parce que ça ne peut tout simplement pas fonctionner.

    EDIT : à partir de $F_{78}$ Mathematica renvoie le résultat en notation scientifique, soit $8.94439\times 10^{15}$. Le message d'erreur venait du fait que je tentais de le forcer à afficher le résultat sous forme d'un entier.
  • Un programme Python (pas commenté, on est en shtam, merdre !) qui crache les décimales (dans l'ordre si vous me posez la question) de $F_{2^{27}}$. Le tout dans un temps raisonnable, sachant que ma babasse commence à accumuler les heures de vol.
    a=1
    b=1
    c=0
    f=0
    l=27
    for k in range(l):
        a,b,c=a*a+b*b,b*(a+c),b*b+c*c
    print(b)
    

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • [suite du précédent]

    En utilisant la fonction native de Mathematica, Fibonacci(n), il semble qu'il n'y ait pas de limite à $n$. Par exemple, Fibonacci[450] = 4953967011875066473162524925231604047727791871346061001150551747313593851366517214899257280600, un nombre de 94 chiffres.

    C'est en utilisant la formule de Binet que le logiciel est limité à n = 77, sans doute à cause de la présence de $\sqrt{5}$. Du coup je me demande comment il calcule Fibonacci(n), et sur combien de bits il représente les entiers, parce que – pour reprendre l'exemple ci-dessus –, Fibonacci(123456789) est un nombre de 25800943 chiffres !!
  • Pour calculer $F_{123456789}$ un code Python. J'ai eu la flemme d'écrire la conversion en binaire.
    Je ne l'ai pas essayé.
    Je vais le lancer, si vous voyez de la fumée, c'est sûrement chez moi.
    L=[1,1,1,0,1,0,1,1,0,1,1,1,1,0,0,1,1,0,1,0,0,0,1,0,1,0,1]
    a=1
    b=0
    c=1
    l=len(L)
    for k in range(l):
        a,b,c=a*a+b*b,b*(a+c),b*b+c*c
        if L[k]==1:
            a,b,c=a+b,a,b
    print(b)
    
    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • Résultat des courses : un peu moins de huit heures...

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • ev
    Donc le programme retranscrit en C++ cela devrait prendre moins de 2h... et probablement avec un $F_n$ encore plus grand...

    [Inutile de reproduire le message précédent. AD]
  • Je ne sais pas avec C ou C++ mais avec Sage, cela va incomparablement plus vite. In:
    def f():
        L=[1,1,1,0,1,0,1,1,0,1,1,1,1,0,0,1,1,0,1,0,0,0,1,0,1,0,1]
        a=1
        b=0
        c=1
        l=len(L)
        for k in range(l):
            a,b,c=a*a+b*b,b*(a+c),b*b+c*c
            if L[k]==1:
                a,b,c=a+b,a,b
        return b
    
    time b = f()
    
    Out:
    CPU times: user 3.58 s, sys: 12 ms, total: 3.6 s
    Wall time: 3.6 s
    
    Puis :
    sage: time log(b*1.,10).floor()+1
    CPU times: user 171 µs, sys: 3 µs, total: 174 µs
    Wall time: 182 µs
    25800943
    
    NB : On peut aussi prendre le chemin des écoliers.
    sage: time len(str(b))
    CPU times: user 11.5 s, sys: 3.79 ms, total: 11.5 s
    Wall time: 11.5 s
    25800943
    
  • @ev,

    Je ne connais rien à Python, mais je comprends néanmoins que L contient les 27 chiffres de 123456789 en base 2, et que ce code produit 27 entiers à croissance très rapide (sans réellement saisir la logique derrière lui) : 1, 1, 2, 8, 13, 377, 610, 987, 2178309, 3524578, 5702887, 9227465, 14930352, 498454011879264, ..., $F_{9383}$ (un nombre de 1961 chiffres). Le dernier résultat n'était-il pas sensé être $F_{123456789}$ plutôt que $F_{9383}$ ?

    Voici la version Mathematica de cet algorithme, avec lequel j'ai obtenu les résultats ci-dessus, pour le cas où j'aurais mal interprété Python :

    algoEv.png

    Je précise que le format de If est If(condition, vrai, faux). A ce sujet, si if ... else existait en Python ça éviterait de calculer deux fois de suite la valeur de a, b, c lorsque L[k] = 1. A noter également que le premier terme d'une liste (ici L) possède l'indice 1, et non pas 0 comme dans la plupart des langages de programmation et de script.
  • Wilfrid a écrit:
    A ce sujet, si if ... else existait en Python ça éviterait de calculer deux fois de suite la valeur de a, b, c lorsque L[k] = 1.

    C'est peut-être là mon erreur d'interprétation du code Python ! Faut-il réellement calculer a, b, c deux fois de suite lorsque L[k] = 1 ?

    Je viens de tester ceci dans Mathematica après avoir modifié mon algorithme en conséquence :

    Last[algoEv[]] - Fibonacci[123456789]

    Si le dernier entier produit par l'algorithme modifié est bien $F_{123456789}$, alors le résultat aurait dû être 0. Ce n'est pas le cas, il est au contraire beaucoup plus grand que 0.

    De toute façon c'est idiot. Les 27 entiers produits sont exactement les mêmes dans les deux cas, il s'agit bien d'un défaut de conception du code.
  • @ Wilfrid
    Effectivement, on peut rogner un peu de temps de calcul en dissociant les deux cas. Mais comme le calcul dans le cas \( \tt L[k]==1 \) est une bête addition, je ne vois pas trop l'intérêt. Maintenant je vais quand même essayer... avec une valeur raisonnable. Je ne vais pas non plus me servir de ma babasse pour faire un barbecue.

    @ Kate.
    Woaouh, Sage, ça déchire son langage !
    Si je savais l'installer, j'abandonnerais Python et ses farces taquines tout de suite.

    amicalement,

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • ev a écrit:
    Maintenant je vais quand même essayer... avec une valeur raisonnable.

    Je viens de le faire avec $F_{123}$. $L={1,1,1,1,0,1,1}$. J'obtiens les 7 entiers 1, 1, 2, 3, 21, 34, 55. Or, $F_{123}$ = 22698374052006863956975682. Entre ce nombre et 55 il y a quand même une fameuse différence !
  • OK, ça gratte quelques pourcents. D'un autre côté comme les programmes calculent plus vite $F_{1023}$ que $F_{450}$, j'avoue ne pas y comprendre grand chose.
    import time
    
    def fibo(n):
        L=[]            
        while n>0:
            L.append(n%2)
            n=n//2
        l=len(L)        
        a=1             
        b=0
        c=1
        for k in range(l):
            if L[l-k-1]==0:
                a,b,c=a*a+b*b,b*(a+c),b*b+c*c   
            else:                 
                a,b,c=a*(a+b)+b*(b+c),a*a+b*b,b*(a+c)
        return(b)
    
    tps1 = time.clock()
    print(fibo(1023))
    tps2 = time.clock()
    print(tps2 - tps1)
    

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • @ev,

    Qu'est-ce qui t'intéresse le plus : les performances de l'algorithme ou la justesse des résultats qu'il produit ? Si les derniers sont faux, à quoi bon s'acharner à le rendre plus rapide ?
  • Pour les plus grandes valeurs de $n$ (pourquoi ?), c'est la première version qui gagne. Si on regarde bien, il y a une addition de moins dans le cas \( \tt L[k]==1 \). Je remballe la deuxième version.

    @ WIlfrid.
    Je ne comprends rien à ce que tu shtames. Les programmes que j'ai écrits donnent-ils la valeur correcte de $F_n$ oui ou non ?

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • ev a écrit:
    Les programmes que j'ai écrits donnent-ils la valeur correcte de Fn oui ou non ?

    À moins que la fonction Fibonacci[n] de Mathematica se plante complètement, Fibonacci[1023] donne pour résultat
    278529355069959292393881241266809350935330735212370380691318266898736
    950320346518362561675961332445274995854966996688219111789542501520845
    5469403731272652158240825628484818131485544230827304940519132195299466733282.
    (le nombre est sur trois lignes, sinon il disparaît sous le bord droit de la fenêtre du message).

    Avec ton algorithme, L = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, et les 10 entiers produits sont 1, 1, 2, 3, 5, 8, 13, 21, 34, 55. Donc il donne 55 pour valeur de $F_{1023}$. À toi d'en déduire si c'est correct ou non.
  • Ah bon. Tu peux donner le code du programme que tu as fait tourner pour obtenir 55 ?

    Mon programme à moi donne les mêmes chiffres que mathematica. En tout cas les mêmes premiers et les mêmes derniers.

    Je ne sais pas ce que je dois en déduire.

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • J'ai posté l'algorithme au format Mathematica un peu plus haut. Il suffit de remplacer le contenu de L par les chiffres de n en base 2, ou d'écrire algoEv[n_] := ..., puis L = IntegerDigits[n, 2] (ce qui calcule n en base 2 et éclate ses chiffres sous forme d'une liste).

    Si tu obtiens la bonne valeur de $F_n$ avec ton algorithme c'est que le mien diffère d'une façon quelconque. Mais la question se pose néanmoins de la machine que tu utilises pour tes calculs, parce que des babasses capables d'afficher des nombres aussi grands que ceux que Mathematica pond sans difficulté, à mon avis ça ne court pas les rues.

    Afin de comparer, quels sont les 7 entiers que tu obtiens pour $F_{123}$ ? Mon algorithme renvoie 1, 1, 2, 3, 21, 34, 55.
  • C'est sûrement toi qui a raison. (Je ne parle pas le mathematica).

    Bon Week end,

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • Et moi je ne parle pas le Python, mais j'ai quand même fait l'effort de comprendre. Voici l'algorithme en Wolfram Language (langage de programmation de Mathematica) transcrit en Python-like. Je prends n = 123 :
    L = [1,1,1,1,0,1,1]
    a,b,c = 1,0,1
    l = len(L)
    for k in range(l):
      if L[k] == 0:
        a,b,c = a*a+b*b,b*(a+c),b*b+c*c
      else:
        a,b,c=a+b,a,b
      print(b)
    

    Je viens de m'apercevoir que tu as discrètement modifié le calcul de a,b,c dans la seconde version de l'algorithme pour L[k] = 1 :
    if L[l-k-1]==0:
      a,b,c=a*a+b*b,b*(a+c),b*b+c*c   
    else:                 
      a,b,c=a*(a+b)+b*(b+c),a*a+b*b,b*(a+c)
    

    D'abord, pourquoi L[l-k-1] au lieu de L[k] ? En supposant que la valeur initiale de k est 0 (attribuée par Python automatiquement puisqu'elle n'est pas spécifiée par le programmeur), L[l-k-1] indique qu'on ne parcourt pas les termes de L en partant du premier mais en partant du dernier, et ça fait une grande différence.

    Dans le else, ensuite, a,b,c ne se voient plus attribuer respectivement $a+b\,,\,a\,,\,b$ mais $a\,(a+b)+b\,(b+c)\,,\,a^2+b^2\,,\,b\,(a+c)$, ce qui change complètement la donne. Dans ce genre de situation il est souhaitable de signaler la modification !

    J'ai donc réécrit l'algorithme en Wolfram Language pour prendre en compte ces changements :

    algoEv2.png

    Le résultat est plus satisfaisant, mais tout aussi faux. algoEv2[123], par exemple, renvoie 70492524767089125814114 comme dernier entier calculé, alors que Fibonacci[123] = 22698374052006863956975682, et j'ai tendance à plus faire confiance à Mathematica qu'à ton algorithme.
  • Bonne nouvelle, cet algorithme fonctionne ! (:P) Il suffit simplement de remplacer
    if L[l-k-1]==0:
      ...
    
    par
    if L[k]==0:
      ...
    
    dans la fonction fibo(n) programmée en Python ci-dessus par @ev. Autrement dit, on doit parcourir les termes de L depuis le début et non depuis la fin. La fonction donne maintenant les résultats recherchés (i.e. identiques à ceux de Mathematica) : fibo(15) =610, fibo(97) = 83621143489848422977, fibo(123) = 22698374052006863956975682, etc.

    @ev, tu es sûr d'avoir testé ta fonction après l'avoir modifiée ?
  • J'ai trouvé un testeur de code Python à cette adresse, et ce qui s'est passé ensuite m'a laissé sans voix : la fonction fibo(n) fonctionne en l'état telle qu'écrite par @ev, donc avec if L[l-k-1]==0. En langage Wolfram elle fonctionne uniquement avec if L[k]==0.

    J'ai franchement du mal à comprendre : si l = 11 (le nombre de termes de L) et que la valeur initiale de k est 0, lorsqu'on entre dans la boucle For et qu'on pose l-k-1 = 10 on vise bien le dernier terme de L, L[10]. Lorsqu'à l'itération suivante k est incrémenté, on vise le terme L[9], c'est-à-dire l'avant-dernier. Etc. On parcourt donc les termes de L depuis le dernier, principe qui devrait rester valable quel que soit le langage de programmation utilisé. Le fait qu'en langage Wolfram il faille commencer par le premier terme de L (c'est-à-dire L[1]) est donc totalement contre-intuitif et pose même un sérieux problème.

    Il est possible que quelque chose m'échappe dans cette forme particulière de boucle For qu'est "for k in range(l)", bien que je ne vois pas d'autre initialisation possible de k que 0.

    En tout cas, voilà qui donne à réfléchir sur la portabilité d'un code...
  • [suite du précédent]

    Voici l'explication du phénomène. Dans son code Python, @ev calcule le contenu de la variable L de la manière suivante :
    L=[]            
    while n>0:
        L.append(n%2)
        n=n//2
    
    En langage Wolfram je procède ainsi :
    L=IntegerDigits[n, 2]
    
    Résultat avec l'exemple de n = 49 = 1100012 :

    Python : L = [1,0,0,0,1,1]
    Wolfram : L = {1,1,0,0,0,1}

    La fonction IntegerDigits place les bits dans le bon ordre, le code Python dans l'ordre inverse, ce qui oblige à parcourir L à partir du dernier terme. Le code suivant élimine le problème en inversant le contenu de L après sa création, ce qui permet d'écrire if L[k]==0, plus naturel et logique que L[l-k-1]==0 :
    def fibo(n):
        L=[]            
        while n>0:
            L.append(n%2)
            n=n//2
        L.reverse()
        l=len(L)       
        a,b,c=1,0,1             
        for k in range(l):
            if L[k]==0:
                a,b,c=a*a+b*b,b*(a+c),b*b+c*c   
            else:                 
                a,b,c=a*(a+b)+b*(b+c),a*a+b*b,b*(a+c)
        return(b)
    
    print(fibo(49))
    
    Mais c'est loin d'être l'idéal, parce que si quelqu'un veut transcrire ce code dans un autre langage il va sans en chercher la raison inverser L, ce qui faussera à nouveau les résultats. Il faudrait modifier ce code de sorte à éviter l'inversion de L.
  • [suite du précédent]

    Voici comment rendre ce code portable en éliminant les pièges potentiels :
    def listebin(x):
        # conversion de x en binaire puis liste de bits
        # exemple : listebin(49) = [1,1,0,0,0,1]
        return [int(d) for d in str(int(bin(x)[2:]))]
    
    def fibo(n):
        L=listebin(n)            
        l=len(L)       
        a,b,c=1,0,1             
        for k in range(l):
            if L[k]==0:
                a,b,c=a*a+b*b,b*(a+c),b*b+c*c   
            else:                 
                a,b,c=a*(a+b)+b*(b+c),a*a+b*b,b*(a+c)
        return(b)
    
    print(fibo(49))
    
    Code de listebin() basé sur une page de Stackoverflow

    En tout cas merci à @ev pour cet algorithme génial ! Il ne te reste plus qu'à nous expliquer la théorie qui en est à l'origine.
  • $\def\N{{\lfloor n/2\rfloor}}$Coucou,
    Je n'ai pas lu tous les posts de manière attentive (et peut-être que je n'en ai pas envie) mais je trouve que certains passages sentent un peu la bidouille. Je me demande quelle peut-être la réaction d'un ``matheux pur'' (sic) vis-à-vis d'une telle présentation de l'algorithmique. Du coup, je raconte comment on enseignait une méthode de calcul de $F_n$ (niveau L2, module algorithmique dans un cursus mathématique). Je ne donne pas tous les détails (cf une note de cours attachée pour en savoir plus).

    Etape 1. Prendre un peu de recul. Pour un monoïde quelconque $(M, \times, 1_M)$, deux exponentiations dichotomiques fournies sous forme d'un schéma récursif. Je désigne par $n = 2\lfloor n/2\rfloor + r$ la division euclidienne de $n$ par $2$ : $\lfloor n/2\rfloor$ en est le quotient et $r = 0,1$ le reste.
    $$
    x^n = (x^2)^{\N} \times x^r = \cases {(x^2)^{\N} &si $n$ est pair \cr (x^2)^{\N} \times x &si $n$ est impair }
    \qquad\qquad
    x^n = (x^\N)^2 \times x^r = \cases {(x^\N)^2&si $n$ est pair \cr (x^\N)^2\times x &si $n$ est impair }
    $$
    avec bien entendu $x^0 = 1_M$. A gauche, c'est l'exponentation dichotomique la plus répandue. Celle de droite (moins répandue) est parfois nommée ``exponentation dichotomique à la Hörner'' car elle résulte en fait de l'évaluation ``à la Hörner'' de l'exposant $n$ via son écriture en base 2.

    Etape 2. Mettre sous forme itérative ces deux schémas récursifs. Interviennent alors les chiffres binaires de $n$ qui sont calculés au fur et à mesure (en commençant par les poids faibles à droite et par les poids forts à gauche). Le nombre d'opérations est grosso-modo $\log_2(n)$ (et pas $n$). Pas de détails ici. Cela vaut le coup une fois dans sa vie de prendre un $n$ ni trop petit ni trop grand et de regarder de manière précise ce que fournissent les schémas : quelles sont exactement les puissances calculées ? ...Etc... Je l'ai fait pour $n = 155$, un extrait d'une page manuscrite (sur deux).

    Etape 3. Pour le calcul de $F_n$, considérer l'anneau quotient $\Z[X] / \langle X^2 -(X+1) \rangle$ et noter $\phi$ la classe de $X$ dans le quotient, qui vérifie $\phi^2 = \phi + 1$.
    $$
    \Z[\phi] =_{\rm def} {\Z[X] \over \langle X^2 - (X+1) \rangle} = \Z \oplus \Z\phi
    $$
    Le point fondamental est l'égalité $\phi^n = F_{n-1} + F_n\phi$ qui se démontre facilement par récurrence sur $n$. Ici, on joue donc la carte : calculer $F_n$ ou $\phi^n$, c'est kif-kif.

    Etape 4. Prendre en charge soi-même l'élévation au carré et la multiplication dans $\Z[\phi]$ en encodant $a + b \phi$ par le couple $(a,b)$.

    Etape 5. Implémenter le binz dans son langage préféré. J'ai fait cela un certain nombre de fois dans des langages divers. Je viens de le refaire histoire de jouer le jeu. J'ai choisi l'exponentiation dichotomique à la Hörner car elle présente ici un avantage : la multiplication par $\phi$ est une fausse multiplication car $(a+b\phi) \times \phi = b + (a+b)\phi$.
    [color=#000000]FibonacciHorner := function(n)
      // Retourne F(n) en calculant Phi^n via une exponentiation dichotomique à la Horner
      if n eq 0 then return 0 ; end if ;
      p := 2^Ilog(2,n) ;    // p puissance de 2   et   n < 2*p
      // (a,b) <--> a.1 + b.Phi
      a := 1 ; b := 0 ;
      m := n ;
      while true do
        // (a,b) <-- (a + b*Phi)^2 = a^2+b^2 + b*(2*a+b)*Phi
        a_saved := a ;
        a := a^2 + b^2 ;
        b := b*(2*a_saved + b) ;
        if m ge p then
          // (a,b) <-- (a + b*Phi)*Phi = b + (a+b)*Phi 
          a_saved := a ;
          a := b ;
          b := a_saved + b ;
          m := m-p ;
        end if ;
        if p eq 1 then break ; end if ;
        p := ExactQuotient(p,2) ;
      end while ;
      return b ;
    end function ;
    [/color]
    
    Je n'ai pas cherché à finasser. Pour $n = 123456789$, l'exécution ci-dessus nécessite 8 secondes versus 6 secondes pour celle implémentée en magma.

    Note (rien à voir) : je crois voir qu'un noble langage a choisi ``len'' comme nom de fonction prédéfinie pour la longueur. No comment.89530
  • @ Claude.

    C'est à un poil($\Q$) près la même chose. La seule différence c'est que je travaille avec les matrices \( \begin{pmatrix} 1 & 1\\ 1 & 0 \end{pmatrix}^n \) que je calcule par exponentiation rapide.

    Mais ces deux anneaux sont-ils vraiment différents ?

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • @ev
    Je viens de comprendre (partiellement). Il me semble (mais je suis peut-être le seul à le penser) que tes posts manquaient d'explications. Et je n'avais pas vu trace, dans tes posts précédents, de cette matrice dont tu parles dans le dernier post : ai je la vue basse ?

    Oui les anneaux sont canoniquement isomorphes. De manière précise, si $F \in A[X]$ est un polynôme unitaire ($A$ anneau commutatif) de degré $n \ge 1$ et $C \in M_n(A)$ sa matrice compagnon, alors les $A$-algèbres $A[X]/\langle F\rangle$ et $A[C]$ sont canoniquement isomorphes via $X \bmod F \leftrightarrow C$.

    Ci-dessous, la matrice compagnon $C$ (à la française) de $F = X^2 - (X+1)$, qui est conjuguée à la tienne
    [color=#000000]> C ;
    [0 1]
    [1 1]
    > S ;
    [0 1]
    [1 0]
    > S*C*S ;
    [1 1]
    [1 0]
    > n := 7 ;
    > C^n ;
    [ 8 13]
    [13 21]
    > Fibonacci(n-1), Fibonacci(n), Fibonacci(n+1) ;
    8 13 21
    [/color]
    
    Par ailleurs, je n'ai pas cherché à comprendre complètement ton code (j'ai passé l'âge). Pas compris non plus les temps de calcul (8 heures ?). Une utilisation brute de l'exponentation de $C$ (pas d'optimisation du fait que $C$ est symétrique) n'est pas si mauvaise.
    [color=#000000]> n := 123456789 ;
    > time F := C^n ;
    Time: 24.270
    [/color]
    
    Je ne sais pas quelle exponentiation dichotomique est utilisée (la classique probablement).
  • @ Claude.

    Ici on est au royaume du shtam. Les commentaires sont remplacés par du mystère, de la dissimulation et de la poudre aux yeux. C'est pourquoi mes matrices se sont avancé masquées.

    Je finis par me demander ce que ma machine - plus toute jeune, certes, mais je ne suis pas non plus un perdreau de l'année - a pu boutiquer pendant huit (8) heures.

    amicalement,

    e.v.
    Personne n'a raison contre un enfant qui pleure.


  • Il semblerait que l'algorithme dont je vais parler soit peu connu, sinon quelqu'un l'aurait déjà évoqué sur ce fil. Il repose sur le même principe que celui de @ev : doubler l'intervalle entre deux termes consécutifs. Exemple avec $F_{31}=1346269$ : les valeurs calculées en 5 itérations par l'algorithme de @ev sont 1, 2, 13, 610, 1346269. Les 31 premiers termes de la suite de Fibonacci sont

    1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269. L'intervalle entre les termes en rouge est successivement $2^1,2^2,2^3,2^4$.

    Dans cette page de math.stackexchange un certain Dane Bouchie expliquait il y a 5 ans, en réponse à la question initiale, que le doublement de l'intervalle est rendu possible par 3 propriétés élémentaires des nombres de Fibonacci. Comme il m'a fallu un certain temps pour comprendre les explications qu'il fournit ensuite, je les répète ici en m'efforçant d'être un peu plus clair :
    1. Pour calculer $F_n$ on commence par convertir $n$ en binaire.
    2. Avec $k=1$ on part de $F_{k-1}=0,F_k=1$, soit $(0,1)$.
    3. On calcule, dans l'ordre 1, 3, 2
      1. $F_{2k-1}=F_k^2+F_{k-1}^2$
      2. $F_{2k}=F_{2k+1}-F_{2k-1}$
      3. $F_{2k+1}=4\,F_k^2-F_{k-1}^2+2\,(-1)^k$

      ce qui dans le bon ordre donne $(1,1,2)$, les trois premiers termes de la suite. Comme l'auteur l'explique au paragraphe "Optimizations", on peut remplacer $2\,(-1)^k$ par 2 lorsqu'un bit de $n_{(2)}$ vaut 0, et par -2 s'il vaut 1. Ce sera la valeur assignée à la variable $p$, qui remplacera $2\,(-1)^k$ dans ce qui suit et permettra de s'affranchir complètement de $k$, ou plutôt de ce qui autrement importerait : sa parité.
    4. Le triplet qu'il faut maintenant calculer dépend de la valeur du bit de $n_{(2)}$, et on commence par le second. Avec $(a,b,c)=(1,1,2)$ on remplace le premier doublet, $(0,1)$, par
      • $(1,1)$, soit $(a,b)$, si le bit vaut 0,
      • $(1,2)$, soit $(b,c)$, si le bit vaut 1.

      Ce doublet équivaut à $(F_{k-1},F_k)$, tout comme $(F_0,F_1)$ avec $k=1$. En possession du nouveau doublet on retourne à l'étape 3, et on répète ces deux étapes jusqu'à ce qu'on ait traité le dernier bit de $n_{(2)}$.
    Je reprends l'exemple de $F_{13}$ proposé par l'auteur. $13=1101_2$. On part du triplet $(1,1,2)$. Comme le second bit est 1, le nouveau doublet est $(F_{k-1},F_k)=(1,2)$ et $p=-2$, ce qui permet de calculer
    • le premier terme du nouveau triplet, $F_k^2+F_{k-1}^2=4+1=5$
    • son troisième terme, $4\,F_k^2-F_{k-1}^2+p=16-1-2=13$
    • et son second terme, $F_{2k+1}-F_{2k-1}=13-5=8$.
    Le triplet est maintenant $(5,8,13)$. Comme le troisième bit de $n_{(2)}$ est 0, le nouveau doublet est $(5,8)$ et $p=2$. Les termes du triplet suivant sont :
    • premier : $F_k^2+F_{k-1}^2=64+25=89$
    • troisième : $4\,F_k^2-F_{k-1}^2+p=4\times 8^2-5^2+2=233$
    • deuxième : $F_{2k+1}-F_{2k-1}=233-89=144$
    A l'étape 5 de son exemple, Dane Bouchie déclare que le bit 0 (l'avant-dernier) étant le dernier bit pris en compte, le résultat du calcul est $F_k=233$. On retiendra seulement que les bits de poids fort et faible ne sont pas utilisés.

    Passons maintenant à la fonction fiboStack[n] (pour rappeler son origine sur Stackexchange) écrite en langage Wolfram (Mathematica) à partir des explications qui précèdent, et que je vais (* commenter *) :
    L = IntegerDigits[n, 2];  (* conversion de n en binaire et création de la liste de ses bits *)
    {a,b,c} = {1,1,2};  (* triplet de départ *)
    For [i = 2, i < Length[L], i++,  (* on parcourt L du second à l'avant-dernier terme *)
      If [L[i] == 0,
        d = {a, b}^2; p = 2,  (* si bit=0 *)
        d = {b, c}^2; p = -2];  (* si bit=1. Format de If : If [condition, vrai, faux] *)
        (* F_(k-1) et F_k sont toujours au carré. On effectue l'opération une seule fois. Voir "Optimizations" *)
      a = Total[d]; c = 4 d[2] - d[1] + p; b = c - a
    ];  (* /For *)
    If [EvenQ[n], b, c]  (* si n est pair on affiche b, sinon on affiche c *)
    
    A la dernière ligne on voit que la variable à afficher, celle qui contient $F_n$, dépend de la parité de $n$, ce qui s'explique facilement : puisqu'à chaque itération l'algorithme calcule $a=F_{2k-1}\,,b=F_{2k}\,,c=F_{2k+1}$, le dernier triplet sera $(F_{n-1}\,,F_{n\:(pair)}\,,F_{n+1})$. C'est le bit de poids faible, selon qu'il vaut 0 ou 1 ($n$ pair ou impair), qui permettra de savoir si le résultat du calcul figure dans $b$ ou dans $c$.

    La fonction fiboStack[n] plus lisible :

    fiboStack.png

    Au format Python :
    def carre(lst):
        # élévation au carré des termes de la liste lst
        return [i**2 for i in lst]
    
    def fiboStack(n):
        # conversion de n en binaire puis liste de bits
        L=[int(d) for d in str(int(bin(n)[2:]))]
        a,b,c=1,1,2; lg=len(L)-1
        # lg pointe sur le dernier terme de L.
        # on parcourt L du second à l'avant-dernier terme.
        # range(1,lg) = [1, 2, ..., lg-1]
        for j in range(1,lg):
           if L[j]==0:
              d=carre([a,b]); p=2
           else:
              d=carre([b,c]); p=-2
           a=sum(d); c=4*d[1]-d[0]+p; b=c-a
        if L[lg]==0: return(b)
        else: return(c)
    
    print(fiboStack(13))
    
    Une fois de plus, si on veut que quelqu'un puisse transcrire ce code dans un autre langage il faut prendre soin de lui signaler les bizarreries de Python. Avec x=range(3,6), par exemple, la variable x ne contiendra pas [3,4,5,6] mais [3,4,5] ! C'est totalement incompréhensible, et bien sûr je me suis fait avoir.

    Pour comparer les performances respectives des fonctions Fibonacci[n] (native), fibo[n] (@ev) et fiboStack[n], je fais afficher "terminé" à Mathematica à l'issue du calcul de $F_{123456789}$, ce qui évite de perdre du temps à tenter d'afficher un nombre de 25 800 943 chiffres. La représentation binaire de 123456789 comptant 27 bits, ce calcul requiert 25 itérations avec fiboStack[n] et 27 avec fibo[n] :

    Fibonacci[n] : 1,22 s
    fiboStack[n] : 1,36 s
    fibo[n] : 3,82 s

    Dane Bouchie ne prétendant pas être l'auteur de cet algorithme, reste à savoir d'où il sort.
  • Juste un aparté pour la notation "range(a,b)" en Python.
    Par défaut, a=0, de sorte que range(b) est un raccourci pour range(0,b), qui est la liste [0,1,...,b-1].
    Cette notation est vraiment très commode à l'usage : d'un côté, il est normal pour tout un tas de raisons de commencer à numéroter à $0$ ; d'autre part, l'argument b donne le nombre de termes de la liste. Pour range(a,b), le nombre de termes est b-a.

    J'insiste : à défaut d'être intuitif, c'est très agréable à l'usage.
  • $\def\vep{\varepsilon}$@Wilfrid
    Je pense pouvoir dire que j'ai lu attentivement ton dernier post et que j'en ai apprécié les explications (quelques légers bémols cependant, cf plus loin). J'ai même été jusqu'à implémenter la méthode de manière à trouver les invariants. De manière à pouvoir en fournir une preuve. Car chez nous autres, il faut faire plus que d'expliquer : un algorithme doit être prouvé.

    1. Petits bémols en question. Il est important de préciser dans quel ordre sont consommés les chiffres binaires de $n$ : ils le sont dans le sens poids forts vers poids faibles. Certes, cela se voit sur l'exemple $13 = (1101)_2$ et les explications que tu donnes. Mais après tout, dans les programmes, on ne sait pas trop dans quel sens sont rangés les chiffres binaires. En tout cas, en maple ou en magma, ils ne sont pas rangés dans le même sens que Mathematica.
    Il y a aussi le coup d'accés à Li en dehors de la boucle qui définit $i$ : dans certains langages, l'indice d'une boucle n'est défini qu'à l'intérieur de la boucle, pas en dehors. On s'en tire en testant la parité de $n$ (ce qui ne coûte rien). Enfin, quand tu dis que les bits de poids fort et faibles ne sont pas utilisés, c'est à modérer : celui de poids faible est utilisé après la boucle pour décider entre $b$ et $c$. Quant à celui de poids fort, il est égal à 1, alors ...

    A noter que l'algorithme ne fonctionne que pour $n \ge 2$ ; ce qui n'est pas un handicap car (petit patch) $F_n = n$ pour $n = 0,1$.

    Mais ces remarques ne sont que du détail. Ce que je voudrais essayer de faire passer, c'est le point 2. qui suit.

    2. Vers des éléments de preuve. Attention, cela va être du lourd, du très lourd même. Je ne sais pas si tu connais la théorie des invariants en algorithmique. Je vais jouer la carte de l'indexation : certes, c'est lourd (bis) mais sûr. D'abord, pour éviter les conflits avec tes notations, je vais noter la décomposition binaire de $n$ de la manière suivante :
    $$
    n = \vep_0 + \vep_1 2^1 + \vep_2 2^2 + \cdots + \vep_h 2^h, \qquad \vep_i \in \{0,1\}
    $$
    Je ne range rien ici dans des listes ou je ne sais trop quoi : pour l'instant, je fais des maths, du moins j'essaie.

    J'introduis alors (attention, c'est là le lourd) les sommes partielles :
    $$
    \left \{
    \begin {array} {ccl}
    s_h &=& \vep_h (= 1) \\
    s_{h-1} &=& \vep_{h-1} + 2 \vep_h \\
    & \vdots & \\
    s_2 &=& \vep_2 + 2\vep_3 + 2^2 \vep_4 + \cdots \\
    s_1 &=& \vep_1 + 2\vep_2 + 2^2 \vep_3 + \cdots \\
    s_0 &=& \vep_0 + 2\vep_1 + 2^2 \vep_2 + \cdots \\
    \end {array}
    \right.
    $$
    Bien observer les indices.Of course $s_0$ c'est $n$. Ce que je dis, c'est que l'on va calculer les triplets successifs :
    $$
    T_i = (F_{2s_i-1}, F_{2s_i}, F_{2s_i+1}) \quad \text{pour} \quad i \text { descendant de } h \text { à } 1
    $$
    Note que chaque triplet est de la forme $(F_{2k-1}, F_{2k}, F_{2k+1})$. Et que l'on descend jusqu'à $s_1$ et pas jusqu'à $s_0 = n$ (qui sera pris en charge comme il faut). Cela commence comment pour $i = h$ ? On a dit que $s_h = 1$ donc :
    $$
    2s_h -1 = 1, \qquad 2s_h = 2, \qquad 2s_h + 1 = 3 \qquad \qquad \hbox {et} \qquad
    (F_1, F_2, F_3) = (1,1,2) \text { que tu reconnais}
    $$
    Enfin, on passe de $T_i$ à $T_{i-1}$ ``comme tu le penses'' en utilisant $s_{i-1} = 2 s_i + \vep_i$ : regarde par exemple $s_1 = 2s_2 + \vep_1$.

    3. Si tu savais tout cela, c'est un coup pour rien. De mon côté, cela m'a permis de verrouiller mon programme. En guise de conclusion, une petite pensée pour quelques anciens (dans le désordre) : Hoare, Floyd, Jacques Arsac ...
Connectez-vous ou Inscrivez-vous pour répondre.