Evaluación de la complejidad del tiempo y del espacio del algoritmo

Por lo general, para un algoritmo dado, hacemos dos análisis. El primero es demostrar matemáticamente la exactitud del algoritmo. Este paso utiliza principalmente métodos de prueba formales y modos de razonamiento relacionados, como los invariantes de bucle y la inducción matemática. A partir de demostrar que el algoritmo es correcto, la segunda parte consiste en analizar la complejidad temporal del algoritmo . La complejidad temporal del algoritmo refleja la magnitud del aumento en el tiempo de ejecución del programa con el aumento de la escala de entrada y, en gran medida, puede reflejar bien los pros y los contras del algoritmo. Por lo tanto, como programador, es necesario dominar el método de análisis de complejidad del tiempo del algoritmo básico.
El tiempo de ejecución del algoritmo debe medirse por el tiempo consumido cuando el programa compilado de acuerdo con el algoritmo se ejecuta en la computadora. Por lo general, hay dos formas de medir el tiempo de ejecución de un programa.

1. El método de las estadísticas posteriores al evento

Este método es factible, pero no es un buen método. Este método tiene dos defectos: uno es que si desea evaluar el rendimiento del algoritmo diseñado, primero debe compilar el programa correspondiente basado en el algoritmo y ejecutarlo; el otro es que las estadísticas de tiempo obtenidas dependen del hardware de la computadora , software y otros factores ambientales. A veces es fácil ocultar las ventajas del algoritmo en sí.

2. Métodos de preanálisis y estimación

Debido a que los métodos estadísticos posteriores al evento se basan más en el hardware, el software y otros factores ambientales de la computadora, a veces es fácil ocultar las ventajas y desventajas del algoritmo en sí. Por lo tanto, la gente suele utilizar el método de preanálisis y estimación.

Antes de escribir el programa, estime el algoritmo basándose en métodos estadísticos. El tiempo que consume un programa escrito en un lenguaje de alto nivel para ejecutarse en una computadora depende de los siguientes factores:

  (1). 算法采用的策略、方法;(2). 编译产生的代码质量;(3). 问题的输入规模;(4).  机器执行指令的速度。

Un algoritmo se compone de la estructura de control (secuencia, rama y bucle) y la operación original (refiriéndose a la operación del tipo de datos inherente), y el tiempo del algoritmo depende del efecto combinado de los dos. Para facilitar la comparación de diferentes algoritmos para el mismo problema, el enfoque habitual es seleccionar una operación original del algoritmo que sea una operación básica para el problema (o tipo de algoritmo) que se está estudiando, y tomar el número de ejecuciones repetidas de la operación básica como La medición del tiempo del algoritmo.

1. Complejidad del tiempo

(1) Frecuencia de tiempo El tiempo que tarda en ejecutarse un algoritmo no se puede calcular teóricamente y solo se puede conocer ejecutando una prueba en la computadora. Pero es imposible e innecesario para nosotros probar cada algoritmo en la computadora, solo necesitamos saber qué algoritmo toma más tiempo y qué algoritmo toma menos tiempo. Y el tiempo empleado por un algoritmo es directamente proporcional al número de ejecuciones de la declaración en el algoritmo. Cuanto mayor es el número de ejecuciones de la declaración en el algoritmo, más tiempo toma. El número de ejecuciones de declaraciones en un algoritmo se denomina frecuencia de declaraciones o frecuencia de tiempo. Denotado como T (n).
(2) Complejidad del tiempo En la frecuencia de tiempo que se acaba de mencionar, n se denomina escala del problema Cuando n cambia constantemente, la frecuencia de tiempo T (n) también cambiará constantemente. Pero a veces queremos saber qué patrón presenta cuando cambia. Para ello, introducimos el concepto de complejidad temporal. En general, el número de ejecuciones repetidas de la operación básica en el algoritmo es una función determinada de la escala del problema n, representada por T (n), si hay una función auxiliar determinada f (n), de modo que cuando n se aproxima a infinito , T (El valor límite de n) / f (n) es una constante que no es igual a cero, y se dice que f (n) es una función del mismo orden de magnitud de T (n). Denominado como T (n) = O (f (n)), llame a O (f (n)) la complejidad temporal progresiva del algoritmo, denominada complejidad temporal.

Además, el símbolo de Landau utilizado en la fórmula anterior fue introducido por primera vez por el teórico de números alemán Paul Bachmann en su libro de 1892 "Teoría analítica de números", por otro teórico de números alemán Edmund Promotion por Edmund Landau. El papel de la notación de Landau es utilizar funciones simples para describir el comportamiento de funciones complejas, dando un límite superior o inferior (exacto). Generalmente, solo se usa el símbolo O grande al calcular la complejidad del algoritmo, y el símbolo O pequeño, el símbolo Θ, etc. en la simbología de Landau se usan con menos frecuencia. La O aquí era originalmente la letra griega mayúscula, pero ahora todos usan la letra inglesa mayúscula O; el símbolo o pequeño también usa la letra inglesa minúscula o, y el símbolo Θ mantiene la letra griega mayúscula Θ.

T (n) = Ο (f (n)) significa que hay una constante C tal que cuando n se acerca al infinito positivo, siempre hay T (n) ≤ C * f (n). En pocas palabras, T (n) es tan grande como f (n) cuando n se acerca al infinito positivo. Es decir, cuando n tiende a infinito positivo, el límite superior de T (n) es C * f (n). Aunque no existe una regulación sobre f (n), generalmente es la función más simple posible. Por ejemplo, O (2n2 + n +1) = O (3n2 + n + 3) = O (7n2 + n) = O (n2), generalmente solo use O (n2) para representarlo. Tenga en cuenta que hay una constante C oculta en el símbolo O grande, por lo que generalmente no se agrega ningún coeficiente af (n). Si T (n) se considera un árbol, entonces lo que O (f (n)) expresa es el tronco, y solo se preocupa por el tronco principal, descartando todas las demás minucias.
En varios algoritmos, si el número de ejecuciones de oraciones en el algoritmo es una constante, la complejidad del tiempo es O (1). Además, cuando la frecuencia del tiempo no es la misma, la complejidad del tiempo puede ser la misma, como T ( n) = n2 + 3n + 4 y T (n) = 4n2 + 2n + 1 tienen frecuencias diferentes, pero la complejidad del tiempo es la misma, ambos son O (n2). Organizados en orden creciente de magnitud, la complejidad de tiempo común son: orden constante O (1), orden logarítmico O (log2n), orden lineal O (n), orden logarítmico lineal O (nlog2n), orden cuadrada O (n2), orden cúbica O (n3), ..., k-ésimo orden O (nk), orden exponencial O (2n). Con el aumento continuo de la escala del problema n, la complejidad de tiempo mencionada anteriormente continúa aumentando y la eficiencia de ejecución del algoritmo se vuelve menor.
Inserte la descripción de la imagen aquí
Se puede ver en la figura que debemos elegir algoritmos de orden polinómico O (nk) tanto como sea posible, en lugar de algoritmos de orden exponencial.

  常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)

   一般情况下,对一个问题(或一类算法)只需选择一种基本操作来讨论算法的时间复杂度即可,有时也需要同时考虑几种基本操作,甚至可以对不同的操作赋予不同的权值,以反映执行不同操作所需的相对时间,这种做法便于综合比较解决同一问题的两种完全不同的算法。

(3) Los pasos específicos para resolver la complejidad temporal del algoritmo son :

⑴ Descubra las frases básicas del algoritmo;

La declaración que se ejecuta con más frecuencia en el algoritmo es la declaración básica, que suele ser el cuerpo del bucle del bucle más interno.

⑵ Calcular el orden de magnitud de los tiempos de ejecución del enunciado básico;

Solo se calcula el orden de magnitud de los tiempos de ejecución del enunciado básico, lo que significa que siempre que la potencia más alta en la función de los tiempos de ejecución del enunciado básico sea correcta, todos los coeficientes de la potencia baja y la potencia más alta puede ignorarse. Esto simplifica el análisis del algoritmo y centra la atención en el punto más importante: la tasa de crecimiento.

⑶ El rendimiento temporal del algoritmo está representado por un símbolo Ο grande.

Ponga el orden de magnitud del número de ejecuciones de la oración básica en el símbolo Ο grande.
Si el algoritmo contiene bucles anidados, la declaración básica suele ser el cuerpo del bucle más interno. Si el algoritmo contiene bucles paralelos, se agrega la complejidad de tiempo de los bucles paralelos. P.ej:

  for (i=1; i<=n; i++)
         x++;
  for (i=1; i<=n; i++)
       for (j=1; j<=n; j++)
            x++;

La complejidad temporal del primer ciclo for es Ο (n), y la complejidad temporal del segundo ciclo for es Ο (n2), entonces la complejidad temporal de todo el algoritmo es Ο (n + n2) = Ο (n2).

Ο (1) significa que el número de ejecuciones del enunciado básico es una constante. En términos generales, siempre que no haya un enunciado de bucle en el algoritmo, la complejidad de tiempo es Ο (1). Entre ellos, Ο (log2n), Ο (n), Ο (nlog2n), Ο (n2) y Ο (n3) se denominan tiempos polinomiales, y Ο (2n) y Ο (n!) Se denominan tiempos exponenciales. Los informáticos generalmente creen que el primero (es decir, el algoritmo de complejidad de tiempo polinomial) es un algoritmo eficaz. Estos problemas se denominan problemas P (polinomios, polinomiales) y el segundo (es decir, el algoritmo de complejidad de tiempo exponencial) es problema llamado NP (polinomio no determinista, polinomio no determinista).

En términos generales, la complejidad del nivel de polinomio es aceptable y muchos problemas tienen soluciones a nivel de polinomio, es decir, para tal problema, para una entrada de tamaño n, el resultado se obtiene en n ^ k tiempo, dicho para Pregunta P. Algunos problemas son más complicados y no tienen una solución de tiempo polinomial, pero puede verificar si una conjetura es correcta en tiempo polinomial. Por ejemplo, ¿4294967297 es un número primo? Si desea comenzar directamente, saque todos los números primos que sean menores que la raíz cuadrada de 4294967297 y vea si pueden ser divisibles. Afortunadamente, Euler nos dijo que este número es igual al producto de 641 y 6700417. No es un número primo y es muy fácil de verificar Por cierto, dígale a Fermat que su conjetura no es válida. Problemas como la descomposición de números grandes y los ciclos de Hamilton pueden verificar si una "solución" es correcta en el tiempo polinomial, estos problemas se denominan problemas NP.

(4) Existen varias reglas simples de análisis de programas al calcular la complejidad temporal del algoritmo:

(1). Para algunas declaraciones simples de entrada y salida o declaraciones de asignación, se considera aproximadamente que toma O (1) tiempo

(2). Para la estructura secuencial, el tiempo requerido para ejecutar una serie de declaraciones en secuencia puede usar la "regla de suma" debajo de la gran O

La ley de la suma: significa que si la complejidad temporal de las dos partes del algoritmo es T1 (n) = O (f (n)) y T2 (n) = O (g (n)), entonces T1 (n) + T2 (n) = O (máx. (F (n), g (n)))

En particular, si T1 (m) = O (f (m)), T2 (n) = O (g (n)), entonces T1 (m) + T2 (n) = O (f (m) + g ( norte))

(3). Para la estructura de selección, como la instrucción if, su principal consumo de tiempo es el tiempo utilizado para ejecutar la oración then o la oración else. Cabe señalar que las condiciones de prueba también requieren O (1) tiempo

(4). Para la estructura del bucle, el tiempo de ejecución de la declaración del bucle se refleja principalmente en el consumo de tiempo de ejecutar el cuerpo del bucle y comprobar las condiciones del bucle en múltiples iteraciones. Generalmente, la "regla de la multiplicación" bajo la gran O puede ser usado.

Regla de multiplicación: si la complejidad temporal de las dos partes del algoritmo es T1 (n) = O (f (n)) y T2 (n) = O (g (n)), entonces T1 * T2 = O (f ( n) * g (n))

(5). Para un algoritmo complejo, se puede dividir en varias partes fáciles de estimar y luego la complejidad temporal de todo el algoritmo utilizando la tecnología de la regla de suma y la regla de multiplicación.

Además, existen los dos algoritmos siguientes: (1) Si g (n) = O (f (n)), entonces O (f (n)) + O (g (n)) = O (f (n) ); (2) O (Cf (n)) = O (f (n)), donde C es un número normal

(5) Los siguientes son ejemplos de varias complejidades de tiempo comunes:

(1) 、 O (1)

Temp=i; 
i=j; 
j=temp;  

La frecuencia de las tres declaraciones individuales anteriores es 1, y el tiempo de ejecución de este segmento de programa es una constante que no tiene nada que ver con la escala del problema n. La complejidad temporal del algoritmo es un orden constante, denotado como T (n) = O (1). Nota: Si el tiempo de ejecución del algoritmo no aumenta con el aumento del tamaño del problema n, incluso si hay miles de declaraciones en el algoritmo, el tiempo de ejecución es solo una constante relativamente grande. La complejidad temporal de este tipo de algoritmo es O (1).
(2), O (n2)

2.1. Intercambiar el contenido de i y j

     sum=0;                 (一次)
     for(i=1;i<=n;i++)     (n+1次)
        for(j=1;j<=n;j++) (n2次)
         sum++;            (n2次)

Solución: Como Θ (2n2 + n + 1) = n2 (Θ es: eliminar los términos de orden inferior, eliminar los términos constantes y eliminar los parámetros comunes de los términos de orden superior para obtener), por lo que T (n) = = O (n2);

2.2.

   for (i=1;i<n;i++)
    {
    
     
        y=y+1;for (j=0;j<=(2*n);j++)    
           x++;}    

Solución: La frecuencia de la oración 1 es n-1
La frecuencia de la oración 2 es (n-1) * (2n + 1) = 2n2-n-1
f (n) = 2n2-n-1 + (n-1) = 2n2-2;

    又Θ(2n2-2)=n2
      该程序的时间复杂度T(n)=O(n2).  

En circunstancias normales, solo es necesario considerar el número de ejecuciones de la declaración en el cuerpo del bucle para la declaración del bucle de pasos, ignorando la longitud del paso más 1, el juicio de valor final, la transferencia de control y otros componentes en la declaración. Cuando hay varios bucles declaraciones, el tiempo del algoritmo es complicado.El grado está determinado por la frecuencia f (n) de la oración más interna en la oración de bucle con la mayoría de los niveles de anidamiento.

(3) 、 O (n)

    a=0;
    b=1;for (i=1;i<=n;i++){
    
      
       s=a+b;    ③
       b=a;     ④  
       a=s;     ⑤
    }

Solución: La frecuencia de la oración 1: 2,
la frecuencia de la oración 2: n,
la frecuencia de la oración 3: n-1,
la frecuencia de la oración 4: n-1,
la frecuencia de la oración 5: n-1,
T ( n) = 2 + n + 3 (n-1) = 4n-1 = O (n).
(4), O (log2n)

     i=1;while (i<=n)
       i=i*2;

Solución: La frecuencia de la oración 1 es 1 y
la frecuencia de la oración 2 es f (n), entonces: 2 ^ f (n) <= n; f (n) <= log2n
tome el valor máximo f (n) = log2n,
T (n) = O (log2n)

(5) 、 O (n3)

for(i=0;i<n;i++)
    {
    
      
       for(j=0;j<i;j++)  
       {
    
    
          for(k=0;k<j;k++)
             x=x+2;  
       }
    }

Solución: Cuando i = m, j = k, el número de bucles internos es k. Cuando i = m, j puede tomar 0,1, ..., m-1, por lo que el bucle más interno aquí tiene un total de 0+ 1 +… + m-1 = (m-1) m / 2 veces. Por lo tanto, si se toma i de 0 an, el ciclo se realiza: 0+ (1-1) * 1/2 +… + ( n-1) n / 2 = n (n + 1) (n-1) / 6 por lo que la complejidad del tiempo es O (n3).

(5) La complejidad temporal y espacial de los algoritmos de uso común son
Inserte la descripción de la imagen aquí
una regla empírica: donde c es una constante. Si la complejidad de un algoritmo es c, log2n, n, n * log2n, entonces la eficiencia temporal de este algoritmo es relativamente alto, si es 2n, 3n, n !, entonces un n un poco más grande hará que el algoritmo no pueda moverse, y los del medio no son satisfactorios.

El análisis de la complejidad del tiempo de los algoritmos es un tema muy importante, cualquier programador debe ser competente en sus conceptos y métodos básicos, y debe ser bueno para explorar su esencia desde un nivel matemático para comprender con precisión su significado.

2. La complejidad espacial del algoritmo

Similar a la discusión de la complejidad del tiempo, la complejidad del espacio (Complejidad del espacio) S (n) de un algoritmo se define como el espacio de almacenamiento consumido por el algoritmo, que también es una función del tamaño del problema n. La complejidad del espacio asintótico a menudo se denomina simplemente complejidad del espacio.
La complejidad espacial (complejidad espacial) es una medida de la cantidad de espacio de almacenamiento que un algoritmo ocupa temporalmente durante su funcionamiento. El espacio de almacenamiento ocupado por un algoritmo en la memoria de la computadora incluye el espacio de almacenamiento ocupado por el propio algoritmo de almacenamiento, el espacio de almacenamiento ocupado por los datos de entrada y salida del algoritmo y el espacio de almacenamiento ocupado temporalmente por el algoritmo durante la operación. El espacio de almacenamiento que ocupan los datos de entrada y salida del algoritmo viene determinado por el problema a resolver, se pasa de la función que llama a través de la tabla de parámetros y no cambia con el algoritmo. El espacio de almacenamiento que ocupa el propio algoritmo de almacenamiento es proporcional a la longitud del algoritmo, para comprimir el espacio de almacenamiento en esta área, se debe escribir un algoritmo más corto. El espacio de almacenamiento que ocupa temporalmente el algoritmo durante la operación varía con el algoritmo. Algunos algoritmos solo ocupan una pequeña cantidad de unidades de trabajo temporales y no cambian con el tamaño del problema. A este algoritmo lo llamamos "in situ". Es un algoritmo de ahorro de almacenamiento, como los varios algoritmos introducidos en esta sección; el número de unidades de trabajo temporal que necesitan ocupar algunos algoritmos está relacionado con la escala n del problema, y ​​aumenta con el aumento de n. Cuando n Si es grande, habrá más unidades de almacenamiento ocupadas. Por ejemplo, los algoritmos de clasificación rápida y clasificación combinada que se presentarán en el Capítulo 9 entran en esta situación.

Por ejemplo, cuando la complejidad espacial de un algoritmo es una constante, es decir, no cambia con la cantidad de datos procesados ​​n, se puede expresar como O (1); cuando la complejidad espacial de un algoritmo es el logaritmo de n basado en 2 Cuando es proporcional, se puede expresar como 0 (10g2n); cuando la complejidad de un algoritmo es linealmente proporcional an, se puede expresar como 0 (n). Si el parámetro formal es una matriz, solo es necesario asignarle uno. El espacio para almacenar un puntero de dirección transferido desde el parámetro real, es decir, un espacio de longitud de palabra de máquina; si el parámetro formal es una referencia, solo necesita asignar un espacio para almacenar una dirección, y utilícelo para almacenar la dirección de la variable de parámetro real correspondiente, de modo que el sistema haga referencia automáticamente a la variable de parámetro real.

Referencia:
http://www.cnblogs.com/songQQ/archive/2009/10/20/1587122.html
http://www.cppblog.com/85940806/archive/2011/03/12/141672.html
https: //blog.csdn.net/zolalad/article/details/11848739

Supongo que te gusta

Origin blog.csdn.net/wuxiaolongah/article/details/109319006
Recomendado
Clasificación