0 前言
FFT是一个很厉害的算法,几乎任何和信号处理有关的算法都依赖于FFT
0.1 引入:多项式的系数表示法
我们从一个简单的问题中引入FFT:
给定两个多项式,我们希望去计算二者的乘积
中学的时候我们学过,展开相乘就可以了
但是在计算机里面,一个很重要的问题是,如何存储一个多项式?
显然,最自然的方法就是存储多项式的系数,我们把系数映射到一个列表中,这样列表中第k个数字正好对应第k阶系数——>这种表示方法,即是多项式的系数表示法
一般来说,给定两个d阶的多项式,二者的乘积应该是2d阶的多项式,所以如果用naive的乘法分配律来计算,时间复杂度应该是【多项式A中的每一项都会跟多项式B中的所有项分别相乘】
那么问题来了,这个算法可以更快一点吗?
0.2 多项式的数值表示法
我们知道,任意的d阶多项式,可以由d+1个点唯一确定
即对于一个p阶多项式
p+1个点
确定了之后,多项式的系数可以唯一确定
证明如下:
我们将这d+1个点带入多项式中,得到d+1个方程
将其转化为矩阵&向量的形式
我们可以发现,只要这d+1个x不一样,那么矩阵始终可逆(该矩阵对应的行列式为范德蒙行列式)
于是我们得到了多项式的两种表示方法:
利用值表示法,多项式乘法就变得很简单:
我们知道了乘法之后的阶数为4维,于是我们分别在A(x)和B(x)上取5个点,然后将对应每个点的两个值相乘,得到C在每个点的函数值 。然后根据前面的证明,我们知道,这五个点唯一决定了这个乘积多项式的系数
于是我们不难发现,使用值表示法之后,计算多项式乘法的时间,从原来的
缩短至O(d)
0.3 多项式乘法的框架
于是我们就可以得到多项式乘法的新框架了:
给定两个d阶多项式,我们已经知道了值表示法计算多项式乘法计算多项式乘法更快,所以我们可以先计算两个多项式在2d+1个点上的值
然后将函数值一对一对乘起来,从而得到乘出来的多项式的值表示
然后,最后一步需要做的是,把值表示转换回系数表示
但问题在于,我们如何把系数形式转换成值表示形式?与此同时,如何把值表示形式转化为系数表示形式?这个就是FFT考虑的内容了
1 求值 evaluation
我们先关注从系数表示到值表示的方向
给定d阶多项式和n个点(n≥d+1),我们想计算多项式在这n个点上的值,最简单粗暴的方法就是随便挑选n个点,一个一个地计算函数值
但是这样的问题在于,每一个点的计算都是O(d)的时间复杂度,加起来的时间复杂度还是
——>这样的话,相当于啥都没做
对于奇函数和偶函数,我们可以取一对一对的相反数,这样可以减少一半的采样点
对于一般的函数,我们可以将其分成奇函数+偶函数的形式
然后我们将奇函数的x提出来,得到两个偶函数的形式
我们将两部分都看成
的函数,于是可以发现Pe和Po的阶数都降到了原先的一半
那么,对于
和
,这两个又是两个求值问题
而我们一开始取的是一对一对的相反数,所以这里只剩下一半的点了(n/2)
2.1 FFT总结?
我们可以看出来,这是一个递归的思想
总结一下就是,我们想计算多项式P在n个点上的值,这n个点是一对一对的相反数
我们将多项式分成两个部分(两个阶为n/2-1的多项式),每个多项式只需要求n/2个点的值
我们只需要递归地求解这两个分多项式的值,就可以得到原多项式的n个值
如果这一切可行,那么我们的时间复杂度是O(nlogn)【每一层对应点的值相乘还是n,只不过现在从上到下,每一层阶数减半,故只有logn层】
2.2 引入复数的概念
但是至此为止,还是有一个小问题,就是从第一次迭代开始,和
只能取正值,但我们希望新的求指点也可以是相反数
于是我们在这里引入了复数的概念
1的n次方根,用图像表示,可以解释为在复平面上沿着单位元等距排布的一系列点,其中任意两点之间的夹角为
2.2.1 欧拉公式
用欧拉公式,可以很简单地表达这些点:
所以我们找的值就是1,ω,...
2.3 FFT总结!
- FFT算法的输入是多项式的n个系数
,其中n是2的幂(也可以不是,只不过复杂一些)
- 我们取
为1的n次方根 (最底层的递归情况是P(1))
- 递归的主要命令,就是把多项式拆分成奇函数和偶函数的部分,两部分分别调用函数。此时两个子多项式函数都是n/2阶的,所以对应的求值点是1的n/2次方根
- 我们假设得到的两部分结果为ye和yo,那么我们可以把这两部分函数值结合一下,计算出原多项式函数在对应点的函数值
- 结合的核心思想是利用正负对,不过这里我们是n次方根,所以稍作修改
与此同时,我们知道
,于是再做修改
同时,我们又有
于是进一步,可以写成
2.4 FFT伪代码
2.5 用公式表达FFT和iFFT
已知一个序列 ,我们可以用
个傅里叶系数表示,其中第k项为
3 interpolation 差值
实际上差值和求值是紧密联系的,我们之间将求值问题表示为矩阵-向量 乘积
因为FFT中的x是1的n次方根,所以可以改写为
这个矩阵被称为离散傅里叶变换矩阵DFT
而我们的差值,即取逆即可
参考资料:The Fast Fourier Transform (FFT): Most Ingenious Algorithm Ever? - YouTube