Saltar al contenido
Editor

Bucles anidados en Python: patrones, velocidad y cuándo evitarlos

Intermedio25 min6 ejercicios80 XP

Esta lección es una traducción del original en inglés.Read in EnglishAvisar de un error de traducción

0/6 ejercicios

Piensa en una hoja de cálculo. Tiene filas y columnas. Para leer cada celda, recorres cada fila y, dentro de cada fila, recorres cada columna. Son dos bucles, uno dentro del otro. Eso es un bucle anidado.

Los bucles anidados aparecen por todas partes en programación: para imprimir patrones en forma de cuadrícula, buscar en tablas, generar combinaciones y procesar imágenes píxel a píxel. Son muy útiles, pero también pueden ser sorprendentemente lentos si no tienes cuidado.

En esta lección aprenderás cómo funcionan los bucles anidados, verás ejemplos prácticos con cuadrículas y combinaciones, entenderás por qué pueden ser lentos y descubrirás cuándo conviene usar otra solución.

¿Cómo funcionan los bucles anidados en Python?

Un bucle anidado es un bucle dentro de otro bucle. El bucle externo arranca primero. En cada iteración del bucle externo, el bucle interno se ejecuta completo, de principio a fin.

Un bucle anidado básico
for row in range(1, 4): # Outer loop: rows 1, 2, 3
for col in range(1, 4): # Inner loop: columns 1, 2, 3
print(f'({row},{col})', end=' ')
print() # New line after each row
Salida
(1,1) (1,2) (1,3) 
(2,1) (2,2) (2,3) 
(3,1) (3,2) (3,3)

Esta es la idea clave: si el bucle externo se ejecuta n veces y el bucle interno m veces, el código dentro del bucle interno se ejecuta n x m veces en total. Con tres filas y tres columnas, se imprime 9 veces.

Contar las iteraciones de un bucle anidado
# Counting total iterations
count = 0
for i in range(5): # 5 iterations
for j in range(3): # 3 iterations each time
count += 1
print(f'Total iterations: {count}') # 5 x 3 = 15
Salida
Total iterations: 15

Trabajar con datos en 2D (cuadrículas y tablas)

El uso más práctico de los bucles anidados es trabajar con datos bidimensionales. En Python, una lista de listas representa una cuadrícula, una tabla o una matriz. El bucle externo recorre las filas y el bucle interno recorre las columnas de cada fila.

Recorrer una cuadrícula 2D
# A 3x3 grid represented as a list of lists
grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Print every element
for row in grid:
for value in row:
print(value, end=' ')
print() # New line after each row
Salida
1 2 3 
4 5 6 
7 8 9

A veces necesitas el índice de la fila y de la columna, no solo los valores. Para eso, usa enumerate().

Usar enumerate con bucles anidados
grid = [
['X', 'O', 'X'],
['O', 'X', 'O'],
['O', 'O', 'X']
]
# Find all positions where X appears
for r, row in enumerate(grid):
for c, cell in enumerate(row):
if cell == 'X':
print(f'X found at row {r}, col {c}')
Salida
X found at row 0, col 0
X found at row 0, col 2
X found at row 1, col 1
X found at row 2, col 2

Veamos un ejemplo práctico: construir una tabla de multiplicar.

Tabla de multiplicar con bucles anidados
# Multiplication table (1 through 5)
for i in range(1, 6):
for j in range(1, 6):
product = i * j
print(f'{product:4}', end='')
print()
Salida
   1   2   3   4   5
   2   4   6   8  10
   3   6   9  12  15
   4   8  12  16  20
   5  10  15  20  25

Generar combinaciones con bucles anidados

Los bucles anidados son perfectos para generar todas las combinaciones de elementos de dos (o más) grupos. El bucle externo elige del primer grupo y el bucle interno elige del segundo.

Todas las combinaciones de colores y tallas
colors = ['red', 'blue']
sizes = ['S', 'M', 'L']
print('All t-shirt options:')
for color in colors:
for size in sizes:
print(f' {color} - {size}')
Salida
All t-shirt options:
  red - S
  red - M
  red - L
  blue - S
  blue - M
  blue - L

Con 2 colores y 3 tallas, obtienes 6 combinaciones. Cada color se combina con cada talla. Esto se llama producto cartesiano de las dos listas.

Encontrar pares que sumen un valor objetivo
# Finding pairs that add up to a target
numbers = [1, 3, 5, 7, 9]
target = 10
print(f'Pairs that sum to {target}:')
for i in range(len(numbers)):
for j in range(i + 1, len(numbers)): # Start j after i to avoid duplicates
if numbers[i] + numbers[j] == target:
print(f' {numbers[i]} + {numbers[j]} = {target}')
Salida
Pairs that sum to 10:
  1 + 9 = 10
  3 + 7 = 10

Crear patrones con bucles anidados

Imprimir patrones es un ejercicio clásico para entender los bucles anidados. El bucle externo controla cada fila, y el bucle interno controla cuántos caracteres tiene cada fila.

Triángulo rectángulo de asteriscos
# Right triangle pattern
n = 5
for row in range(1, n + 1):
for col in range(row):
print('*', end='')
print()
Salida
*
**
***
****
*****

El truco está en que el rango del bucle interno depende de la variable del bucle externo. En la fila 1, el bucle interno se ejecuta una vez. En la fila 2, dos veces. En la fila 5, cinco veces. Eso crea la forma de escalera.

Triángulo invertido (una forma más sencilla)
# Inverted triangle
n = 5
for row in range(n, 0, -1):
print('*' * row)
Salida
*****
****
***
**
*

Rendimiento: por qué los bucles anidados pueden ser lentos

Un bucle sencillo sobre 1000 elementos se ejecuta 1000 veces. Un bucle anidado sobre esos 1000 elementos se ejecuta 1000 x 1000 = 1 000 000 de veces. Eso es un millón de operaciones. Añade un tercer nivel de anidamiento y llegas a mil millones. Los bucles anidados multiplican, y las cifras crecen muy rápido.

En informática, esto se describe con la notación Big O (notación O grande). Un bucle sencillo es O(n): el tiempo crece de forma lineal con el tamaño de los datos. Un bucle anidado es O(n²): el tiempo crece con el cuadrado del tamaño de los datos. Si duplicas la entrada, el trabajo se multiplica por cuatro.

Comparar la velocidad de un bucle sencillo y uno anidado
import time
# O(n) - single loop
n = 10000
start = time.time()
total = 0
for i in range(n):
total += i
single_time = time.time() - start
# O(n²) - nested loop
start = time.time()
total = 0
for i in range(n):
for j in range(n):
total += 1
nested_time = time.time() - start
print(f'Single loop: {single_time:.4f} seconds')
print(f'Nested loop: {nested_time:.4f} seconds')
print(f'Nested is {nested_time / max(single_time, 0.0001):.0f}x slower')

Cuándo evitar los bucles anidados

No todos los problemas que parecen pedir bucles anidados los necesitan de verdad. A menudo hay una forma más rápida con conjuntos, diccionarios o funciones integradas.

O(n²): bucle anidado para buscar duplicados
nums = [3, 1, 4, 1, 5, 9]
has_dup = False
for i in range(len(nums)):
    for j in range(i+1, len(nums)):
        if nums[i] == nums[j]:
            has_dup = True
print(has_dup)  # True
O(n): usa un conjunto
nums = [3, 1, 4, 1, 5, 9]
has_dup = len(nums) != len(set(nums))
print(has_dup)  # True

La versión con un conjunto no solo es más corta: es muchísimo más rápida. Con 10 000 elementos, la versión con bucle anidado hace 50 millones de comparaciones. La versión con conjunto hace unas 10 000 operaciones. Es 5000 veces más rápida.

O(n²): bucle anidado para encontrar un valor
students = [
    {'name': 'Alice', 'id': 101},
    {'name': 'Bob', 'id': 102},
    {'name': 'Charlie', 'id': 103}
]
ids_to_find = [103, 101]

for target_id in ids_to_find:
    for s in students:
        if s['id'] == target_id:
            print(s['name'])
O(n): crea primero un diccionario
students = [
    {'name': 'Alice', 'id': 101},
    {'name': 'Bob', 'id': 102},
    {'name': 'Charlie', 'id': 103}
]
ids_to_find = [103, 101]

lookup = {s['id']: s['name'] for s in students}
for target_id in ids_to_find:
    print(lookup[target_id])

Ejercicios de práctica

Estos ejercicios afianzarán lo que sabes de los bucles anidados. Empiezan con patrones básicos y avanzan hacia problemas del mundo real.

Ejercicio 1: imprime un rectángulo
Escribe el código

Imprime un rectángulo de asteriscos de 3 filas y 5 columnas. Cada fila debe tener 5 asteriscos, sin espacios entre ellos.

Salida esperada:

*****
*****
*****
# Print a 3x5 rectangle of stars
Ejercicio 2: predice la salida
Predice la salida

Sin ejecutar el código, predice qué va a imprimir. Cuenta cuántas líneas tiene la salida en total. Después, ejecútalo para comprobarlo.

for i in range(3):
for j in range(2):
print(f'{i}-{j}')
Ejercicio 3: triángulo de números
Escribe el código

Imprime un triángulo de números en el que cada fila contiene su número de fila repetido. El triángulo tiene 4 filas.

Salida esperada:

1
22
333
4444
# Print a number triangle with 4 rows
Ejercicio 4: suma todos los valores de la cuadrícula
Escribe el código

Tienes una cuadrícula de números. Usa bucles anidados para calcular e imprimir la suma de todos sus valores.

Salida esperada:

45
grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Calculate the sum of all values
Ejercicio 5: todas las combinaciones de ropa
Escribe el código

Dada una lista de prendas de arriba (tops) y otra de prendas de abajo (bottoms), imprime todas las combinaciones posibles y, al final, el total.

Salida esperada:

shirt + jeans
shirt + shorts
sweater + jeans
sweater + shorts
hoodie + jeans
hoodie + shorts
Total outfits: 6
tops = ['shirt', 'sweater', 'hoodie']
bottoms = ['jeans', 'shorts']
# Print all combinations and the total count
Ejercicio 6: corrige la cuadrícula de coordenadas
Corrige el error

Este código debería imprimir una cuadrícula de coordenadas de 3x3, pero tiene un error. La salida debe mostrar cada fila de coordenadas en su propia línea, donde tanto x como y van de 0 a 2.

Salida esperada:

(0,0) (0,1) (0,2)
(1,0) (1,1) (1,2)
(2,0) (2,1) (2,2)

Encuentra el error y corrígelo.

for x in range(3):
for y in range(3):
print(f'({x},{y})', end=' ')
print()

Resumen: bucles anidados en Python

Esto es lo que aprendiste sobre los bucles anidados:

ConceptoIdea clave
Bucle anidadoUn bucle dentro de otro bucle
Iteraciones totalesVueltas del externo x vueltas del interno
Datos 2DBucle externo = filas, bucle interno = columnas
CombinacionesEmparejar cada elemento del grupo A con cada elemento del grupo B
RendimientoO(n²): si duplicas la entrada, el trabajo se multiplica por cuatro
AlternativasLos conjuntos y los diccionarios a menudo sustituyen a los bucles anidados

Reglas prácticas:

  • Piensa en el tamaño de los datos antes de anidar bucles
  • Usa nombres de variables con significado (row/col, no i/j)
  • Multiplicar cadenas ('*' * n) suele sustituir al bucle interno en los patrones
  • Para buscar elementos, un diccionario o un conjunto casi siempre es más rápido que un bucle anidado
  • ¿Qué sigue?

    En la siguiente lección, [Cómo leer los mensajes de error de Python](/python/python-error-messages), aprenderás a descifrar los mensajes de error de Python, a corregir los errores más comunes y a dejar de temerle al traceback (rastreo de errores). Saber depurar facilita todo lo demás.

    Tutoriales relacionados

    ¿Te sirvió este tutorial?

    Sugerir una mejora