Algoritmo de Números Complejos en Python
Todo lo que necesitas saber para trabajar con números complejos en Python: teoría, implementación, comparativas y buenas prácticas.
1. ¿Qué son los números complejos?
Un número complejo se representa como a + bj, donde a es la parte real, b la parte imaginaria y j (o i en matemáticas) es la raíz cuadrada de -1. En forma polar se escribe r·e^{jθ}, con r módulo y θ argumento.
Representación cartesiana
- Real (Re):
a - Imaginaria (Im):
b - Operaciones: suma, resta, multiplicación y división siguen reglas algebraicas.
Representación polar
- Módulo (r):
sqrt(a² + b²) - Argumento (θ):
atan2(b, a) - Ideal para rotaciones y transformadas de Fourier.
2. Números complejos nativos de Python
Python incluye el tipo complex y el módulo cmath para operaciones avanzadas.
# Creación de un número complejo
z = 3 + 4j
print(z) # 3+4j
print(z.real, z.imag) # 3.0 4.0
# Operaciones básicas
suma = z + (1 - 2j)
producto = z * (2 + 0j)
division = z / (1 + 1j)
print(suma, producto, division)
El módulo cmath aporta funciones como sqrt, exp, log, phase y polar:
import cmath
r, phi = cmath.polar(z) # r = módulo, phi = argumento
print(f"Módulo={r:.2f}, Ángulo={phi:.2f} rad")
print("Fase con cmath.phase:", cmath.phase(z))
print("Conjugado:", z.conjugate())
3. Números complejos con NumPy
Para cálculos vectorizados y de alto rendimiento, numpy es la herramienta estándar.
import numpy as np
# Array de complejos
arr = np.array([1+2j, 3-4j, -1j])
print("Array:", arr)
# Operaciones element‑wise
print("Módulos:", np.abs(arr))
print("Argumentos:", np.angle(arr))
# Producto punto de vectores complejos
v1 = np.array([1+1j, 2+0j])
v2 = np.array([0+1j, 3-1j])
print("Producto punto:", np.vdot(v1, v2)) # vdot conjuga el primero
Beneficios de NumPy:
- CPU‑vectorización mediante
BLASyOpenBLAS. - Compatibilidad con
cupypara GPU sin cambiar el código. - Soporte nativo para FFT (
np.fft) que opera sobre datos complejos.
4. Algoritmos clave usando números complejos
4.1. Solución de ecuaciones cuadráticas
def solve_quadratic(a, b, c):
"""Resuelve ax² + bx + c = 0, devuelve raíces complejas si es necesario."""
disc = cmath.sqrt(b**2 - 4*a*c)
r1 = (-b + disc) / (2*a)
r2 = (-b - disc) / (2*a)
return r1, r2
print(solve_quadratic(1, 2, 5)) # (-1+2j), (-1-2j)
4.2. Transformada de Fourier Discreta (FFT)
import numpy as np
def demo_fft(N=8):
# Señal de prueba: suma de dos sinusoides
t = np.arange(N)
x = np.exp(2j*np.pi*0.125*t) + 0.5*np.exp(2j*np.pi*0.375*t)
X = np.fft.fft(x)
print("Señal original:", x)
print("FFT:", X)
# Reconstrucción
x_rec = np.fft.ifft(X)
print("Reconstrucción (≈ original):", x_rec)
demo_fft()
La FFT es la aplicación más frecuente de números complejos en procesamiento de señales, análisis de audio y aprendizaje automático.
5. Comparativa: Python vs. C++ vs. Julia para cálculos complejos
| Aspecto | Python (cmath / NumPy) | C++ (std::complex) | Julia |
|---|---|---|---|
| Facilidad de uso | Alta – tipado dinámico y sintaxis directa | Media – requiere cabeceras y manejo de plantillas | Alta – tipado fuerte pero sintaxis matemática |
| Rendimiento bruto | Bueno con NumPy (BLAS) – | Óptimo – compilado nativo | Muy alto – comparable a C++ gracias a LLVM |
| Bibliotecas de FFT | numpy.fft, scipy.fft (basado en FFTW) | FFTW, Intel MKL | FFTW.jl, AbstractFFTs |
| Soporte GPU | CuPy (código idéntico a NumPy) | cuComplex, thrust | CUDA.jl, AMDGPU.jl |
| Ecosistema de IA | TensorFlow, PyTorch (tanto CPU como GPU) | LibTorch, ONNX Runtime | Flux.jl, Knet.jl |
6. Buenas prácticas y optimizaciones
- Usar
numpysiempre que proceses colecciones grandes. Evita bucles Python puros; aprovecha la vectorización. - Preferir
np.vdotpara productos escalares. Internamente conjuga el primer argumento, evitando errores de signo. - Control de precisión. En cálculos críticos, especifica
dtype=np.complex128para doble precisión. - Evita conversiones innecesarias. No mezcles
complexnativo connp.complex64sin casting explícito. - Utiliza
numbaoCythonpara acelerar loops personalizados.from numba import njit import numpy as np @njit def mandelbrot(c, max_iter=1000): z = 0+0j for n in range(max_iter): if (z.real*z.real + z.imag*z.imag) > 4.0: return n z = z*z + c return max_iter
7. Depuración y troubleshooting
Los errores más comunes suelen derivarse de:
- Confusión entre
jyi. En Python el literal es siemprej(p. ej.1j). - División por cero en la parte imaginaria. Usa
cmath.isclosepara comparar con tolerancia. - Desbordamiento de precisión. Cuando el módulo supera ~1e308, el número se vuelve
inf. Considera escalar la entrada.
Ejemplo de manejo seguro:
def safe_division(a, b):
try:
return a / b
except ZeroDivisionError:
return complex(float('inf'), float('inf'))
print(safe_division(1+2j, 0))
8. Casos de uso del mundo real
- Procesamiento de señales de audio. Análisis de espectro con FFT para detección de tono.
- Simulación de circuitos eléctricos. Impedancia de componentes reactivos (Z = R + jX).
- Fractales y generación de Mandelbrot/Julia sets. Renderizado rápido con
numbaotorchen GPU. - Control de sistemas dinámicos. Raíces de la ecuación característica en espacio de estados.
Conclusión
Los números complejos son una herramienta esencial en ciencia e ingeniería. Python ofrece una combinación única de simplicidad (tipo complex), potencia (NumPy, SciPy) y ecosistema de IA que lo convierten en la opción preferida para prototipado rápido y producción a gran escala.
Aplica las buenas prácticas mostradas, elige la librería adecuada según el tamaño del dataset y el nivel de rendimiento requerido, y estarás preparado para abordar cualquier desafío matemático que implique números complejos.
Algoritmo de Números Complejos en Python: Guía Completa con Ejemplos Prácticos