¿Existe algún algoritmo para calcular el n-ésimo número de fibonacci en tiempo sub lineal?
performance
algorithm
math
time-complexity
fibonacci
Biswajyoti Das
fuente
fuente
Respuestas:
El
n
número de Fibonacci está dado pordónde
Suponiendo que las operaciones matemáticas primitivas (
+
,-
,*
y/
) sonO(1)
puede utilizar este resultado para calcular lan
ésimo número de Fibonacci enO(log n)
el tiempo (O(log n)
debido a la exponenciación en la fórmula).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); }
fuente
phi^n / sqrt(5) + 1/2
dondephi = (1 + sqrt(5)) / 2
. Esto es un hecho. En segundo lugar, entiendo el punto que otros están haciendo acerca de la longitud de la respuesta,O(n)
pero agregué un comentario a mi respuesta asumiendo que las operaciones matemáticas primitivas toman un tiempo constante (sé que no lo son a menos que limite las entradas). Mi punto es que podemos encontrar el número n de Fibonacci enO(log n)
operaciones aritméticas.Siguiendo la referencia de Pillsy a la exponenciación de la matriz, tal que para la matriz
luego
Elevar matrices a potencias usando multiplicaciones repetidas no es muy eficiente.
Dos enfoques para la exponenciación de matrices son dividir y conquistar, lo que produce M n en O ( ln n ) pasos, o descomposición de valores propios, que es tiempo constante, pero puede introducir errores debido a la precisión limitada de coma flotante.
Si desea un valor exacto mayor que la precisión de su implementación de punto flotante, debe usar el enfoque O (ln n) basado en esta relación:
La descomposición de valores propios en M encuentra dos matrices U y Λ tales que Λ es diagonal y
Elevar a la matriz diagonal Λ a la n- ésima potencia es una simple cuestión de elevar cada elemento en Λ a la n- ésima, por lo que esto da un método O (1) para elevar M a la n- ésima potencia. Sin embargo, no es probable que los valores en Λ sean números enteros, por lo que se producirá algún error.Definiendo Λ para nuestra matriz 2x2 como
Para encontrar cada λ , resolvemos
lo que da
usando la fórmula cuadrática
Si ha leído la respuesta de Jason, puede ver a dónde va a ir esto.
Resolver para los vectores propios X 1 y X 2 :
Estos vectores dan U :
Invertir U usando
entonces U -1 viene dado por
Prueba de cordura:
Entonces el control de cordura se mantiene.
Ahora tenemos todo lo que necesitamos para calcular M n 1,2 :
entonces
Lo cual concuerda con la fórmula dada en otra parte.
Se puede derivar de una relación de recurrencia, pero en ingeniería computacional y simulación calcular los autovalores y autovectores de matrices grandes es una actividad importante, ya que da estabilidad y armónicos de sistemas de ecuaciones, además de permitir elevar matrices a altas potencias de manera eficiente.
fuente
Si desea el número exacto (que es un "bignum", en lugar de un int / float), me temo que
¡Es imposible!
Como se indicó anteriormente, la fórmula para los números de Fibonacci es:
¿Cuántos dígitos tiene
fib n
?Dado que el resultado solicitado es de O ( n ), no se puede calcular en menos de O ( n ) tiempo.
Si solo desea los dígitos más bajos de la respuesta, entonces es posible calcular en tiempo sub-lineal usando el método de exponenciación matricial.
fuente
O(n*log n)
clasificación basada en la comparación de una secuencia den
números donde cada número tieneO(log n)
dígitos?Uno de los ejercicios en SICP trata sobre esto, que tiene la respuesta descrita aquí.
En el estilo imperativo, el programa se vería algo así como
fuente
twisted
marco).if even(count)
tiene razón. La secuencia comienza con cero (el número cero de Fibonacci es cero): 0,1,1,2,3,5,8,13, ...También puede hacerlo exponenciando una matriz de números enteros. Si tienes la matriz
entonces
(M^n)[1, 2]
va a ser igual aln
número de Fibonacci, si[]
es un subíndice de matriz y^
es exponenciación de matriz. Para una matriz de tamaño fijo, la exponenciación a una potencia integral positiva se puede hacer en tiempo O (log n) de la misma manera que con los números reales.EDITAR: Por supuesto, dependiendo del tipo de respuesta que desee, es posible que pueda salirse con la suya con un algoritmo de tiempo constante. Como muestran las otras fórmulas, el
n
número de Fibonacci crece exponencialmente conn
. Incluso con enteros sin signo de 64 bits, solo necesitará una tabla de búsqueda de 94 entradas para cubrir todo el rango.SEGUNDA EDICIÓN: Hacer la matriz exponencial con una descomposición propia primero es exactamente equivalente a la solución de JDunkerly a continuación. Los valores propios de esta matriz son
(1 + sqrt(5))/2
y(1 - sqrt(5))/2
.fuente
Wikipedia tiene una solución de formato cerrado http://en.wikipedia.org/wiki/Fibonacci_number
O en c #:
fuente
|1 - phi|^n / sqrt(5) < 1/2
whenn
es un número entero no negativo.Para los realmente grandes, esta función recursiva funciona. Utiliza las siguientes ecuaciones:
Necesita una biblioteca que le permita trabajar con números enteros grandes. Utilizo la biblioteca BigInteger de https://mattmccutchen.net/bigint/ .
Comience con una serie de números de Fibonacci. Use fibs [0] = 0, fibs [1] = 1, fibs [2] = 1, fibs [3] = 2, fibs [4] = 3, etc. En este ejemplo, uso una matriz de los primeros 501 (contando 0). Puede encontrar los primeros 500 números de Fibonacci distintos de cero aquí: http://home.hiwaay.net/~jalison/Fib500.html . Se necesita un poco de edición para ponerlo en el formato correcto, pero eso no es demasiado difícil.
Luego puede encontrar cualquier número de Fibonacci usando esta función (en C):
He probado esto para el número 25.000 de Fibonacci y similares.
fuente
Aquí está mi versión recursiva que recurre log (n) veces. Creo que es más fácil de leer en forma recursiva:
Funciona porque puede calcular
fib(n),fib(n-1)
usandofib(n-1),fib(n-2)
si n es impar y si n es par, puede calcularfib(n),fib(n-1)
usandofib(n/2),fib(n/2-1)
.El caso base y el caso extraño son simples. Para derivar el caso par, comience con a, b, c como valores de fibonacci consecutivos (por ejemplo, 8,5,3) y escríbalos en una matriz, con a = b + c. Darse cuenta:
A partir de eso, vemos que una matriz de los primeros tres números de Fibonacci, multiplicada por una matriz de tres números de Fibonacci consecutivos, es igual a la siguiente. Entonces sabemos que:
Entonces:
Simplificar el lado derecho conduce al caso par.
fuente
usando R
fuente
La aritmética de punto fijo es inexacta. El código C # de Jason da una respuesta incorrecta para n = 71 (308061521170130 en lugar de 308061521170129) y más.
Para obtener una respuesta correcta, use un sistema de álgebra computacional. Sympy es una biblioteca de este tipo para Python. Hay una consola interactiva en http://live.sympy.org/ . Copiar y pegar esta función
Entonces calcula
Es posible que desee intentar inspeccionar
phi
.fuente
Aparte del ajuste mediante enfoques matemáticos, una de las mejores soluciones óptimas (creo) es usar un diccionario para evitar cálculos repetitivos.
Comenzamos con un diccionario trivial (los dos primeros valores de la secuencia de Fibonacci) y agregando constantemente valores de Fibonacci al diccionario.
Tomó alrededor de 0,7 segundos para los primeros 100000 valores de Fibonacci (Intel Xeon CPU E5-2680 a 2,70 GHz, 16 GB de RAM, sistema operativo Windows 10-64 bit)
fuente
ver el algoritmo divide y vencerás aquí
El enlace tiene un pseudocódigo para la exponenciación de la matriz mencionada en algunas de las otras respuestas para esta pregunta.
fuente
Puedes usar la extraña ecuación de raíz cuadrada para obtener una respuesta exacta. La razón es que $ \ sqrt (5) $ cae al final, solo tiene que realizar un seguimiento de los coeficientes con su propio formato de multiplicación.
fuente
Aquí hay una línea que calcula F (n), usando números enteros de tamaño O (n), en operaciones aritméticas O (log n):
Usar números enteros de tamaño O (n) es razonable, ya que es comparable al tamaño de la respuesta.
Para entender esto, sea phi la proporción áurea (la solución más grande de x ^ 2 = x + 1) y F (n) sea el n-ésimo número de Fibonacci, donde F (0) = 0, F (1) = F (2) = 1
Ahora, phi ^ n = F (n-1) + F (n) phi.
También los números de la forma (a + b * phi), donde a, b son números enteros, se cierran mediante multiplicación.
Usando esta representación, se puede calcular phi ^ n en operaciones de números enteros O (log n) usando exponenciación al elevar al cuadrado. El resultado será F (n-1) + F (n) phi, del cual se puede leer el n-ésimo número de Fibonacci.
Tenga en cuenta que la mayoría de este código es una función estándar de exponenciación al cuadrado.
Para llegar al principio de una sola línea que inicia esta respuesta, se puede notar que al representar phi por un entero lo suficientemente grande
X
, se puede realizar(a+b*phi)(c+d*phi)
como la operación de entero(a+bX)(c+dX) modulo (X^2-X-1)
. Entonces lapow
función puede ser reemplazada por lapow
función estándar de Python (que convenientemente incluye un tercer argumentoz
que calcula el módulo de resultadoz
. ElX
elegido es2<<i
.fuente
Me he encontrado con algunos de los métodos para calcular Fibonacci con una complejidad de tiempo eficiente, los siguientes son algunos de ellos:
Método 1 - Programación dinámica Ahora, aquí la subestructura se conoce comúnmente, por lo tanto, iré directamente a la solución:
Una versión optimizada para el espacio de lo anterior se puede hacer de la siguiente manera:
Método 2- (usando el poder de la matriz {{1,1}, {1,0}})
Este es un O (n) que se basa en el hecho de que si multiplicamos n veces la matriz M = {{1,1}, {1,0}} a sí misma (en otras palabras, calculamos la potencia (M, n)), entonces obtenemos el (n + 1) número de Fibonacci como el elemento en la fila y columna (0, 0) en la matriz resultante. Esta solución tendría O (n) tiempo.
La representación matricial da la siguiente expresión cerrada para los números de Fibonacci: fibonaccimatrix
Esto se puede optimizar para trabajar en complejidad de tiempo O (Logn). Podemos hacer multiplicaciones recursivas para obtener potencia (M, n) en el método anterior.
Método 3 (tiempo O (log n)) A continuación se muestra una fórmula de recurrencia más interesante que se puede usar para encontrar el número n de Fibonacci en el tiempo O (log n).
Si n es par, entonces k = n / 2: F (n) = [2 * F (k-1) + F (k)] * F (k)
Si n es impar, entonces k = (n + 1) / 2 F (n) = F (k) * F (k) + F (k-1) * F (k-1) ¿Cómo funciona esta fórmula? La fórmula se puede derivar de la ecuación matricial anterior. fibonaccimatrix
Tomando determinante en ambos lados, obtenemos (-1) n = Fn + 1Fn-1 - Fn2 Además, dado que AnAm = An + m para cualquier matriz cuadrada A, se pueden derivar las siguientes identidades (se obtienen de dos coeficientes diferentes de el producto de matriz)
FmFn + Fm-1Fn-1 = Fm + n-1
Al poner n = n + 1,
FmFn + 1 + Fm-1Fn = Fm + n
Poniendo m = n
F2n-1 = Fn2 + Fn-12
F2n = (Fn-1 + Fn + 1) Fn = (2Fn-1 + Fn) Fn (Fuente: Wiki)
Para que la fórmula sea probada, simplemente necesitamos hacer lo siguiente Si n es par, podemos poner k = n / 2 Si n es impar, podemos poner k = (n + 1) / 2
Método 4: uso de una fórmula En este método, implementamos directamente la fórmula para el enésimo término de la serie de Fibonacci. Tiempo O (1) Espacio O (1) Fn = {[(√5 + 1) / 2] ^ n} / √5
Referencia: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html
fuente
Primero debemos notar que los números de Fibonacci
(F(n))
crecen muy rápido conn
y no se pueden representar en 64 bits paran
más de 93. Entonces, un programa para calcularlos para talesn
necesidades debe usar mecanismos adicionales para operar en estos números grandes. Ahora, considerando solo el recuento de operaciones (de gran número), el algoritmo para calcularlas secuencialmente requerirá un número lineal de operaciones.Podemos beneficiarnos de la siguiente identidad sobre los números de Fibonacci:
(un símbolo como A ^ 2 denota el cuadrado de A).
Entonces, si conocemos
F(m)
yF(m+1)
, podemos calcular directamenteF(2m)
yF(2m+1)
.Considere la representación binaria de
n
. Observe que comenzando conx = 1
, podemos hacerx = n
duplicando iterativamente y posiblemente agregando 1 ax
. Esto se puede hacer iterando sobre los bits den
y verificando si es 0 o 1.La idea es que podamos mantenernos
F(x)
sincronizados conx
. En cada una de esas iteraciones, a medida que duplicamosx
y posiblemente agregamos 1x
, también podemos calcular el nuevo valor deF(x)
usar el valor anterior deF(x)
yF(x+1)
, con las ecuaciones anteriores.Dado que el número de iteraciones será logarítmico en
n
, las operaciones totales (números grandes) también son logarítmicas enn
.Para obtener más detalles, consulte la sección "Algoritmo mejorado" de este artículo .
fuente