WhatsApp

  

Algoritmo Factorial en Python: Guía Completa, Ejemplos y Buenas Prácticas

Descubre todo sobre el algoritmo factorial, su teoría, implementaciones en Python (recursiva, iterativa, memoizada, con functools y numpy) y cómo optimizarlo para grandes números.

Algoritmo Factorial en Python: Conceptos, Implementaciones y Buenas Prácticas

1. ¿Qué es el factorial?

El factorial de un número entero no negativo n se define como:

n! = 1 × 2 × 3 × … × n (con 0! = 1)

Se utiliza en combinatoria (permutaciones, combinaciones), análisis de algoritmos y series de Taylor.

1.1. Propiedades matemáticas relevantes

  • n! = n × (n‑1)! (relación recursiva)
  • (n+1)! = (n+1) × n!
  • n! ≈ sqrt(2πn) (n/e)^n (aproximación de Stirling)

2. Complejidad algorítmica

En cualquier implementación que multiplique los números del 1 al n, la complejidad temporal es O(n) y la complejidad espacial varía según el enfoque:

ImplementaciónTiempoEspacio
Iterativa (bucle)O(n)O(1)
Recursiva simpleO(n)O(n) (pila)
Tail‑recursiva (Python < 3.8 no optimiza)O(n)O(n)
Memoizada/LRU‑CacheO(n) (una sola evaluación)O(n)

3. Implementaciones en Python

3.1. Versión iterativa (más eficiente en espacio)

def factorial_iter(n: int) -> int:
    """Calcula n! usando un bucle while.
    Lanza ValueError si n es negativo.
    """
    if n < 0:
        raise ValueError("El factorial no está definido para números negativos")
    result = 1
    while n > 1:
        result *= n
        n -= 1
    return result

3.2. Versión recursiva clásica

def factorial_rec(n: int) -> int:
    if n < 0:
        raise ValueError("Número negativo")
    return 1 if n == 0 else n * factorial_rec(n - 1)

3.3. Memoización con functools.lru_cache

from functools import lru_cache
@lru_cache(maxsize=None)
def factorial_memo(n: int) -> int:
    if n < 0:
        raise ValueError("Número negativo")
    return 1 if n == 0 else n * factorial_memo(n - 1)

3.4. Usando math.factorial (C‑optimizado)

import math
def factorial_stdlib(n: int) -> int:
    return math.factorial(n)  # lanza ValueError para n < 0

3.5. Con functools.reduce (estilo funcional)

from functools import reduce
def factorial_reduce(n: int) -> int:
    if n < 0:
        raise ValueError("Número negativo")
    return reduce(lambda x, y: x * y, range(1, n + 1), 1)

3.6. Vectorizado con numpy.prod (útil en cálculos masivos)

import numpy as np
def factorial_numpy(n: int) -> int:
    if n < 0:
        raise ValueError("Número negativo")
    return int(np.prod(np.arange(1, n + 1)))

3.7. Paralelismo con multiprocessing (para n muy grandes)

import multiprocessing as mp
def _partial_product(rng):
    prod = 1
    for i in rng:
        prod *= i
    return prod
def factorial_parallel(n: int, workers: int = None) -> int:
    if n < 0:
        raise ValueError("Número negativo")
    workers = workers or mp.cpu_count()
    # dividir el rango en bloques
    chunk = (n // workers) + 1
    ranges = [range(i, min(i + chunk, n + 1)) for i in range(1, n + 1, chunk)]
    with mp.Pool(workers) as pool:
        partials = pool.map(_partial_product, ranges)
    result = 1
    for p in partials:
        result *= p
    return result

4. Casos de uso reales

  • Combinaciones: C(n, k) = n! / (k! (n‑k)!)
  • Permutaciones: número de formas de ordenar n objetos = n!
  • Distribuciones de probabilidad: distribución binomial, Poisson, etc.
  • Series de Taylor: los coeficientes de la expansión de e^x usan n!

5. Buenas prácticas y troubleshooting

  1. Validación de entrada: siempre verifica que n sea entero no negativo.
  2. Manejo de excepciones: captura ValueError y muestra mensajes claros al usuario.
  3. Evita recursión profunda: Python tiene límite de recursión (~1000). Para n > 995 prefiere la versión iterativa o math.factorial.
  4. Control de consumo de memoria: la memoización es útil sólo si vas a calcular varios factoriales repetidos; de lo contrario, consume memoria innecesaria.
  5. Seguridad: nunca confíes en valores de entrada sin validar; un número extremadamente grande puede consumir CPU y provocar Denial‑of‑Service.
  6. Testing: usa pytest con casos límite (0, 1, valores grandes como 1000) y verifica contra math.factorial.

Ejemplo de test con pytest

import pytest, math
from mymodule import factorial_iter, factorial_rec, factorial_memo
@pytest.mark.parametrize("func", [factorial_iter, factorial_rec, factorial_memo])
def test_factorial_small(func):
    for n in range(0, 10):
        assert func(n) == math.factorial(n)
def test_factorial_negative():
    with pytest.raises(ValueError):
        factorial_iter(-5)

6. Comparativa con otros lenguajes

LenguajeImplementación típicaTiempo (n=10⁵)Observaciones
Python (math.factorial)C‑optimizado interno≈ 0.004 sSoporta enteros arbitrarios
Python (iterativo puro)Bucle while≈ 0.018 sMás legible, sin dependencias C
C (uint64)Loop for≈ 0.001 sDesbordamiento a partir de 20!
JavaScript (Number)Recursivo≈ 0.025 sLímite de precisión en 21!

© 2025 BlogTech – Algoritmos y Python. Todos los derechos reservados.



Algoritmo Factorial en Python: Guía Completa, Ejemplos y Buenas Prácticas
ASIMOV Ingeniería S. de R.L. de C.V., Emiliano Nava 15 noviembre, 2025
Compartir
Iniciar sesión dejar un comentario

  
Guía completa de Media Queries para diseño responsivo: conceptos, ejemplos y mejores prácticas
Aprende todo sobre media queries en CSS, cómo crear diseños responsivos, ejemplos prácticos, comparativas de breakpoints y consejos de optimización y accesibilidad.