Skip to main content

Notación Big O

La notación Big O (o "O grande") es una herramienta matemática usada para describir el comportamiento asintótico de un algoritmo, es decir, cómo crece el tiempo de ejecución o el uso de memoria en función del tamaño de la entrada n. Esta notación es esencial en el análisis de algoritmos porque permite comparar su eficiencia de forma independiente al hardware o lenguaje utilizado (Cormen et al., 2022).

Principales características

  1. Describe el peor caso (upper bound) de crecimiento de un algoritmo.
  2. Ignora constantes y términos de menor orden porque, a gran escala, no afectan significativamente el crecimiento.
  3. Permite clasificar algoritmos en categorías de eficiencia.

Categorías comunes de complejidad

  • O(1)O(1): Tiempo constante. El tiempo de ejecución no depende de n.
  • O(logn)O(log n): Tiempo logarítmico. Ejemplo: búsqueda binaria.
  • O(n)O(n): Tiempo lineal. Ejemplo: recorrer un arreglo completo.
  • O(nlogn)O(n log n): Combinación. Ejemplo: algoritmos de ordenamiento eficientes como MergeSort.
  • O(n2)O({n}^{2}): Tiempo cuadrático. Ejemplo: doble bucle anidado.
  • O(2n)O({2}^{n}): Tiempo exponencial. Ejemplo: algoritmos de fuerza bruta.

Ejemplo

Si un algoritmo tiene las siguientes operaciones:

  • 5 operaciones iniciales (constante)
  • Un bucle que se ejecuta n veces
  • Un bucle interno que se ejecuta n veces

El número total de operaciones será aproximadamente:

5+n+n2n2O(n2)5 + n + {n}^{2} \approx {n}^{2} \Longrightarrow O({n}^{2})

Flujo conceptual de análisis Big O

Ejemplo técnico

/**
* Class demonstrating algorithms with different Big O complexities.
*/
public class ComplexityExamples {

// O(1): Constant time
public int getFirstElement(int[] array) {
return array[0];
}

// O(n): Linear time
public boolean contains(int[] array, int target) {
for (int value : array) {
if (value == target) {
return true;
}
}
return false;
}

// O(n^2): Quadratic time
public void printAllPairs(int[] array) {
for (int i : array) {
for (int j : array) {
System.out.println(i + ", " + j);
}
}
}
}

Aplicaciones Prácticas

  • Evaluar algoritmos de búsqueda y ordenamiento: Elegir entre búsqueda lineal o binaria según la estructura de datos.
  • Optimizar software empresarial: Determinar cuellos de botella en consultas a bases de datos.
  • Diseño de aplicaciones en tiempo real: Seleccionar estructuras de datos con complejidad O(1) u O(log n) para garantizar rapidez.
  • Escalabilidad en sistemas distribuidos: Elegir algoritmos que soporten grandes volúmenes de datos.

Buenas prácticas

  • Responsabilidad única (SRP): Cada función demuestra un solo comportamiento.
  • Nombres descriptivos: getFirstElement, contains explican su propósito claramente.
  • Modularidad: Separar lógica de negocio y pruebas facilita refactorización.
  • Medición objetiva: Los tests y ejemplos facilitan verificar comportamiento y desempeño.

Referencias

  • Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2022). Introduction to Algorithms (4th ed.). MIT Press.
  • Weiss, M. A. (2020). Data Structures and Algorithm Analysis in Java (4th ed.). Pearson.
  • McDowell, G. (2016). Cracking the Coding Interview. CareerCup.
  • Big-O Cheat Sheet
  • Python 3