Convolución, transformada rápida de Fourier y polinomios (2022)
(alvarorevuelta.com)- Si se expanden polinomios de alto grado con el método escolar, hay que multiplicar todos los pares de términos, por lo que el costo O(n²) se convierte rápidamente en un cuello de botella
- La multiplicación de vectores de coeficientes de polinomios es igual a la convolución de señales discretas, y el resultado de
[2, 3, 4]y[5, 6, 7]es[10, 27, 52, 45, 28] - La DFT lleva una señal discreta al dominio de la frecuencia, y la FFT calcula la misma transformación en O(n log n), marcando la diferencia con entradas grandes
- La convolución en el dominio del tiempo se convierte en una multiplicación elemento a elemento en el dominio de la frecuencia, por lo que se puede multiplicar polinomios más rápido transformando con FFT, multiplicando y luego volviendo con IFFT
- En grados pequeños, el costo de ida y vuelta FFT/IFFT puede anular la ventaja, pero a medida que el grado crece, el método con FFT se vuelve más eficiente
Por qué se vuelve lenta la multiplicación de polinomios
- Un polinomio
P(x)se expresa como una suma de términos formados por coeficientesa_ky potencias de la variablex- Por ejemplo,
P(x)=5x²+2x+9es un polinomio de grado 2 - Según la convención de notación, el vector de coeficientes puede representarse como
[5, 2, 9]o como[9, 2, 5]
- Por ejemplo,
- La suma y la resta son relativamente simples, porque basta con sumar o restar los términos del mismo grado
- En Python se puede recorrer cada coeficiente con
zip(p, q)y calculara + boa - b - Si los grados son distintos, se puede usar
zip_longest
- En Python se puede recorrer cada coeficiente con
- La multiplicación requiere multiplicar cada término con los demás y luego volver a combinar los términos del mismo grado, lo que aumenta el volumen de cálculo
- El resultado de
(2x²+3x+4) × (5x²+6x+7)es10x⁴+27x³+52x²+45x+28 - La complejidad de este método es O(n²), y a medida que crece el grado aumenta la cantidad de multiplicaciones necesarias
- El resultado de
Vectores de coeficientes y convolución
- En el dominio discreto, la convolución de dos señales
pyqse define comoy[n]=Σ p[k]·q[n-k] - El cálculo consiste en invertir
q, desplazarla de izquierda a derecha sobrepy sumar los productos de los elementos que se superponen - Las señales de ejemplo son las siguientes
p = [2, 3, 4]q = [5, 6, 7]
- Al invertir y desplazar
q, cada coeficiente de salida se genera en el siguiente orden2×5 = 102×6 + 3×5 = 272×7 + 3×6 + 4×5 = 523×7 + 4×6 = 454×7 = 28
- El resultado de la convolución es
y = [10, 27, 52, 45, 28]- Esto coincide con los coeficientes de
10x⁴+27x³+52x²+45x+28, obtenidos mediante la multiplicación de polinomios - Por lo tanto, la multiplicación de polinomios puede verse como una convolución de vectores de coeficientes
- Esto coincide con los coeficientes de
Transformada de Fourier y FFT
- La transformada de Fourier convierte una señal del dominio del tiempo al dominio de la frecuencia
- Desde la perspectiva del tiempo, la señal se ve como valores en instantes específicos
- Desde la perspectiva de la frecuencia, la señal se interpreta como la suma de distintas frecuencias de oscilación
- Las frecuencias de oscilación se representan con senos y cosenos, cada uno con coeficiente y fase
- Si se aplica FFT a una onda sinusoidal pura de 5 Hz, en el dominio de la frecuencia aparece como un delta en la posición de 5 Hz
- Esto muestra que la onda sinusoidal en el dominio del tiempo puede representarse como un único seno de 5 Hz
- Los términos relacionados se distinguen así
- Fourier Transform(FT): transformada de Fourier definida en el dominio continuo
- Discrete Fourier Transform(DFT): transformada de Fourier definida para señales discretas
- Fast Fourier Transform(FFT): algoritmo que calcula la DFT en O(n log n) en lugar de O(n²)
- La DFT convierte una señal de tiempo discreto
x[n]enX[k]en el dominio de la frecuencia- Cada
X[k]se calcula multiplicando y sumando las muestras de entrada por números complejos que representan una frecuencia específica
- Cada
Convertir en multiplicación en el dominio de la frecuencia
- La ventaja central de la DFT y del dominio de la frecuencia es que la convolución puede convertirse en multiplicación elemento a elemento
- Convolucionar dos señales en el dominio del tiempo equivale a multiplicarlas en el dominio de la frecuencia
- La multiplicación se puede calcular más rápido que la convolución
- El procedimiento para multiplicar polinomios rápidamente es el siguiente
- Transformar los polinomios al dominio de la frecuencia con FFT: O(n log n)
- Multiplicar elemento a elemento en el dominio de la frecuencia: O(n)
- Transformar el resultado de vuelta al dominio del tiempo con IFFT: O(n log n)
- En conjunto, al usar FFT se puede realizar la multiplicación de polinomios con complejidad O(n log n)
- En polinomios grandes, es más rápido que la multiplicación escolar O(n²)
Implementación en Python y benchmark
multiply_naivemultiplica todos los pares de coeficientes con un doble bucle y los suma en la posición de resultadoi + j- La longitud del resultado es
len(p) + len(q) - 1 - Su complejidad es O(n²)
- La longitud del resultado es
multiply_fftrealiza la multiplicación de coeficientes con base en FFT/IFFT- Calcula una longitud que sea potencia de 2 y al menos
len(p) + len(q) - 1, para que quepa el resultado - Rellena ambas entradas con
np.pad - Multiplica elemento a elemento los valores transformados con
np.fft.fft - Vuelve con
np.fft.iffty luego redondea la parte real para convertirla en coeficientes enteros
- Calcula una longitud que sea potencia de 2 y al menos
- Con la entrada de ejemplo
p = [2, 3, 4],q = [5, 6, 7], ambos métodos devuelven[10, 27, 52, 45, 28] - En el benchmark se compara el método con FFT con
multiply_convolve, que usanp.convolveen lugar demultiply_naive- Esto se debe a que
multiply_naivees lento por los bucles de Python, por lo que es difícil compararlo directamente con el método FFT que usanp.fft.fft np.convolverealiza la misma operación en código C de bajo nivel
- Esto se debe a que
- El grado se incrementa en el rango
range(1, 30000, 1000), y para cada grado se generan dos polinomios con coeficientes aleatorios entre 1 y 999999- Para cada método se mide el tiempo promedio con
n_runs = 5 - En grados bajos, el método FFT puede no ser ventajoso por el costo de las transformaciones de ida y vuelta FFT/IFFT
- A medida que aumenta el grado, el método FFT muestra resultados mucho más eficientes
- Para cada método se mide el tiempo promedio con
1 comentarios
Comentarios de Hacker News
Lo que siempre me molesta de estas explicaciones es que normalmente se olvidan del error numérico
No se puede abstraer la multiplicación de coeficientes simplemente como si fuera de “tiempo constante”. Si vas a hacer eso, entonces también podrías abstraer toda la multiplicación desde el principio. Si se toma en cuenta la precisión numérica, está más cerca de O(n (log n)^3) [1]
[1]: http://numbers.computation.free.fr/Constants/Algorithms/fft....
[1] One-Dimensional Quaternion Discrete Fourier Transform and an Approach to Its Fast Computation:
https://www.mdpi.com/2079-9292/12/24/4974
[2] Convolution Theorems for Quaternion Fourier Transform: Properties and Applications:
https://onlinelibrary.wiley.com/doi/10.1155/2013/162769
[3] On the Matrix Form of the Quaternion Fourier Transform and Quaternion Convolution:
https://arxiv.org/abs/2307.01836
Con este método se pueden multiplicar números largos entre sí. La idea clave es que la multiplicación de polinomios es equivalente a la multiplicación usual de números largos sin hacer acarreos
Por ejemplo, si tienes un número de 1000 dígitos, tomas cada dígito como coeficiente de un polinomio con 1000 elementos. Luego puedes multiplicar esos polinomios con el método FFT explicado en el artículo. Para convertir el resultado de nuevo en un número, hay que procesar los acarreos. Si algún elemento es mayor que 10, se pasa el exceso al siguiente dígito, y luego se convierten los coeficientes en un número
Esa es la idea básica, aunque hay sutilezas en la precisión necesaria para los acarreos y en garantizar que redondear el resultado de la FFT al entero más cercano siga siendo correcto. Así es como GMP, la biblioteca más representativa de este campo, multiplica números grandes
Me da curiosidad qué tan grande tiene que ser un número para que esto realmente valga la pena, y en qué se usa
Si todavía no lo has visto, vale la pena ver este video
https://youtu.be/h7apO7q16V0?si=bmgUEMTQSqU3flIv
Es realmente excelente para derivar el algoritmo FFT a partir de la multiplicación de polinomios. Lo vuelvo a ver como cada 6 meses
La propiedad de la FFT de que “la convolución es multiplicación punto a punto” también se cumple en cualquier grupo multiplicativo cíclico. Para una derivación más algebraica, ver https://www.sciencedirect.com/science/article/pii/S002200007...
A veces a esto se le llama “FFT armónica”, y también hay FFT no armónicas: la [LCH14] “additive NTT” sobre GF(2^n), la circle FFT de [HLP24] sobre la variedad unitaria X^2+Y^2=1 en campos finitos, y ecfft de [BCKL21] sobre torres de isogenias de curvas elípticas
[LCH14]: https://arxiv.org/abs/1404.3458
[HLP24]: https://eprint.iacr.org/2024/278
[BCKL21]: https://arxiv.org/pdf/2107.08473
¿Quién habrá sido la primera persona en proponer usar la FFT para una multiplicación de polinomios más rápida?
Me dio curiosidad hace poco y me puse a investigar; aunque no soy muy bueno siguiendo citas, logré remontarme hasta el artículo de 1995 de David Eppstein [0]. Ahí se usa para resolver eficientemente el problema de suma de subconjuntos tras actualizaciones incrementales. Seguro que en el TAOCP de Knuth debe haber algo incluso anterior
También me impactó bastante saber que la suma de subconjuntos exacta permitiendo repeticiones puede resolverse en tiempo subexponencial con multiplicación de polinomios por FFT [1]. Lo importante es que este algoritmo es O(N log N), donde N no es el tamaño del conjunto sino el valor máximo del elemento, así que no es algo como un contraejemplo a P ≠ NP
[0] https://escholarship.org/content/qt6sd695gn/qt6sd695gn.pdf
[1] https://x.com/festivitymn/status/1788362552998580473?s=46&t=...
Se dice que Strassen descubrió un enfoque al estilo de Pollard en 1968, pero no hay documentación al respecto. También vale la pena considerar que, aunque no marque exactamente el nacimiento de la FFT, el artículo de Cooley-Tukey de 1965 [4] sí impulsó en serio la investigación sobre la FFT y sus aplicaciones. Esto fue apenas unos años después
[1] https://doi.org/10.1090/S0025-5718-1971-0301966-0
[2] https://doi.org/10.1016/S0022-0000(71)80014-4
[3] https://doi.org/10.1007/BF02242355
[4] https://doi.org/10.1090/S0025-5718-1965-0178586-1
https://www.cis.rit.edu/class/simg716/FFT_Fun_Profit.pdf
Creo que todo el aprendizaje automático consiste en resolver ecuaciones de convolución
Este artículo lo trata en el contexto del aprendizaje por refuerzo https://arxiv.org/abs/1712.06115, pero la mayoría de los enfoques encajan dentro de ese paradigma
Justo implementé un algoritmo (matrix profile) que usa FFT para calcular productos internos sobre un gran conjunto de subsecuencias de series temporales. La longitud n de la serie puede llegar a cientos de millones
Con la convolución rápida vía FFT, el tiempo de cómputo baja de O(n) a O(log n), y a esta escala la mejora de velocidad es enorme. Si además usas GPU, se acelera todavía más: algo como procesar 10 millones de puntos de datos en 0.1 segundos desde una laptop
El “truco” clave de esta operación parece ser esta observación:
Existe la propiedad de que “hacer la convolución de dos señales en el dominio del tiempo es lo mismo que multiplicar esas dos señales en el dominio de la frecuencia”, y la FFT permite transformar del dominio del tiempo al dominio de la frecuencia. Entonces pasas los polinomios al dominio de la frecuencia con FFT, y ahí solo multiplicas. Eso es más rápido que hacer la convolución. Me pregunto si eso aclara el paso faltante; si todavía falta algo, puedo actualizar el texto
Entonces, ¿la factorización de enteros es una deconvolución discreta? Me pregunto si, al poner lado a lado la representación por FFT, o sea la multiplicación punto a punto, y el tableau, es decir la multiplicación larga normal con suma de acarreos, la simetría se rompe y se puede obtener suficiente información para un algoritmo rápido
Claro, la multiplicación ingenua de polinomios es lenta respecto al grado del polinomio. Pero, ¿cuándo realmente hace falta trabajar con dos polinomios de grado 100?
Por eso da la impresión de que en los sistemas de álgebra computacional no se usan estos métodos.
https://www.youtube.com/watch?v=CcZf_7Fb4Us
https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_cor... es un ejemplo.
[1]: https://github.com/8051enthusiast/delsum
En las entradas del blog de Hazy Research de 2020 a 2023 hay mucha información sobre este enfoque.
"(...) en investigación de física he trabajado con expresiones de casi 1 terabyte de longitud y con más de 100 millones de términos"