· Jose Ramón Bogarin · Documentation  · 6 min read

La notación Big-O

¿Para que es esto? ¿que mi código que? no recuerdo verlo en la escuela

¿Para que es esto? ¿que mi código que? no recuerdo verlo en la escuela

Descripción General

La notación Big-O es una notación matemática que se usa para describir el comportamiento de tiempo de ejecución o la complejidad de un algoritmo en función del tamaño de la entrada. Específicamente, Big-O se utiliza para expresar el peor caso de un algoritmo, proporcionando una medida para comparar la eficiencia de diferentes algoritmos.

La notación Big O es importante por varias razones:

  1. La notación Big O es importante porque ayuda a analizar la eficiencia de los algoritmos.
  2. Proporciona una manera de describir cómo crecen los requisitos de tiempo de ejecución o espacio de un algoritmo a medida que aumenta el tamaño de entrada.
  3. Permite a los programadores comparar diferentes algoritmos y elegir el más eficiente para un problema específico.
  4. Ayuda a comprender la escalabilidad de los algoritmos y a predecir cómo funcionarán a medida que crezca el tamaño de la entrada. Permite a los desarrolladores optimizar el código y mejorar el rendimiento general.

Manos a la obra

Voy a mostrar ejemplos de diferentes complejidades en código para que puedas ver cómo se aplican estas ideas.

O(1) - Tiempo Constante

Un algoritmo tiene complejidad O(1) si el tiempo de ejecución no cambia con el tamaño de la entrada.

def get_first_element(arr):
    return arr[0]

# Independientemente del tamaño de 'arr', esta función siempre ejecuta en tiempo constante.

O(n) - Tiempo Lineal

Un algoritmo tiene complejidad O(n) si el tiempo de ejecución crece linealmente con el tamaño de la entrada.


def print_elements(arr):
    for element in arr:
        print(element)

# Si 'arr' tiene n elementos, la función realizará n operaciones de impresión.

O(n^2) - Tiempo Cuadrático

Un algoritmo tiene complejidad O(n^2) si el tiempo de ejecución crece proporcionalmente al cuadrado del tamaño de la entrada.

def print_pairs(arr):
    for i in range(len(arr)):
        for j in range(len(arr)):
            print(arr[i], arr[j])

# Si 'arr' tiene n elementos, el bucle anidado realizará n^2 operaciones.

O(log n) - Tiempo Logarítmico

Un algoritmo tiene complejidad O(log n) si el tiempo de ejecución crece logarítmicamente con el tamaño de la entrada. Esto es común en algoritmos de búsqueda en estructuras de datos como árboles binarios de búsqueda.

def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

# En el peor de los casos, el número de comparaciones realizadas es logarítmico en relación al tamaño de 'arr'.

O(n log n) - Tiempo Linealítmico

Un algoritmo tiene complejidad O(n log n) si combina un comportamiento lineal y logarítmico, común en algoritmos de ordenación eficientes como Merge Sort y Quick Sort.

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr) // 2
        left_half = arr[:mid]
        right_half = arr[mid:]

        merge_sort(left_half)
        merge_sort(right_half)

        i = j = k = 0

        while i < len(left_half) and j < len(right_half):
            if left_half[i] < right_half[j]:
                arr[k] = left_half[i]
                i += 1
            else:
                arr[k] = right_half[j]
                j += 1
            k += 1

        while i < len(left_half):
            arr[k] = left_half[i]
            i += 1
            k += 1

        while j < len(right_half):
            arr[k] = right_half[j]
            j += 1
            k += 1

# El Merge Sort tiene una complejidad de tiempo de O(n log n) en el peor caso.

Realmente Funciona en la Vida Real Usando Connotación Big O?

La notación Big-O, tiene aplicaciones prácticas y significativas en la vida real, especialmente en el campo de la informática y el desarrollo de software. Aquí hay algunos ejemplos y conclusiones prácticas sobre cómo Big-O puede influir en decisiones reales:

1. Desarrollo de Software y Optimización:

  • Elección de Algoritmos: Cuando desarrollas software, elegir el algoritmo correcto es crucial. Por ejemplo, para ordenar grandes volúmenes de datos, un algoritmo de ordenación O(n log n) como Merge Sort o Quick Sort es preferible a uno O(n^2) como Bubble Sort. Esto se traduce en mejoras significativas en el rendimiento y la eficiencia del software.

  • Escalabilidad: En aplicaciones que deben manejar un número creciente de usuarios o datos (como una red social o una tienda en línea), los algoritmos eficientes garantizan que el sistema pueda escalar sin degradaciones significativas en el rendimiento.

2. Diseño de Sistemas y Arquitectura:

  • Bases de Datos: El uso de estructuras de datos eficientes, como índices B-tree en bases de datos, que tienen una complejidad de O(log n) para operaciones de búsqueda, permite consultas rápidas incluso en bases de datos grandes.

  • Optimización de Recursos: Al diseñar sistemas distribuidos, comprender la complejidad de los algoritmos de comunicación y sincronización puede ayudar a optimizar el uso de recursos y reducir la latencia.

3. Experiencia del Usuario:

  • Tiempos de Carga y Respuesta: Algoritmos eficientes mejoran la experiencia del usuario. Por ejemplo, un motor de búsqueda que utiliza algoritmos de búsqueda eficientes puede proporcionar resultados en milisegundos, mejorando la satisfacción del usuario.

  • Aplicaciones Móviles y Web: Para aplicaciones móviles y web, donde los recursos (CPU, memoria, batería) son limitados, usar algoritmos con menor complejidad asegura que las aplicaciones sean rápidas y responsivas.

4. Costo y Mantenimiento:

  • Costos Operativos: Sistemas más eficientes pueden reducir los costos operativos. Por ejemplo, un servidor que puede manejar más solicitudes con menos recursos reduce los costos de infraestructura.

  • Mantenimiento y Actualización: Algoritmos y estructuras de datos bien diseñados facilitan el mantenimiento y la actualización del software, reduciendo el esfuerzo y el costo a largo plazo.

Ejemplo Concreto: E-commerce

Imagina que trabajas en una tienda en línea que debe procesar miles de pedidos por segundo durante una venta especial. Si el algoritmo de procesamiento de pedidos tiene una complejidad de O(n^2), el tiempo para manejar estos pedidos crecerá cuadráticamente con el número de pedidos, llevando a tiempos de respuesta inaceptables y posiblemente a la pérdida de ventas. En cambio, un algoritmo O(n log n) puede procesar los pedidos mucho más rápidamente, asegurando una experiencia de usuario fluida y mayor capacidad para manejar el tráfico de la venta.

Conclusión

Comprender y aplicar la notación Big-O en el desarrollo y diseño de sistemas informáticos permite construir soluciones más eficientes, escalables y sostenibles. Esto no solo mejora el rendimiento y la experiencia del usuario, sino que también optimiza el uso de recursos y reduce costos operativos. En resumen, la notación Big-O es una herramienta fundamental para tomar decisiones informadas y estratégicas en la ingeniería de software y la tecnología.

Referencias.

Algorithms & Data Structures

Back to Blog

Related Posts

View All Posts »
Pruebas unitarias

Pruebas unitarias

¿Realmente son necesarias hacer pruebas unitarias? son perdida de tiempo? ¿hace tener el doble de tiempo en la entrega?.

Atomic Design

Atomic Design

A caso diseñare una bomba atómica?, en que parte se utiliza?, es necesario usar algo asi?.