知识总结快速傅里叶变换(FFT)

Posted zyt1253679098

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识总结快速傅里叶变换(FFT)相关的知识,希望对你有一定的参考价值。

这可能是我第五次学FFT了……菜哭qwq

先给出一些个人认为非常优秀的参考资料:

一小时学会快速傅里叶变换(Fast Fourier Transform) - 知乎

小学生都能看懂的FFT!!! - 胡小兔 - 博客园

快速傅里叶变换(FFT)用于计算两个(n)次多项式相乘,能把复杂度从朴素的(O(n^2))优化到(O(nlog_2n))。一个常见的应用是计算大整数相乘。

本文中所有多项式默认(x)为变量,其他字母均为常数。所有角均为弧度制。

一、多项式的两种表示方法

我们平时常用的表示方法称为“系数表示法”,即

[A(x)=sum _{i=0}^n a_ix^i]

上面那个式子也可以看作一个以(x)为自变量的(n)次函数。用(n+1)个点可以确定一个(n)次函数(自行脑补初中学习的二次函数)。所以,给定(n+1)(x)和对应的(A(x)),就可以求出原多项式。用(n+1)个点表示一个(n)次多项式的方式称为“点值表示法”。

在“点值表示法”中,两个多项式相乘是(O(n))的。因为对于同一个(x),把它代入(A)(B)求值的结果之积就是把它带入多项式(A imes B)求值的结果(这是多项式乘法的意义)。所以把点值表示法下的两个多项式的(n+1)个点的值相乘即可求出两多项式之积的点值表示。

线性复杂度点值表示好哇好

但是,把系数表示法转换成点值表示法需要对(n+1)个点求值,而每次求值是(O(n))的,所以复杂度是(O(n^2))。把点值表示法转换成系数表示法据说也是(O(n^2))的(然而我只会(O(n^3))的高斯消元qwq)。所以暴力取点然后算还不如直接朴素算法相乘……

但是有一种神奇的算法,通过取一些具有特殊性质的点可以把复杂度降到(O(nlog_2n))

二、单位根

从现在开始,所有(n)都默认是(2)的非负整数次幂,多项式次数为(n-1)。应用时如果多项式次数不是(2)的非负整数次幂减(1),可以加系数为(0)的项补齐。

先看一些预备知识:

复数(a+bi)可以看作平面直角坐标系上的点((a,b))。这个点到原点的距离称为模长,即(sqrt{a^2+b^2});原点与((a,b))所连的直线与实轴正半轴的夹角称为辐角,即(sin^{-1}frac{b}{a})。复数相乘的法则:模长相乘,辐角相加

把以原点为圆心,(1)为半径的圆(称为“单位圆”)(n)等分,(n)个点中辐角最小的等分点(不考虑(1))称为(n)单位根,记作(omega_n),则这(n)个等分点可以表示为(omega_n^k(0leq k < n))

这里如果不理解,可以考虑周角是(2pi)(n)次单位根的辐角是(frac{2pi}{n})(w_n^k=w_n^{k-1} imes w_n^1),复数相乘时模长均为(1),相乘仍为(1)。辐角(frac{2pi (k-1)}{n})加上单位根的辐角(frac{2pi}{n})变成(frac{2pi k}{n})

单位根具有如下性质:

1.折半引理

[w_{2n}^{2k}=w_n^k]

模长都是(1),辐角(frac{2pi imes 2k}{2n}=frac{2pi k}{n}),故相等。

2.消去引理

[w_n^{k+frac{n}{2}}=-w_n^k]

这个从几何意义上考虑,(w_n^{k+frac{n}{2}})的辐角刚好比(w_n^k)多了(frac{2pi imes frac{n}{2}}{n}=pi),刚好是一个平角,所以它们关于原点中心对称。互为相反数的复数关于原点中心对称。

3.(不知道叫什么的性质)其中(k)是整数

[w_n^{a+kn}=w_n^a]

这个也很好理解:(w_n^n)的辐角是(2pi),也就是转了一整圈回到了实轴正半轴上,这个复数就是实数(1)。乘上一个(w_n^n)就相当于给辐角加了一个周角,不会改变位置。

三、离散傅里叶变换(DFT)

DFT把多项式从系数表示法转换到点值表示法。

我们大力尝试把(n)次单位根的(0)(n-1)次幂分别代入(n-1)次多项式(A(x))。但首先先对(A(x))进行奇偶分组,得到:

[A_1(x)=sum_{i=0}^{frac{n-1}{2}}a_{2i}·x^i]

[A_2(x)=sum_{i=0}^{frac{n-1}{2}}a_{2i+1}·x^i]

则有:

[A(x)=A_1(x^2)+x·A_2(x^2)]

(w_n^k)代入,得:

[A(w_n^k)=A_1(w_n^{2k})+w_n^k·A_2(w_n^{2k})]

根据折半引理,有:

[A(w_n^k)=A_1(w_{frac{n}{2}}^k)+w_n^k·A_2(w_{frac{n}{2}}^k)]

此时有一个特殊情况。当(frac{n}{2}leq k < n),记(a=k-frac{n}{2}),则根据消去引理和上面第三个性质,有:

[w_n^a=-w_n^k]

[w_{frac{n}{2}}^a=w_{frac{n}{2}}^k]

所以

[A(w_n^k)=A_1(w_{frac{n}{2}}^a)-w_n^a·A_2(w_{frac{n}{2}}^a)]

这样变换主要是为了防止右侧式子里出现(w_n)的不同次幂。

按照这个式子可以递归计算。共递归(O(log_2n))层,每层需要(O(n))枚举(k),因此可以在(O(nlog_2n))内把系数表示法变为点值表示法。

四、离散傅里叶反变换(IDFT)

(w_n^k(0leq k<n))代入多项式(A(x))后得到的点值为(b_k),令多项式(B(x))

[B(x)=sum_{i=0}^{n-1}b_ix^i]

一个结论:设(w_n^{-k}(0leq k<n))代入(B(x))后得到的点值为(c_k),则多项式(A(x))的系数(a_k=frac{c_k}{n})。下面来证明这个结论。

[ egin{aligned} c_k&=sum_{i=0}^{n-1}b_i·w_n^{-ik}&=sum_{i=0}^{n-1}sum_{j=0}^{n-1}a_j·w_n^{ij}·w_n^{-ik}&=sum_{j=0}^{n-1}a_jsum_{i=0}^{n-1}w_n^{i(j-k)} end{aligned} ]

脑补一下(sum_{i=0}^{n-1}w_n^{i(j-k)})怎么求。可以看出这是一个公比为(w_n^{j-k})的等比数列。

(j=k)(w_n^0=1),所以上式的值是(n)

否则,根据等比数列求和公式,上式等于(w_n^{j-k}·frac{w_n^{n(j-k)}-1}{w_n^{j-k}-1})(w_n^{n(j-k)})相当于转了整整((j-k))圈,所以值为(1),这个等比数列的和为(0)

由于当(j eq k)时上述等比数列值为(0),所以(c_k=a_kn),即(a_k=frac{c_k}{n})

至此,已经可以写出递归的FFT代码了。(常数大的一批qwq

五、优化

(未完待续……

以上是关于知识总结快速傅里叶变换(FFT)的主要内容,如果未能解决你的问题,请参考以下文章

快速傅里叶变换FFT(Fast Fourier Transform)

快速傅里叶变换(FFT)详解

快速傅里叶变换FFT及其延伸(只是一个引导)

快速傅里叶变换FFT及其延伸(只是一个引导)

快速傅里叶变换fft

FFT(快速傅里叶变换)