快速傅里叶变换,在竞赛中离散傅里叶变换DFT及其逆变换IDFT尤为常用,主要用于快速求多项式的乘积。形式化地说,多项式就是某个f(x)=∑i=0naixi,两个系数分别为ai和bi,那么(f×g)(x)=f(x)g(x)=∑i=0n∑j=0naibjxi+j,容易看出这个多项式是2n次的。
【补充知识1】我们接着先看一下一个叫卷积的东西。卷积实际上就是对应相乘,(f⊗g)(x)=∑i=0naibixi,可以稍微理解一下,实质上我们如果将乘法视为一种变换,卷积就是在向量上的变换的叠加。
【补充知识2】
其实,对于一条n次的多项式曲线,我们可以通过类比认识到我们需要n+1个不同的点就可以唯一确定这条曲线。比如说一次函数需两个点,二次函数需三个点,等等。我们考虑到,我们表达这些函数为f(x)=∑i=0naixi,那么如果我们现在得知了n+1个函数上的点,直接代入就可以相应得出n+1个方程,由于点两两不同,所以这个方程组一定恰有一组解,这也就确定了一条唯一的曲线。
好的,接下来是真正的内容。
我们可以发现,要确定最终的(f⊗g)(x),需要使用2n+1个点值表达,也就是在最终结果曲线上的2n+1个点。我们如果考虑在f(x)和g(x)上取2n+1个点,设为(xi,yfi)和(xi,ygi),我们发现只要将其卷积一下,我们就能够找到这2n+1个点。这是很好理解的,因为将yfi和ygi卷积后作为新的纵坐标所得到的点,显然就在(f⊗g)(x)上。
那么我们的问题就只剩下怎样计算和如何再次快速确定了。
我们考虑下分治,如果我们将系数向量中奇数项和偶数项分开来,我们会发现所得的两个式子间有一定的相似性:
a1x1+a3x3+⋯+a2k+1x2k+1
a0x0+a2x2+⋯+a2kx2k+⋯
实际上,我们考虑将上面的式子变为
x(a1x0+a3x2+⋯+a2k+1x2k+⋯),再将各项的次数折半(因为是偶数),我们现在就能够得到两个规模相对于原来的问题减半的子问题,可以递归求解,这里我们就得出了DFT的算法,由主定理对于
T(n)=2T(n2)+O(n),马上看出其复杂度
O(nlgn)。
但是,在还原的时候,我们却遇到了一个困难:我们如何将点值表达快速变为系数表达?难道我们去用高斯消元法?这样
O(n3)岂不是比原来还慢?我们来观察一下DFT的实质。我们将DFT的效果写成这样一个矩阵:
⎡⎣⎢⎢⎢⎢⎢11⋮1x0x1⋮xn⋯xn0⋯xn1⋱⋯xnn⋮⎤⎦⎥⎥⎥⎥⎥
稍微解释下,根据线性代数,这个东西乘上我们的系数向量之后所得就是对应的
x=x0,1,⋯,n时
(f⊗g)(x)的结果。这个东西是很显然的,考虑用线性变换的角度理解。
然后我们考虑DFT的逆变换,即所谓IDFT,其的实质就是乘上DFT所乘上的这个矩阵的一个逆矩阵。由逆矩阵定义,我们可以考虑猜想在逆矩阵中
(j,k)处,数为
x−jk×C,其中
C是一个常数。那么如果将两个矩阵乘起来,再考虑
(j,j′)处的值,为
∑k=0nxj′−jkC
要满足逆矩阵条件
VV−1=In,我们需要当且仅当
j′=j的时候上式为
1,其余情况为
0。先看第一个条件,代进去得到
∑nk=0C=1,得
C=1n。代回去并根据第二个条件我们有
∑k=0nxj′−jk=0(j′≠j)
然后我并不知道怎么一直推下去,于是我们直接来一个构造法。我们令
xk=wkn,然后我们就得到了一个等比数列,按照上式求和根据公式可以得到
∑k=0n(wkn)j′−j=∑k=0n(wj′−jn)k=(wj′−jn)n−1wj′−jn−1=0
,即(这里跳了一步移项)
(wj′−jn)n=1
我们又考虑到,
j′和
j取遍
0⋯n间的所有值,所以上式可以改为
(wkn)n=wknn=1,其中
k的值域为从
−n到
n。
然后,我们考虑先取
k=1,本质上这里说的就是我们需要这样的一类不同的
2n个数使得
(wn)n=1。显然在实数域中只能有
1(以及
−1,因为我们在分治的时候预先补了零使得次数是
2d)满足这个性质,显然太少了,怎么办?
我们考虑扩展取值范围。一种方法,就是不只考虑实轴,我们可以考虑下复平面。也就是说,我们可以不只考虑
a∈R,而是考虑所有
a+bi∈C,其中
a,b∈R,而这里的
i,是一个曾经数学中往往避开的事物,满足
i2=−1。实质上来说,复数可以理解为一种向量,比如说两个复数相加就是向量作用的叠加,而两个复数的积,则是另一种程度上的叠加。比方说我们考虑到,我们用极坐标也可以确定向量,进而确定复数,我们由变换
f(x)={xy==ρcosθρsinθ
我们可以得出在极坐标意义下
a+bi可以写成
ρ(cosθ+isinθ),考虑两个复数相乘的时候,我们有(涉及高中三角学知识,若无基础可以跳过)
ρ1(cosθ1+isinθ1)×ρ2(cosθ2+isinθ2)=ρ1ρ2(cosθ1cosθ2+isinθ1cosθ2+isinθ2cosθ1−sinθ1sinθ2)
考虑利用各种和差化积公式(具体推导这里略去),得到乘积为
ρ1ρ2(cos(θ1+θ2)+isin(θ1+θ2)),实质上就是长度相乘,旋转角相加。
我在继续来考量下这个概念有什么用。上面的
(wn)n=1在变换群里面其实对应的是一种循环,称作生成元。我们考虑到,如果对应在复平面上,我们考虑令
ρ=1,然后长度上不管怎么乘都不会变,最后也就是
1(由于
ρ被要求是非负实数),然后神奇的东西来了,我们令
θ=2π/n,也就是将整个复平面划分成
n份,那么根据我们刚才的相乘原则,这个复数向量
cos(2πn+isin(2πn))实际上就是我们所要求的
wn。继续考量下它的性质,如果考虑
wkn(0≤k<n),其的
n次方也是
1,因为
(wkn)n=(wnn)k=1k=1,同时在复数意义下,这
n个数也是两两不同的,所以我们如果代入这
n个数,我们就可以在
O(nlgn)的时间内计算出IDFT,而且代码上和DFT差不多,因为只是最后多除了个
n,同时把
wkn换成了
w−kn,因此也可以写在一起,传入参数在某几步上加个特判。
同时复数还有性质
wk+n/2n=wkn×wn/2n,而又容易由角度看出
wn/2n=−1,所以
wk+n/2n=−wkn。这个东西可以用来稍微改进下DFT,这样我们的计算量大概减少了一半左右,代码也是更容易写了。
不过,虽然说DFT在理论上是没有误差的,但是鉴于三角函数浮点数上计算有不小的误差,所以说如果多项式的系数向量都是整数的话,显然最后的系数向量也是整数,那么我们完全可以不在复数域下进行,而是在另一种同样具有群性质的数系下进行,比如说我们同样熟悉的模域下。而这就要在数论上进行讨论了,所以并不写,坑等以后再填……
好吧,最后再提一下,其实是有四种傅里叶变换的,分别对应着时域和频域上的离散和连续,其中如果两个域都是连续的,实质上就是傅里叶级数,也就是将一个函数展开成许多三角函数构成的无穷级数。再换句话说,就是用许多正(余)弦波的叠加去不断拟合某条给定的曲线。当然,你也可以用方波去拟合,这就涉及到快速沃尔什变换(FWT)了。