web-dev-qa-db-fra.com

nième numéro de fibonacci en sous-temps

Existe-t-il un algorithme pour calculer le nième nombre de fibonacci en temps sub-linéaire?

73
Biswajyoti Das

Le nombre nth de Fibonacci est donné par

f(n) = Floor(phi^n / sqrt(5) + 1/2) 

où 

phi = (1 + sqrt(5)) / 2

En supposant que les opérations mathématiques primitives (+, -, * et /) soient O(1), vous pouvez utiliser ce résultat pour calculer le nombre nth de Fibonacci dans O(log n) time (O(log n) en raison de l'exponentiation dans la formule).

En C #:

static double inverseSqrt5 = 1 / Math.Sqrt(5);
static double phi = (1 + Math.Sqrt(5)) / 2;
/* should use 
   const double inverseSqrt5 = 0.44721359549995793928183473374626
   const double phi = 1.6180339887498948482045868343656
*/

static int Fibonacci(int n) {
    return (int)Math.Floor(Math.Pow(phi, n) * inverseSqrt5 + 0.5);
}
63
jason

D'après la référence de Pillsy à l'exponentiation matricielle, telle que pour la matrice

M  = [1 1] 
 [dix] 

puis 

mensonge(n) = Mn1,2

Élever des matrices à des puissances utilisant une multiplication répétée n’est pas très efficace.

Deux approches de l’exponentiation matricielle sont diviser et conquérir, ce qui donne Mn dans O(dans n) des étapes, ou une décomposition en valeurs propres qui est un temps constant, mais peut introduire des erreurs dues à la précision en virgule flottante limitée.

Si vous voulez une valeur exacte supérieure à la précision de votre implémentation en virgule flottante, vous devez utiliser l'approche O (ln n) basée sur cette relation: 

Mn  = (Mn/ 2)2  si n  même
 = M·Mn-1  si n  est impair

La décomposition en valeur propre sur M trouve deux matrices U et Λ tel que Λ est en diagonale et 

M   = UΛU-1Mn  = ( UΛU-1nUΛU-1UΛU-1UΛU-1  ... n fois 
 = UΛΛΛ  ... U-1  
 = UΛnU-1
Élever une matrice diagonale Λ au nle pouvoir consiste simplement à élever chaque élément de Λ au nth, donc cela donne une méthode O(1) pour élever M au nle pouvoir. Cependant, les valeurs dans Λ ne sont pas susceptibles d'être des entiers, donc une erreur se produira.

Définissant Λ pour notre matrice 2x2 comme

Λ  = [λ1  0] 
 = [0 λ2  ] 

Trouver chacun λ, nous résolvons 

  |M  - λJe| = 0

qui donne

  |M  - λJe| = -λ (1 - λ) - 1 

 λ² - λ - 1 = 0 

en utilisant la formule quadratique 

 λ = (-b ± √ (b²-4ac))/2a 
 = (1 ± √5)/2 
 {λ1, λ2  } = {, 1-Φ} où = (1 + √5)/2 

Si vous avez lu la réponse de Jason, vous pouvez voir où cela va aller.

Résoudre pour les vecteurs propres X1 et X2:

si X1  = [ X1,1X1,2  ] 

 M.X1 1  = λ1X1X1,1  + X1,2  = λ1X1,1X1,1       = λ1X1,2

 => 
 X1  = [Φ, 1] 
 X2  = [1-Φ, 1] 

Ces vecteurs donnent U:

U  = [ X1,1X2,2  ] 
 [ X1,1X2,2  ] 

 = [Φ, 1-Φ] 
 [1, 1] 

Inverser U en utilisant

UNE    = [a b] 
 [c d] 
 => 
UNE-1  = (1/|UNE| ) [d -b] 
 [ -Californie ]

alors U-1 est donné par

U-1  = (1/(Φ - (1 - Φ)) [1 Φ-1] 
 [-1 Φ] 
U-1  = (√5)-1   [1 Φ-1] 
 [-1 Φ] 

Verification sanitaire:

UΛU-1  = (√5)-1  [Φ 1-Φ]. [Φ 0]. [1 Φ-1] 
 [1 1] [0 1-Φ] [-1 Φ] 

 Soit = ​​1-Φ, l'autre valeur propre 

 Étant donné que Φ est une racine de λ²-λ-1 = 0 
 so -ΨΦ = Φ²-= 1 
 et Ψ + Φ = 1 

UΛU-1  = (√5)-1  [Φ Ψ]. [Φ 0]. [1 -Ψ] 
 [1 1] [0 Ψ] [-1 Φ] 

 = (√5)-1  [Φ Ψ]. [Φ -ΨΦ] 
 [1 1] [-Ψ ΨΦ] 

 = (√5)-1  [Φ Ψ]. [Φ 1] 
 [1 1] [-Ψ -1] 

 = (√5)-1  [Φ²-Ψ²-Ψ] 
 [Φ-Ψ 0] 

 = [Φ + Ψ 1] 
 [ dix ]

 = [1 1] 
 [ dix ]

 = M

Donc, le test de santé mentale tient.

Maintenant, nous avons tout ce dont nous avons besoin pour calculer Mn1,2:

Mn  = UΛnU-1
 = (√5)-1  [Φ Ψ]. [n   0] [1 -Ψ] 
 [1 1] [0n  ] [-1 Φ] 

 = (√5)-1  [Φ Ψ]. [n   -ΨΦn  ] 
 [1 1] [-Ψn    Ψn] 

 = (√5)-1  [Φ Ψ]. [n    Φn-1  ] 
 [1 1] [-Ψn   -Ψn-1  ] comme = -1 

 = (√5)-1  [n+1n+1       Φnn  ] 
 [nn       Φn-1n-1  ] 

alors

mensonge(n) = Mn1,2
 = (n  - (1-)n  )/√5 

Ce qui est en accord avec la formule donnée ailleurs.

Vous pouvez le déduire d'une relation de récurrence, mais en informatique de calcul et de simulation, calculer les valeurs propres et les vecteurs propres de grandes matrices est une activité importante.

96
Pete Kirkham

Si vous voulez le nombre exact (qui est un "bignum" plutôt qu'un int/float), alors je crains que

C'est impossible!

Comme indiqué ci-dessus, la formule pour les nombres de Fibonacci est la suivante:

fib n = sol (phin/ √5 + 1/2)

fib n ~ = phin/ √5

Combien de chiffres est fib n?

numDigits (fib n) = log (fib n) = log (phin/ √5) = log phin - log √5 = n * log phi - log √5

numDigits (fib n) = n * const + const

c'est O (n)

Le résultat demandé étant de O (n), il ne peut pas être calculé en moins de O (n).

Si vous souhaitez uniquement les chiffres inférieurs de la réponse, vous pouvez calculer en temps sub-linéaire à l'aide de la méthode de l'exponentiation matricielle.

54
yairchu

Un des exercices de SICP concerne ce sujet, dont la réponse est décrite ici.

Dans le style impératif, le programme ressemblerait à quelque chose comme:

Une fonctionMensonge(compter) 
 une  ← 1 
 b  ← 0 
 p  ← 0 
 q  ← 1 

 Tandis quecompter  > 0 FaireSi  Même(compterEnsuitep  ← p² + q² 
 q  ← 2pq  + q² 
 compter  ← compter  ÷ 2 
 Elseune  ← bq  + aq  + apb  ← bp  + aqcompter  ← compter  - 1
 Fin siFin tout enRevenirbFonction de fin
33
Nietzche-jou

Vous pouvez le faire en exposant également une matrice d'entiers. Si vous avez la matrice 

    / 1  1 \
M = |      |
    \ 1  0 /

alors (M^n)[1, 2] sera égal au nth nombre de Fibonacci, si [] est un indice de matrice et ^ est une exponentiation de matrice. Pour une matrice de taille fixe, l'exponentiation à une puissance intégrale positive peut être effectuée en un temps O (log n) de la même manière qu'avec des nombres réels. 

EDIT: Bien sûr, selon le type de réponse que vous souhaitez, vous pourrez peut-être vous en sortir avec un algorithme à temps constant. Comme le montrent les autres formules, le nombre nth de Fibonacci croît de manière exponentielle avec n. Même avec des entiers non signés 64 bits, vous aurez uniquement besoin d'une table de correspondance à 94 entrées pour couvrir toute la plage.

SECOND EDIT: Faire la matrice exponentielle avec une composition eigend est tout d'abord exactement équivalent à la solution de JDunkerly ci-dessous. Les valeurs propres de cette matrice sont le (1 + sqrt(5))/2 et le (1 - sqrt(5))/2.

24
Pillsy

Wikipedia propose une solution sous forme fermée http://en.wikipedia.org/wiki/Fibonacci_number

Ou en c #:

    public static int Fibonacci(int N)
    {
        double sqrt5 = Math.Sqrt(5);
        double phi = (1 + sqrt5) / 2.0;
        double fn = (Math.Pow(phi, N) - Math.Pow(1 - phi, N)) / sqrt5;
        return (int)fn;
    }
5
JDunkerley

Pour les très gros, cette fonction récursive fonctionne. Il utilise les équations suivantes:

F(2n-1) = F(n-1)^2 + F(n)^2
F(2n) = (2*F(n-1) + F(n)) * F(n)

Vous avez besoin d'une bibliothèque qui vous permet de travailler avec de grands entiers. J'utilise la bibliothèque BigInteger de https://mattmccutchen.net/bigint/

Commencez avec un tableau de nombres de fibonacci. Utilisez fibs [0] = 0, fibs [1] = 1, fibs [2] = 1, fibs [3] = 2, fibs [4] = 3, etc. Dans cet exemple, j'utilise un tableau du premier 501 (en comptant 0). Vous pouvez trouver les 500 premiers nombres de Fibonacci non nuls ici: http://home.hiwaay.net/~jalison/Fib500.html . Il faut un peu d’édition pour le mettre dans le bon format, mais ce n’est pas trop difficile.

Ensuite, vous pouvez trouver n’importe quel numéro de Fibonacci en utilisant cette fonction (en C):

BigUnsigned GetFib(int numfib)
{
int n;
BigUnsigned x, y, fib;  

if (numfib < 501) // Just get the Fibonacci number from the fibs array
    {
       fib=(stringToBigUnsigned(fibs[numfib]));
    }
else if (numfib%2) // numfib is odd
    {
       n=(numfib+1)/2;
       x=GetFib(n-1);
       y=GetFib(n);
       fib=((x*x)+(y*y));
    }
else // numfib is even
    {
       n=numfib/2;
       x=GetFib(n-1);
       y=GetFib(n);
       fib=(((big2*x)+y)*y);
   }
return(fib);
}

J'ai testé cela pour le 25 000e numéro de Fibonacci, etc.

3
user3137939

Voici ma version récursive qui récursive log (n) fois. Je pense que c'est plus facile à lire dans la forme récursive:

def my_fib(x):
  if x < 2:
    return x
  else:
    return my_fib_helper(x)[0]

def my_fib_helper(x):
  if x == 1:
    return (1, 0)
  if x % 2 == 1:
    (p,q) = my_fib_helper(x-1)
    return (p+q,p)
  else:
    (p,q) = my_fib_helper(x/2)
    return (p*p+2*p*q,p*p+q*q)

Cela fonctionne parce que vous pouvez calculer fib(n),fib(n-1) en utilisant fib(n-1),fib(n-2) si n est impair et si n est pair, vous pouvez calculer fib(n),fib(n-1) en utilisant fib(n/2),fib(n/2-1).

Le cas de base et le cas impair sont simples. Pour obtenir le cas pair, commencez par a, b, c en tant que valeurs consécutives de fibonacci (par exemple, 8,5,3) et écrivez-les dans une matrice, avec a = b + c. Remarquer:

[1 1] * [a b]  =  [a+b a]
[1 0]   [b c]     [a   b]

À partir de là, nous voyons qu'une matrice des trois premiers nombres de Fibonacci, fois une matrice de trois nombres de Fibonacci consécutifs, est égale à la suivante. Nous savons donc que:

      n
[1 1]   =  [fib(n+1) fib(n)  ]
[1 0]      [fib(n)   fib(n-1)]

Alors:

      2n                        2
[1 1]    =  [fib(n+1) fib(n)  ]
[1 0]       [fib(n)   fib(n-1)]

Simplifier le côté droit mène au cas même.

3
Eyal

en utilisant R

l1 <- (1+sqrt(5))/2
l2 <- (1-sqrt(5))/2

P <- matrix(c(0,1,1,0),nrow=2) #permutation matrix
S <- matrix(c(l1,1,l2,1),nrow=2)
L <- matrix(c(l1,0,0,l2),nrow=2)
C <- c(-1/(l2-l1),1/(l2-l1))

k<-20 ; (S %*% L^k %*% C)[2]
[1] 6765
1
George Dontas

Outre la mise au point par des approches mathématiques, une des meilleures solutions optimales (je crois) consiste à utiliser un dictionnaire afin d’éviter des calculs répétitifs.

import time

_dict = {1:1, 2:1}

def F(n, _dict):
    if n in _dict.keys():
        return _dict[n]
    else:
        result = F(n-1, _dict) + F(n-2, _dict)
        _dict.update({n:result})
        return result

start = time.time()

for n in range(1,100000):
    result = F(n, _dict) 

finish = time.time()

print(str(finish - start))

Nous commençons avec un dictionnaire trivial (les deux premières valeurs de la séquence de Fibonacci) et en ajoutant constamment des valeurs de Fibonacci au dictionnaire.

Il a fallu environ 0,7 seconde pour les premières valeurs 100 000 Fibonacci (processeur Intel Xeon E5-2680 à 2,70 GHz, 16 Go de RAM, système d'exploitation Windows 10-64 bits)

1
bilalsamioguz

L'arithmétique en points fixes est inexacte. Le code C # de Jason donne une réponse incorrecte pour n = 71 (308061521170130 au lieu de 308061521170129) et au-delà.

Pour une réponse correcte, utilisez un système de calcul algébrique. Sympy est une telle bibliothèque pour Python. Il y a une console interactive sur http://live.sympy.org/ . Copier et coller cette fonction

phi = (1 + sqrt(5)) / 2
def f(n):
    return floor(phi**n / sqrt(5) + 1/2)

Puis calculer

>>> f(10)
55

>>> f(71)
308061521170129

Vous voudrez peut-être essayer d’inspecter phi

0
Colonel Panic

voir l'algorithme diviser pour régner ici

Le lien contient un pseudo-code pour l'exponentiation matricielle mentionnée dans d'autres réponses à cette question.

0
Chinmay Lokesh

Voici un one-liner qui calcule F (n), en utilisant des entiers de taille O (n), dans les opérations arithmétiques O (log n):

for i in range(1, 50):
    print(i, pow(2<<i, i, (4<<2*i)-(2<<i)-1)//(2<<i))

Utiliser des entiers de taille O(n) est raisonnable, car cela est comparable à la taille de la réponse.

Pour le comprendre, prenons phi le nombre d’or (la plus grande solution pour x ^ 2 = x + 1) et F(n) le nième nombre de Fibonacci, où F (0) = 0, F ( 1) = F (2) = 1

Maintenant, phi ^ n = F(n-1) + F (n) phi.

Preuve par induction: phi ^ 1 = 0 + 1 * phi = F(0) + F (1) phi. Et si phi ^ n = F(n-1) + F (n) phi, puis phi ^ (n + 1) = F (n-1) phi + F (n) phi ^ 2 = F (n-1) phi + F (n) (phi + 1) = F(n) + (F (n) + F (n-1)) phi = F(n) + F (n + 1) phi . La seule étape délicate de ce calcul est celle qui remplace phi ^ 2 par (1 + phi), car phi est le nombre d'or.

De plus, les nombres de la forme (a + b * phi), où a, b sont des entiers sont fermés sous multiplication.

Preuve: (p0 + p1 * phi) (q0 + q1 * phi) = p0q0 + (p0q1 + q1p0) phi + p1q1 * phi ^ 2 = p0q0 + (p0q1 + q1p0) phi + p1q1 * (phi + 1) = (p0q0 + p1q1) + (p0q1 + q1p0 + p1q1) * phi.

En utilisant cette représentation, on peut calculer phi ^ n dans des opérations entières O (log n) en utilisant exponentiation par quadrature. Le résultat sera F (n-1) + F (n) phi, à partir duquel on peut lire le nombre de Fibonacci.

def mul(p, q):
    return p[0]*q[0]+p[1]*q[1], p[0]*q[1]+p[1]*q[0]+p[1]*q[1]

def pow(p, n):
    r=1,0
    while n:
        if n&1: r=mul(r, p)
        p=mul(p, p)
        n=n>>1
    return r

for i in range(1, 50):
    print(i, pow((0, 1), i)[1])

Notez que la majorité de ce code est une fonction standard d’exponentiation par carré.

Pour obtenir le one-liner qui commence cette réponse, on peut noter que, représentant phi avec un entier assez grand X, on peut exécuter (a+b*phi)(c+d*phi) sous la forme de l’opération entier (a+bX)(c+dX) modulo (X^2-X-1). Ensuite, la fonction pow peut être remplacée par la fonction standard Python pow (qui inclut commodément un troisième argument z qui calcule le résultat modulo z. La X est 2<<i.

0
Paul Hankin

Vous pouvez utiliser l'équation étrange racine carrée pour obtenir une réponse exacte. La raison en est que $\sqrt (5) $ tombe à la fin, il vous suffit de suivre les coefficients avec votre propre format de multiplication.

def rootiply(a1,b1,a2,b2,c):
    ''' multipy a1+b1*sqrt(c) and a2+b2*sqrt(c)... return a,b'''
    return a1*a2 + b1*b2*c, a1*b2 + a2*b1

def rootipower(a,b,c,n):
    ''' raise a + b * sqrt(c) to the nth power... returns the new a,b and c of the result in the same format'''
    ar,br = 1,0
    while n != 0:
        if n%2:
            ar,br = rootiply(ar,br,a,b,c)
        a,b = rootiply(a,b,a,b,c)
        n /= 2
    return ar,br

def fib(k):
    ''' the kth fibonacci number'''
    a1,b1 = rootipower(1,1,5,k)
    a2,b2 = rootipower(1,-1,5,k)
    a = a1-a2
    b = b1-b2
    a,b = rootiply(0,1,a,b,5)
    # b should be 0!
    assert b == 0
    return a/2**k/5

if __== "__main__":
    assert rootipower(1,2,3,3) == (37,30) # 1+2sqrt(3) **3 => 13 + 4sqrt(3) => 39 + 30sqrt(3)
    assert fib(10)==55
0
Scott