Números de Fibonacci
La sucesión de Fibonacci se define de la siguiente manera:
Los primeros elementos de la sucesión (OEIS A000045 ) son:
Propiedades
Los números de Fibonacci poseen muchas propiedades interesantes. Aquí hay algunas de ellas:
- Identidad de Cassini:
Esta identidad se puede demostrar por inducción. Una demostración de una sola línea de Knuth se obtiene tomando el determinante de la forma matricial 2x2 de más abajo.
- La regla de “suma”:
- Aplicando la identidad anterior al caso , obtenemos:
-
A partir de esto podemos demostrar por inducción que, para cualquier entero positivo , es múltiplo de .
-
El recíproco también es cierto: si es múltiplo de , entonces es múltiplo de .
-
Identidad del GCD:
- Los números de Fibonacci son las peores entradas posibles para el algoritmo de Euclides (véase el teorema de Lamé en Algoritmo de Euclides)
Codificación de Fibonacci
Podemos usar la sucesión para codificar enteros positivos en palabras de código binarias. Según el teorema de Zeckendorf, cualquier número natural se puede representar de forma única como una suma de números de Fibonacci:
tal que (es decir: la representación no puede usar dos números de Fibonacci consecutivos).
Se sigue que cualquier número se puede codificar de forma única en la codificación de Fibonacci. Y podemos describir esta representación con códigos binarios , donde es si se usa en la representación. Al código se le agregará un para indicar el final de la palabra de código. Nótese que esta es la única ocurrencia en la que aparecen dos bits 1 consecutivos.
\begin{eqnarray} 1 &=& 1 &=& F_2 &=& (11)_F \ 2 &=& 2 &=& F_3 &=& (011)_F \ 6 &=& 5 + 1 &=& F_5 + F_2 &=& (10011)_F \ 8 &=& 8 &=& F_6 &=& (000011)_F \ 9 &=& 8 + 1 &=& F_6 + F_2 &=& (100011)_F \ 19 &=& 13 + 5 + 1 &=& F_7 + F_5 + F_2 &=& (1001011)_F \end{eqnarray}
La codificación de un entero se puede hacer con un algoritmo voraz (greedy) simple:
-
Iterar por los números de Fibonacci de mayor a menor hasta encontrar uno menor o igual que .
-
Supongamos que este número fue . Restar de y poner un en la posición de la palabra de código (indexando desde 0, del bit más a la izquierda al más a la derecha).
-
Repetir hasta que no quede resto.
-
Agregar un final a la palabra de código para indicar su fin.
Para decodificar una palabra de código, primero se quita el final. Después, si el -ésimo bit está prendido (indexando desde 0, del bit más a la izquierda al más a la derecha), se suma al número.
Fórmulas para el número de Fibonacci { data-toc-label=“Fórmulas para el -ésimo número de Fibonacci” }
Expresión en forma cerrada
Hay una fórmula conocida como “fórmula de Binet”, aunque ya era conocida por Moivre:
Esta fórmula es fácil de demostrar por inducción, pero se puede deducir con ayuda del concepto de funciones generatrices o resolviendo una ecuación funcional.
Se puede notar de inmediato que el valor absoluto del segundo término siempre es menor que , y además decrece muy rápido (exponencialmente). Por lo tanto, el valor del primer término por sí solo es “casi” . Esto se puede escribir de forma estricta como:
donde los corchetes denotan el redondeo al entero más cercano.
Como estas dos fórmulas exigirían una precisión muy alta al trabajar con números fraccionarios, son de poca utilidad en cálculos prácticos.
Fibonacci en tiempo lineal
El -ésimo número de Fibonacci se puede encontrar fácilmente en calculando los números uno por uno hasta . Sin embargo, también hay formas más rápidas, como veremos.
Podemos partir de un enfoque iterativo, para aprovechar el uso de la fórmula ; por lo tanto, simplemente precalcularemos esos valores en un arreglo. Teniendo en cuenta los casos base para y .
int fib(int n) {
int a = 0;
int b = 1;
for (int i = 0; i < n; i++) {
int tmp = a + b;
a = b;
b = tmp;
}
return a;
}De esta manera obtenemos una solución lineal, de tiempo , conservando todos los valores anteriores a en la sucesión.
Forma matricial
Para pasar de a , podemos expresar la recurrencia lineal como una multiplicación de matrices 2x2:
Esto nos permite tratar la iteración de la recurrencia como una multiplicación repetida de matrices, que tiene propiedades convenientes. En particular,
^n
donde . De hecho, como
=
podemos usar la matriz de forma directa:
^n =
Así, para encontrar en tiempo , hay que elevar la matriz a la . (Véase Exponenciación binaria)
struct matrix {
long long mat[2][2];
matrix friend operator *(const matrix &a, const matrix &b){
matrix c;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c.mat[i][j] = 0;
for (int k = 0; k < 2; k++) {
c.mat[i][j] += a.mat[i][k] * b.mat[k][j];
}
}
}
return c;
}
};
matrix matpow(matrix base, long long n) {
matrix ans{ {
{1, 0},
{0, 1}
} };
while (n) {
if(n&1)
ans = ans*base;
base = base*base;
n >>= 1;
}
return ans;
}
long long fib(int n) {
matrix base{ {
{1, 1},
{1, 0}
} };
return matpow(base, n).mat[0][1];
}Método de duplicación rápida
Al expandir la expresión matricial anterior para
^{2k}
^2
podemos encontrar estas ecuaciones más simples:
.
Así, usando las dos ecuaciones anteriores, los números de Fibonacci se pueden calcular fácilmente con el siguiente código:
pair<int, int> fib (int n) {
if (n == 0)
return {0, 1};
auto p = fib(n >> 1);
int c = p.first * (2 * p.second - p.first);
int d = p.first * p.first + p.second * p.second;
if (n & 1)
return {d, c + d};
else
return {c, d};
}El código anterior devuelve y como un par.
Periodicidad módulo p
Consideremos la sucesión de Fibonacci módulo . Demostraremos que la sucesión es periódica.
Demostrémoslo por contradicción. Consideremos los primeros pares de números de Fibonacci tomados módulo :
Solo puede haber restos distintos módulo , y a lo sumo pares distintos de restos, así que hay al menos dos pares idénticos entre ellos. Esto basta para demostrar que la sucesión es periódica, ya que un número de Fibonacci solo está determinado por sus dos predecesores. Por lo tanto, si se repiten dos pares de números consecutivos, eso también significa que los números posteriores al par se repetirán de la misma manera.
Ahora elegimos dos pares de restos idénticos con los índices más chicos en la sucesión. Sean los pares y . Demostraremos que . Si esto fuera falso, habría dos pares anteriores y que, por la propiedad de los números de Fibonacci, también serían iguales. Sin embargo, esto contradice el hecho de que habíamos elegido pares con los índices más chicos, lo que completa nuestra demostración de que no hay preperíodo (es decir, los números son periódicos a partir de ).
Problemas de práctica
- SPOJ - Euclid Algorithm Revisited
- SPOJ - Fibonacci Sum
- HackerRank - Is Fibo
- Project Euler - Even Fibonacci numbers
- DMOJ - Fibonacci Sequence
- DMOJ - Fibonacci Sequence (Harder)
- DMOJ UCLV - Numbered sequence of pencils
- DMOJ UCLV - Fibonacci 2D
- DMOJ UCLV - fibonacci calculation
- LightOJ - Number Sequence
- Codeforces - C. Fibonacci
- Codeforces - A. Hexadecimal’s theorem
- Codeforces - B. Blackboard Fibonacci
- Codeforces - E. Fibonacci Number