写在前面:
初学FFT算法移步:here
讲的真心不错,本文主要记录下自己的理解。
建议阅读的过程中,自己动手算一算需要仔细理解的地方!
overview
定义一个多项式: A(x)=Σj=0n−1aj⋅xj
计算C(x)=A(x)∗B(x)
一般我们要计算两个 n 次多项式的乘积,直接计算的复杂度是O(n2) ,利用 FFT 算法可以达到 O(nlogn) 的复杂度,其中利用到了傅里叶变换,周期性,还有分治策略。大致的思路呢,就是先得到 A(x) 与 B(x) 的点表示法(具体见下文),也就是利用 DFT 算法在 O(nlogn) 时间内得到点表示法,然后得到的两个点表示形式在对应位上直接相乘得到 C(x) 的点表示形式,最后就是逆 DFT 得到 C(x) 的多项式表示。
detail
一般我们将多项式表示为 A(x)=a0+a1x+a2x2+...+an−1xn−1 ,记为: a=(a1,a2,...an−1)
实际上还有另外一种方法来唯一的表征一个多项式 ,即点值法,也就是找出多项式在 n 个不同的点上的取值,记为:
{(x0,y0),(x1,y1),...,(xn−1,yn−1)} ,具体证明略。
现在我们的目的是得到 A(x) 的点值表示,直接代入 n 个数,计算的时间复杂度为O(n2) ,所以现在就用到了 傅里叶变换 去优化这个过程。我们选用n个特殊的值,用处后面自然会感受到:
ω0n,ω1n,...,ωn−1n,其中ωn=e2iπn
你会发现上面的取值 ωin 的周期是 n ,也就是i=n+k 与 i=k 时候的值是一样的。
现在我们对 A(x) 作一些变换:
A(x)=A[0](x2)+xA[1](x2)
A[0](x)=a0+a2x+a4x2+...+an−2xn2−1
A[1](x)=a1+a3x1+a5x2+...+an−1xn2−1现在我们需要分别去计算 A[0](x),A[1](x) 在 上述 n 个数上的取值,即计算
(ωin)2 ,你会发现这个式子的周期是 n/2 ,所以这个 n 个数的序列的 前后半段完全相同 (聪明的你是否想到了优化的地方呢)!也就是我们计算
A[0](x),A[1](x) 的时候,其实只需代入计算 前 n/2 个数,这样问题的规模就缩小了一半,假设我们得到了上述两者的取值结果:v0,v1,...vn/2 和 t0,t1,...tn/2为什么取负号?自己想一想!(^_^
最后就是递归的计算下去,但是每次计算问题的规模都会为上一次的一半,所以整体的复杂度变为 nlogn
接下来就是逆 DFT :
其实逆DFT与DFT的计算过程类似,只需看看我们之前计算 yi 序列的过程:
![]()
那么我们只需要在等式两边分别左乘一个逆矩阵就OK了,如何求上面那个矩阵的逆矩阵呢?
直接给出了矩阵的:C(j,k)=ω−kjnn
具体证明略。
所以要计算 ai 序列,就是一个类似的过程,用逆矩阵去乘我们先前得到的 yi 序列,方法类似嘛,只不过:aj=1nΣk=0n−1ykω−kjn
把之前的 w 替换为w−1 ,最后除以 n 就完成啦!复杂度也是nlogn 。
all
有错误的话,欢迎指出哦!