En esta sección, vamos a poner en práctica los conceptos de optimización de algoritmos que hemos aprendido en los módulos anteriores. Realizaremos ejercicios que nos permitirán mejorar el rendimiento del código, tanto en términos de tiempo como de espacio. Cada ejercicio incluirá una descripción del problema, un ejemplo de código inicial, y una serie de pasos para optimizarlo. Al final de cada ejercicio, se proporcionará una solución optimizada y una explicación detallada de las mejoras realizadas.

Ejercicio 1: Optimización de Búsqueda Lineal

Descripción del Problema

Dado un array de números enteros, queremos encontrar si un número específico está presente en el array. El enfoque inicial es utilizar una búsqueda lineal.

Código Inicial

def linear_search(arr, target):
    for i in range(len(arr)):
        if arr[i] == target:
            return True
    return False

# Ejemplo de uso
arr = [1, 3, 5, 7, 9, 11]
target = 7
print(linear_search(arr, target))  # Output: True

Pasos para Optimizar

  1. Mejora de la Condición de Salida: Podemos mejorar la eficiencia del bucle añadiendo una condición de salida temprana.
  2. Uso de Estructuras de Datos Adecuadas: Convertir el array en un conjunto (set) para aprovechar la búsqueda en tiempo constante.

Código Optimizado

def linear_search_optimized(arr, target):
    return target in set(arr)

# Ejemplo de uso
arr = [1, 3, 5, 7, 9, 11]
target = 7
print(linear_search_optimized(arr, target))  # Output: True

Explicación de la Optimización

  • Condición de Salida Temprana: En el código inicial, el bucle continúa hasta el final del array incluso si el elemento se encuentra antes. La optimización con set permite realizar la búsqueda en tiempo constante O(1).

Ejercicio 2: Optimización de Ordenamiento por Burbuja

Descripción del Problema

El algoritmo de ordenamiento por burbuja es ineficiente para grandes listas. Vamos a optimizarlo para mejorar su rendimiento.

Código Inicial

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr

# Ejemplo de uso
arr = [64, 34, 25, 12, 22, 11, 90]
print(bubble_sort(arr))  # Output: [11, 12, 22, 25, 34, 64, 90]

Pasos para Optimizar

  1. Detección de Lista Ordenada: Añadir una bandera para detectar si la lista ya está ordenada y evitar iteraciones innecesarias.
  2. Optimización de Bucle Interno: Reducir el número de comparaciones en cada iteración.

Código Optimizado

def bubble_sort_optimized(arr):
    n = len(arr)
    for i in range(n):
        swapped = False
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = True
        if not swapped:
            break
    return arr

# Ejemplo de uso
arr = [64, 34, 25, 12, 22, 11, 90]
print(bubble_sort_optimized(arr))  # Output: [11, 12, 22, 25, 34, 64, 90]

Explicación de la Optimización

  • Detección de Lista Ordenada: La bandera swapped permite salir del bucle si no se realizaron intercambios en una iteración, indicando que la lista ya está ordenada.
  • Reducción de Comparaciones: El bucle interno se reduce en cada iteración, ya que los elementos más grandes se "burbujearán" hacia el final de la lista.

Ejercicio 3: Optimización de Uso de Memoria

Descripción del Problema

Dado un array de números enteros, queremos eliminar los duplicados y devolver un array con elementos únicos. El enfoque inicial utiliza una lista adicional para almacenar los elementos únicos.

Código Inicial

def remove_duplicates(arr):
    unique_elements = []
    for elem in arr:
        if elem not in unique_elements:
            unique_elements.append(elem)
    return unique_elements

# Ejemplo de uso
arr = [1, 2, 2, 3, 4, 4, 5]
print(remove_duplicates(arr))  # Output: [1, 2, 3, 4, 5]

Pasos para Optimizar

  1. Uso de Estructuras de Datos Adecuadas: Utilizar un conjunto (set) para eliminar duplicados de manera eficiente.
  2. Reducción de Uso de Memoria: Evitar el uso de una lista adicional.

Código Optimizado

def remove_duplicates_optimized(arr):
    return list(set(arr))

# Ejemplo de uso
arr = [1, 2, 2, 3, 4, 4, 5]
print(remove_duplicates_optimized(arr))  # Output: [1, 2, 3, 4, 5]

Explicación de la Optimización

  • Uso de set: Convertir el array a un conjunto elimina automáticamente los duplicados, y luego convertirlo de nuevo a una lista.
  • Reducción de Uso de Memoria: El uso de set es más eficiente en términos de tiempo y espacio comparado con la lista adicional.

Conclusión

En esta sección, hemos abordado varios ejercicios prácticos para optimizar algoritmos en términos de tiempo y espacio. Hemos aprendido a:

  • Mejorar la eficiencia de búsqueda utilizando estructuras de datos adecuadas.
  • Optimizar algoritmos de ordenamiento mediante la detección de listas ordenadas y la reducción de comparaciones.
  • Utilizar estructuras de datos eficientes para eliminar duplicados y reducir el uso de memoria.

Estos ejercicios no solo refuerzan los conceptos aprendidos, sino que también proporcionan técnicas prácticas que pueden aplicarse en situaciones del mundo real para mejorar el rendimiento del código.

© Copyright 2024. Todos los derechos reservados