Algoritmo del Triángulo de Pascal
Una guía completa: teoría, implementaciones en Python, comparativas y mejores prácticas.
¿Qué es el Triángulo de Pascal?
El Triángulo de Pascal es una disposición triangular de los coeficientes binomiales. Cada fila n contiene los valores C(n, k) para k = 0..n. Sus propiedades son la base de combinatoria, teoría de probabilidad y algoritmos de generación de combinaciones.
- Relación recursiva:
C(n, k) = C(n‑1, k‑1) + C(n‑1, k) - Fila
ntienen+1elementos. - Los bordes siempre son 1.
Comparativa: Características del Algoritmo vs. Implementaciones en Python
Características del algoritmo
- Complejidad temporal: O(n²) para generar n filas completas.
- Complejidad espacial: O(n²) si se almacenan todas las filas; O(n) si solo se mantiene la fila actual.
- Recursividad: Naturalmente recursivo mediante la relación binomial.
- Iteratividad: Se puede generar fila a fila usando sumas de pares adyacentes.
- Escalabilidad: Adecuado para n ≤ 30‑40 en entornos de memoria limitada; para n > 30 se recomiendan generadores perezosos.
Implementaciones en Python
- Iterativa (lista mutable): Simple, rápida, uso O(n) de memoria por fila.
- Recursiva con memoización: Elegante, pero con sobrecarga de llamadas.
- Generator (perezoso): Ideal para flujos grandes; produce una fila a la vez.
- NumPy vectorizado: Aprovecha operaciones de arrays para n > 10⁴.
- Uso de
math.comb(Python 3.8+): Obtención directa de coeficientes, útil para filas aisladas.
Implementaciones en Python
A continuación se presentan tres enfoques que cubren la mayor parte de los casos de uso.
1️⃣ Implementación iterativa (lista mutable)
def pascal_iterativo(filas: int):
"""Genera el Triángulo de Pascal usando un algoritmo iterativo.
Retorna una lista de listas donde cada sub‑lista es una fila.
"""
triangulo = []
for n in range(filas):
fila = [1]
if n > 0:
ultima = triangulo[-1]
fila.extend([ultima[i] + ultima[i+1] for i in range(len(ultima)-1)])
fila.append(1)
triangulo.append(fila)
return triangulo
# Ejemplo de uso
for fila in pascal_iterativo(7):
print(fila)
2️⃣ Generador perezoso (yield)
def pascal_generator():
"""Genera filas del Triángulo de Pascal indefinidamente.
Cada llamada a next() devuelve la siguiente fila.
"""
fila = [1]
while True:
yield fila
fila = [1] + [fila[i] + fila[i+1] for i in range(len(fila)-1)] + [1]
# Consumo del generador
gen = pascal_generator()
for _ in range(7):
print(next(gen))
3️⃣ Implementación recursiva con memoización
from functools import lru_cache
@lru_cache(maxsize=None)
def coeficiente_binomial(n: int, k: int) -> int:
if k == 0 or k == n:
return 1
return coeficiente_binomial(n-1, k-1) + coeficiente_binomial(n-1, k)
def pascal_recursivo(filas: int):
return [[coeficiente_binomial(n, k) for k in range(n+1)] for n in range(filas)]
# Ejemplo
for fila in pascal_recursivo(7):
print(fila)
💡 Tip de rendimiento: Para n > 30, la versión iterativa o el generador son mucho más eficientes que la recursiva, ya que evitan la sobrecarga de la pila.
Comparación con otras técnicas de generación combinatoria
El Triángulo de Pascal es solo una forma de obtener combinaciones. A continuación se comparan alternativas comunes:
Método del Triángulo de Pascal
- Ventaja: Simple, reutiliza resultados previos.
- Desventaja: O(n²) de complejidad cuando se requieren todas las filas.
Uso de math.comb
- Ventaja: Cálculo directo y preciso de
C(n,k)en O(log k) usando aritmética de enteros. - Desventaja: No genera la estructura completa del triángulo sin bucle adicional.
En contextos donde sólo se necesita un coeficiente puntual (por ejemplo, cálculos de probabilidades), math.comb es la opción más rápida.
Rendimiento, optimización y buenas prácticas
Benchmark rápido (Python 3.11)
import timeit
# 1) Iterativo
print('Iterativo:', timeit.timeit('pascal_iterativo(500)', globals=globals(), number=1))
# 2) Generador (primeras 500 filas)
print('Generador:', timeit.timeit('list(itertools.islice(pascal_generator(), 500))', globals=globals(), number=1))
# 3) math.comb (calculando toda la tabla)
print('math.comb:', timeit.timeit('[[math.comb(n, k) for k in range(n+1)] for n in range(500)]', globals=globals(), number=1))
En pruebas típicas, el método iterativo supera al recursivo y al uso directo de math.comb cuando se necesita la tabla completa, mientras que math.comb es superior para consultas esporádicas.
Optimización de memoria
- Usar
dequedecollectionspara mantener sólo la fila anterior. - Si la salida es sólo de lectura, el generador evita almacenar la tabla completa.
Seguridad y validación de entradas
Aunque el algoritmo no maneja datos externos críticos, es buena práctica validar que el número de filas sea un entero no negativo y limitar su valor para evitar consumo excesivo de memoria.
def validar_filas(filas: int, max_filas: int = 10_000):
if not isinstance(filas, int) or filas < 0:
raise ValueError('El número de filas debe ser un entero positivo.')
if filas > max_filas:
raise ValueError(f'El número máximo permitido es {max_filas}.')
Buenas prácticas de código
- Documentar cada función con
docstringsiguiendo el estilo Google o Sphinx. - Escribir pruebas unitarias con
pytestpara validar casos límite (fila 0, fila 1, valores grandes). - Utilizar tipado estático (
typing.List[int]) para mejorar la mantenibilidad.
Casos de uso reales del Triángulo de Pascal
- Probabilidad y estadísticas: Cálculo de distribuciones binomiales.
- Algoritmos de combinatoria: Generación de subconjuntos sin repetición.
- Criptografía: Coeficientes binomiales en esquemas de compartición de secretos (Shamir).
- Ingeniería de software: Tablas de expansión de versiones de software (semver).
Algoritmo del Triángulo de Pascal: Teoría, Implementación en Python y Buenas Prácticas