Mi opinión es que cualquiera que escriba código para ganarse la vida debe comprender la idea de Big O. No es necesario que pueda presentar pruebas matemáticas rigurosas que indiquen que un fragmento de código es O (lo que sea).
Para fines prácticos de ingeniería, Big O básicamente comprende la relación entre el tamaño de un problema y el tiempo que tomará resolverlo.
O (1) significa que el tiempo que lleva resolver un problema no varía según el tamaño del problema. Ejemplos:
- agregar dos enteros de tamaño fijo (por ejemplo, 8 bits, 32 bits, 64 bits, 128 bits, lo que sea) juntos (esto excluye los enteros que pueden ser de tamaño arbitrario, como los proporcionados en las bibliotecas BigInteger), (se aplica igualmente a otras funciones aritméticas simples en valores de tamaño fijo)
- agregar un elemento a un hashset o buscar si un elemento ya está en un hashset (suponiendo que no haya colisiones),
- recuperar el enésimo elemento de una matriz o colección basada en una matriz,
- empujando o haciendo estallar un elemento dentro o fuera de una pila,
- poner en cola o eliminar un artículo de una cola,
- insertar un nodo o conjunto de nodos en una lista vinculada siempre que ya tenga los nodos a los que deberá conectarlos.
- Comparar dos valores de tamaño fijo (como dos enteros)
- Mover un valor a otra ubicación en la memoria donde el espacio ya está asignado
O (log n) significa que el tiempo que llevará completar el algoritmo aumentará logarítmicamente a medida que aumente el tamaño del problema. Obviamente, esto no es tan bueno como O (1) pero sigue siendo bastante bueno.
Así que imagine que una función foobar (x) necesitará realizar operaciones log (x.Size) para obtener su valor de retorno. Suponga que cada operación toma .1 microsegundos. Entonces (redondeando hacia arriba) si x es tamaño 1000, entonces log2 (1000) = 10, entonces la cantidad de tiempo (en un mundo perfecto) para completar será de 1 microsegundo (10 * .1 microsegundos). Ahora, ¿qué tan grande debería ser x para duplicar ese tiempo de 1 microsegundo? Bueno, log2 (x) = 20 – resuelve para x. Resulta que la respuesta es 1,000,000.0. Por lo tanto, el tamaño del problema tuvo que aumentar mil veces el tiempo que tardó en duplicarse .
Ejemplos de operaciones O (log n)
1- encontrar un número en una matriz ordenada usando búsqueda binaria o un árbol de búsqueda binaria
2- insertar o eliminar un elemento de un árbol de búsqueda binario equilibrado
3- usando el algoritmo de euclides para encontrar el mcd de dos enteros de tamaño fijo
O (n) –
Estas operaciones hacen que la cantidad de tiempo crezca linealmente con el tamaño del problema. Muy claro.
Ejemplos
1- bucle sobre una serie de enteros
2- insertar un elemento en una matriz de enteros (en un lugar que no sea el principio o el final, o en cualquier lugar cuando la matriz haya alcanzado su capacidad)
3- ubicar el enésimo elemento en una lista vinculada
4- enumerar todos los elementos en un árbol de búsqueda binario balanceado
O (n log n)
Estas operaciones llevan más tiempo que las operaciones lineales, pero no tan horriblemente. El ejemplo clásico es un algoritmo de clasificación eficiente, como Heapsort.
O (n ^ 2)
El tiempo aquí crece polinomialmente con respecto al tamaño del problema. Para tamaños de problema razonables, estos problemas pueden resolverse con computadoras modernas. Para grandes cantidades, puede llevar bastante tiempo.
Ejemplo: bucles anidados de tamaño n.
Sin embargo, señalaré que una de las cosas más importantes que eliminé de mi estudio formal de CS es que a menudo, aunque no siempre, se puede superar el tiempo polinómico. Por ejemplo, el tipo de burbuja (el tipo de clasificación que probablemente se te ocurrió cuando eras niño tratando de descubrir cómo ordenar elementos en una matriz) es O (n ^ 2). Pero un tipo más eficiente puede darle O (n log n) en su lugar.
O, por ejemplo, escriba una función que dado un entero n devuelve n agregado por todos los enteros anteriores a uno. Por ejemplo 5 => 5 + 4 + 3 + 2+ 1 => 15. La forma en que se te ocurre inmediatamente toma O (n). Pero se puede hacer en O (1) si conoce el algoritmo correcto o puede derivarlo de su conocimiento matemático. Cosas ordenadas.
De todos modos, realmente no hago este análisis en mi código muy a menudo porque la mayor parte de mi trabajo está fuertemente vinculado a E / S de todos modos. Eso no significa que no sepa la diferencia entre encontrar un número en una matriz sin clasificar frente a un hashset. O la complejidad temporal básica de las operaciones en la biblioteca .NET que uso con frecuencia. Creo que eso es todo lo que la mayoría de los programadores necesitan saber.