Complejidad computacional de la Secuencia de Fibonacci


Entiendo la notación Big-O, pero no se como calcularla para muchas funciones. En particular, he estado tratando de averiguar la complejidad computacional de la versión ingenua de la secuencia de Fibonacci:

int Fibonacci(int n)
{
    if (n <= 1)
        return n;
    else
        return Fibonacci(n - 1) + Fibonacci(n - 2);
}

¿Cuál es la complejidad computacional de la secuencia de Fibonacci y cómo se calcula?

Author: Tony Tannous, 2008-12-11

11 answers

Modela la función de tiempo para calcular Fib(n) como la suma del tiempo para calcular Fib(n-1) más el tiempo para calcular Fib(n-2) más el tiempo para sumarlos (O(1)).

T(n<=1) = O(1)

T(n) = T(n-1) + T(n-2) + O(1)

Resuelves esta relación de recurrencia (usando funciones generadoras, por ejemplo) y terminarás con la respuesta.

Alternativamente, puede dibujar el árbol de recursión, que tendrá profundidad n e intuitivamente averiguar que esta función es asintóticamente O(2n). Entonces puedes probar tu conjetura por inducción.

Base: n = 1 es obvio

Asumir T(n-1) = O(2n-1), por lo tanto

T(n) = T(n-1) + T(n-2) + O(1) que es igual a

T(n) = O(2n-1) + O(2n-2) + O(1) = O(2n)

Sin embargo, como se señaló en un comentario, este no es el límite apretado. Un hecho interesante acerca de esta función es que la T(n) es asintóticamente la misma como el valor de Fib(n) ya que ambos se definen como

f(n) = f(n-1) + f(n-2).

Las hojas del árbol de recursión siempre devolverán 1. El valor de Fib(n) es la suma de todos los valores devueltos por las hojas en el árbol de recursión que es igual a la cuenta de hojas. Dado que cada hoja tomará O (1) para calcular, T(n) es igual a Fib(n) x O(1). En consecuencia, el límite apretado para esta función es la secuencia de Fibonacci en sí (~θ(1.6n)). Usted puede encontrar este apretado límite mediante el uso de generar funciones como mencioné anteriormente.

 304
Author: Mehrdad Afshari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-13 20:23:30

Solo pregúntese cuántas instrucciones deben ejecutarse para que F(n) se complete.

Para F(1), la respuesta es 1 (la primera parte del condicional).

Para F(n), la respuesta es F(n-1) + F(n-2).

Entonces, ¿qué función satisface estas reglas? Prueba a n (a > 1):

Unn == a(n-1) + e(n-2)

Dividir por a (n-2):

A2 == a + 1

Resuelve para a y obtienes (1+sqrt(5))/2 = 1.6180339887, de lo contrario conocido como el proporción áurea.

Así que toma tiempo exponencial.

 107
Author: Jason Cohen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-04 09:02:20

Hay una muy buena discusión de este problema específico en MIT. En la página 5, hacen el punto de que, si se asume que una suma toma una unidad computacional, el tiempo requerido para calcular Fib(N) está muy estrechamente relacionado con el resultado de Fib(N).

Como resultado, puede saltar directamente a la aproximación muy cercana de la serie de Fibonacci:

Fib(N) = (1/sqrt(5)) * 1.618^(N+1) (approximately)

Y decir, por lo tanto, que el peor rendimiento del caso del algoritmo ingenuo es

O((1/sqrt(5)) * 1.618^(N+1)) = O(1.618^(N+1))

PD: Hay es una discusión de la expresión de forma cerrada del Enésimo número de Fibonacci en Wikipedia si desea más información.

 27
Author: Bob Cross,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-11 21:21:14

Estoy de acuerdo con pgaur y rickerbh, la complejidad recursiva de fibonacci es O(2^n).

Llegué a la misma conclusión por un razonamiento bastante simplista pero creo que sigue siendo válido.

Primero, se trata de averiguar cuántas veces se llama a la función de fibonacci recursiva ( F() a partir de ahora ) al calcular el Enésimo número de fibonacci. Si se llama una vez por número en la secuencia de 0 a n, entonces tenemos O(n), si se llama n veces para cada número, entonces obtenemos O (n*n), o O (n^2), y así sucesivamente.

Así, cuando se llama a F() para un número n, el número de veces que se llama a F() para un número dado entre 0 y n-1 crece a medida que nos acercamos a 0.

Como primera impresión, me parece que si lo ponemos de una manera visual, dibujando una unidad por tiempo F() se llama para un número dado, wet obtener una especie de forma de pirámide (es decir, si centramos unidades horizontalmente). Algo como esto:

n              *
n-1            **
n-2           ****  
...
2           ***********
1       ******************
0    ***************************

Ahora, la pregunta es, ¿qué tan rápido se agranda la base de esta pirámide como n crece?

Tomemos un caso real, por ejemplo F(6)

F(6)                 *  <-- only once
F(5)                 *  <-- only once too
F(4)                 ** 
F(3)                ****
F(2)              ********
F(1)          ****************           <-- 16
F(0)  ********************************    <-- 32

Vemos F(0) se llama 32 veces, que es 2^5, que para este caso de ejemplo es 2^(n-1).

Ahora, queremos saber cuántas veces se llama a F(x), y podemos ver que el número de veces que se llama a F(0) es solo una parte de eso.

Si mentalmente movemos todas las *'s de F(6) a F(2) líneas en F(1) línea, vemos que F(1) y F(0) líneas ahora son iguales en longitud. Lo que significa, total times F () se llama cuando n = 6 es 2x32 = 64 = 2^6.

Ahora, en términos de complejidad:

O( F(6) ) = O(2^6)
O( F(n) ) = O(2^n)
 24
Author: J.P.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-15 21:38:56

Puedes expandirlo y tener una visulización

     T(n) = T(n-1) + T(n-2) <
     T(n-1) + T(n-1) 

     = 2*T(n-1)   
     = 2*2*T(n-2)
     = 2*2*2*T(n-3)
     ....
     = 2^i*T(n-i)
     ...
     ==> O(2^n)
 11
Author: Tony Tannous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-10 15:39:12

Está limitada en el extremo inferior por 2^(n/2) y en el extremo superior por 2^n (como se indica en otros comentarios). Y un hecho interesante de esa implementación recursiva es que tiene un límite asintótico apretado de Fib(n) en sí. Estos hechos pueden resumirse:

T(n) = Ω(2^(n/2))  (lower bound)
T(n) = O(2^n)   (upper bound)
T(n) = Θ(Fib(n)) (tight bound)

El límite apretado se puede reducir aún más usando su forma cerrada si lo desea.

 8
Author: Dave L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-11 21:03:08

Las respuestas de prueba son buenas, pero siempre tengo que hacer algunas iteraciones a mano para convencerme realmente. Así que dibujé un pequeño árbol de llamadas en mi pizarra y empecé a contar los nodos. Divido mis cuentas en nodos totales, nodos hoja y nodos interiores. Esto es lo que tengo:

IN | OUT | TOT | LEAF | INT
 1 |   1 |   1 |   1  |   0
 2 |   1 |   1 |   1  |   0
 3 |   2 |   3 |   2  |   1
 4 |   3 |   5 |   3  |   2
 5 |   5 |   9 |   5  |   4
 6 |   8 |  15 |   8  |   7
 7 |  13 |  25 |  13  |  12
 8 |  21 |  41 |  21  |  20
 9 |  34 |  67 |  34  |  33
10 |  55 | 109 |  55  |  54

Lo que salta inmediatamente es que el número de nodos de la hoja es fib(n). Lo que tomó algunas iteraciones más para notar es que el número de nodos interiores es fib(n) - 1. Por lo tanto, el número total de nodos es 2 * fib(n) - 1.

Dado que se eliminan los coeficientes al clasificar la complejidad computacional, la respuesta final es θ(fib(n)).

 8
Author: benkc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-28 01:21:33

Bueno, de acuerdo a mí es O(2^n) como en esta función solo la recursión está tomando el tiempo considerable (divide y vencerás). Vemos que la función anterior continuará en un árbol hasta que las hojas se aproximen cuando alcancemos el nivel F(n-(n-1)), es decir F(1). Entonces, aquí cuando anotamos la complejidad de tiempo encontrada en cada profundidad del árbol, la serie de suma es:

1+2+4+.......(n-1)
= 1((2^n)-1)/(2-1)
=2^n -1

Ese es el orden de 2^n [ O(2^n) ].

 2
Author: pgaur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-18 01:24:21
 1
Author: nsh3,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-28 20:33:32

La versión recursiva ingenua de Fibonacci es exponencial por diseño debido a la repetición en el cálculo:

En la raíz estás computando:

F(n) depende de F(n-1) y F(n-2)

F(n-1) depende de F(n-2) de nuevo y F (n-3)

F(n-2) depende de F(n-3) y F(n-4)

Entonces usted está teniendo en cada nivel 2 llamadas recursivas que están perdiendo una gran cantidad de datos en el cálculo, la función de tiempo se verá así:

T (n) = T (n-1) + T (n-2) + C, con C constante

T (n-1) = T(n-2) + T(n-3) > T (n-2) entonces

T (n) > 2 * T (n-2)

...

T (n) > 2^(n/2) * T(1) = O(2^(n/2))

Esto es solo un límite inferior que para el propósito de su análisis debería ser suficiente, pero la función en tiempo real es un factor de una constante por la misma fórmula de Fibonacci y la forma cerrada se sabe que es exponencial de la razón áurea.

Además, puede encontrar versiones optimizadas de Fibonacci utilizando Dynamic programación como esta:

static int fib(int n)
{
    /* memory */
    int f[] = new int[n+1];
    int i;

    /* Init */
    f[0] = 0;
    f[1] = 1;

    /* Fill */
    for (i = 2; i <= n; i++)
    {
        f[i] = f[i-1] + f[i-2];
    }

    return f[n];
}

Que está optimizado y hacer solo n pasos, pero también es exponencial.

Las funciones de costo se definen desde el tamaño de la entrada hasta el número de pasos para resolver el problema. Cuando vea la versión dinámica de Fibonacci (n pasos para calcular la tabla) o el algoritmo más fácil para saber si un número es primo (sqrt (n) para analizar los divisores válidos del número). usted puede pensar que estos algoritmos son O(n) o O(sqrt(n)) pero esto simplemente no es cierto por la siguiente razón: La entrada a su algoritmo es un número: n , usando la notación binaria el tamaño de entrada para un entero n es log2 (n) luego haciendo un cambio variable de

m = log2(n) // your real input size

Vamos a averiguar el número de pasos en función del tamaño de entrada

m = log2(n)
2^m = 2^log2(n) = n

, Entonces el costo de su algoritmo en función del tamaño de entrada es:

T(m) = n steps = 2^m steps

Y esta es la razón por la que el costo es exponencial.

 0
Author: Miguel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-30 22:21:52

Esto funciona mucho mejor:

unsigned int Fib(unsigned int n)
{
    // first Fibonaci number is Fib(0)
    // second one is Fib(1) and so on

    // unsigned int m;  // m + current_n = original_n
    unsigned int a = 1; // Fib(m)
    unsigned int b = 0; // Fib(m-1)
    unsigned int c = 0; // Fib(m-2)

    while (n--)
    {
        c = b;
        b = a;
        a = b+c;
    }

    return a;
}
 -4
Author: pyon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-20 21:10:16