快速傅里叶变换

  • 先讲结论
  • 问题引入
  • 点值表示
  • 求值(Evaluate)
  • 引入复数使递归成立
  • 小结


先讲结论

离散傅里叶变换:将系数表示(Coefficient Representation)的多项式转换成点值表示的多项式
快速傅里叶变换(Fast Fourier Transform)是能够在javascript fft javascript fft快速傅里叶变换_傅里叶变换的时间复杂度内完成离散傅里叶变换的一种算法

问题引入

计算一个多项式乘法:

javascript fft javascript fft快速傅里叶变换_多项式_02
javascript fft javascript fft快速傅里叶变换_傅里叶变换_03
javascript fft javascript fft快速傅里叶变换_javascript fft_04


值得注意的是在计算机中对多项式的存储一般是系数表示法,如下:
javascript fft javascript fft快速傅里叶变换_javascript fft_05在计算机中的表示应该是[2,3,1]
javascript fft javascript fft快速傅里叶变换_javascript fft_06在计算机中的表示应该是[0,2,3]
想要计算javascript fft javascript fft快速傅里叶变换_傅里叶变换_07的表示,需要通过一个二重循环,
由此可见,在计算系数表示的矩阵时需要的时间复杂度应该为javascript fft javascript fft快速傅里叶变换_javascript fft_08

点值表示

一个多项式除了用系数表示外,还可以通过多项式函数上的若干个点表示,规范一些的描述就是:

平面上n+1个不同的点可以唯一确定一个n次多项式函数

对于这一点可以用矩阵来证明:
将n+1个点带入到待定系数的n解多项式中可以得到n+1个等式;
写成矩阵的形式,因为各个点都不相同,化简成行阶梯形矩阵可以看到系数矩阵的秩等于增广矩阵的秩;
因此该多项式有唯一解。

事实上这就是拉格朗日插值法
拉格朗日插值法可以找到唯一一个多项式,其恰好在各个观测点上取得观测值,这样的多项式称为拉格朗日插值多项式

插值法:可以将拉格朗日多项式在任意一点的值作为准确值的近似值


当多项式是点值表示的时候再来计算多项式乘法的话:
javascript fft javascript fft快速傅里叶变换_多项式_02可以表示为[(-2,1),(-1,0),(0,1),(1,4),(2,9)]
javascript fft javascript fft快速傅里叶变换_傅里叶变换_03可以表示为[(-2,9),(-1,4),(0,1),(1,0),(2,1)]
计算javascript fft javascript fft快速傅里叶变换_傅里叶变换_07的点值表示的话只需要计算对应点上y值的乘积就好了,可以得到javascript fft javascript fft快速傅里叶变换_傅里叶变换_07的点值表示为[(-2,9),(-1,0),(0,1),(1,0),(2,9)]

不难看出,想要计算javascript fft javascript fft快速傅里叶变换_傅里叶变换_07的点值表示只需要用到一个循环,也就是说时间复杂度只有javascript fft javascript fft快速傅里叶变换_javascript fft_14

我们可以想到,如果先将计算机存储的系数表示的多项式转换成点值表示,再计算多项式乘法的结果,最后将结果的点值表示转换回系数表示,这样是否可以降低时间复杂度?

javascript fft javascript fft快速傅里叶变换_javascript fft_15


如果能够以较低的时间复杂度实现将系数表示的多项式转换成点值表示,就可以极大的降低多项式乘法的时间复杂度,而这个过程就是快速傅里叶变换,这个过程的逆过程就是反傅里叶变换(IFFT)。

求值(Evaluate)

上面我们已经明确了快速傅里叶变换要做的事就是将系数表示的多项式转换成点值表示,这一过程(Coeff -> Value)被称为求值(Evaluate)。

想要用点值表示就需要在多项式函数的图像上找到n+1个不同的点(对于n阶多项式来说):

  • 随便选取n个点并计算多项式的值的时间复杂度依旧是javascript fft javascript fft快速傅里叶变换_javascript fft_16
  • 利用多项式的奇偶性,选取互为相反数的点可以简化计算

举个例子来表明一下这一过程的效果:

将多项式
javascript fft javascript fft快速傅里叶变换_javascript fft_17按照次数分解成奇偶项
javascript fft javascript fft快速傅里叶变换_时间复杂度_18javascript fft javascript fft快速傅里叶变换_javascript fft_19javascript fft javascript fft快速傅里叶变换_傅里叶变换_07javascript fft javascript fft快速傅里叶变换_多项式_21多项式从5阶降维到2阶javascript fft javascript fft快速傅里叶变换_javascript fft_22用同样的方法递归求解javascript fft javascript fft快速傅里叶变换_多项式_21即可,回溯时
javascript fft javascript fft快速傅里叶变换_javascript fft_24即可求解多项式的值

总结一下:

  1. 将多项式按照奇偶项分解成两个低阶多项式
  2. 选取一组点,计算多项式在该点和对称点上的值
  3. 迭代
  4. 回溯

这样做的时间复杂度可以达到javascript fft javascript fft快速傅里叶变换_傅里叶变换比原来的javascript fft javascript fft快速傅里叶变换_javascript fft_08好得多

为了使递归成立,还要解决一个问题就是javascript fft javascript fft快速傅里叶变换_javascript fft_27不会为负,因此引入复数

引入复数使递归成立

为了使递归成立,我们需要:

  • 使任何一个选取的点可以表示成互为相反数的两个数的平方
  • 上述互为相反数的两个点也可以分别表示成两对相反数的平方

javascript fft javascript fft快速傅里叶变换_傅里叶变换_28

为什么说这样就可以实现递归,需要在复平面上解释一下:

1的n次方根可以表示成复平面单位圆上n等分点

javascript fft javascript fft快速傅里叶变换_多项式_29


通过将原多项式降阶,使我们求解多项式的值得时候更快;将上面的解集平方得到的就是降阶后的多项式的解集,而对应在复平面上

javascript fft javascript fft快速傅里叶变换_时间复杂度_30


就可以很清楚的看懂递归的过程

将1的n次方根一一带入多项式求值,这一过程就是离散傅里叶变换(DFT)

小结

所以到现在为止,我们就实现了最开始的目标,将一个系数表示的多项式转换成点值表示的,最后FFT的输出就是多项式在n个n次方根的值

javascript fft javascript fft快速傅里叶变换_时间复杂度_31

整个快速傅里叶变换的伪码和执行流程

参考:
https://www.bilibili.com/video/BV1za411F76U https://zhuanlan.zhihu.com/p/31584464