快速傅里叶变换(Fast Fourier Transform,FFT),是一种可以在 的时间内完成的离散傅里叶变换(Discrete Fourier transform,DFT)算法,在 OI 中的主要应用之一是加速多项式乘法的计算。
#前置知识
#多项式
#系数表示与点值表示
多项式的系数表示,设 表示一个 次多项式,所有项的系数组成的 维向量 唯一确定了这个多项式。
多项式的点值表示,将一组 互不相同 的 个 带入多项式,得到的 个值。设它们组成的 维向量分别为 、。
#求值与插值
定理:一个 次多项式在 个不同点的取值唯一确定了该多项式。
证明:假设命题不成立,存在两个不同的 次多项式 、,满足对于任何 ,有 。
令 ,则 也是一个 次多项式。对于任何 ,有 。
即 有 个根,这与代数基本定理(一个 次多项式在复数域上有且仅有 个根)相矛盾,故 并不是一个 次多项式,原命题成立,证毕。
如果我们按照定义求一个多项式的点值表示,时间复杂度为 。
已知多项式的点值表示,求其系数表示,可以使用 插值。使用待定系数法的插值算法时间复杂度为 。
#多项式乘法
我们定义两个多项式 与 相乘的结果为 (假设两个多项式次数相同,若不同可在后面补零)。
两个 次多项式相乘,得到的是一个 次多项式,时间复杂度为 。
如果使用两个多项式在 个点处取得的点值表示,那么
时间复杂度为 。
#复数
设 、 为实数,,形如 的数叫做 复数,其中 被称为 虚数单位。复数域是已知最大的域。
关于复数的更多内容详见人民教育出版社出版的《普通高中教科书 数学(必修 第二册)》。
#复平面
在复平面中, 轴代表实数、 轴(除原点外的所有点)代表虚数。每一个复数 对应复平面上一个从 指向 的向量。
该向量的长度 叫做模长。表示从 轴正半轴到该向量的转角的有向(以逆时针为正方向)角叫做幅角。
复数相加遵循平行四边形定则。
复数相乘时,模长相乘,幅角相加。
#单位根
下文中,如不特殊指明,均设 为 的正整数次幂。
在复平面上,以原点为圆心, 为半径作圆,所得的圆叫做单位圆。以原点为起点,单位圆的 等分点为终点,作 个向量。设所得的 幅角为正且最小 的向量对应的复数为 ,称为 次单位根。
由复数乘法的定义(模长相乘,幅角相加)可知,其与的 个向量对应的复数分别为 ,其中 。
单位根的幅角为周角的 ,这为我们提供了一个计算单位根及其幂的公式:
#单位根的性质
性质一:
从几何意义上看,在复平面上,二者表示的向量终点相同。
更具体的,有
性质二:
等式左边相当于 乘上 ,考虑其值
#离散傅里叶变换(DFT)
离散傅里叶变换(Discrete Fourier Transform,DFT)是一种将系数表达转换为点值表达的变换。这一变换可以求出一个 次多项式在每个 次单位根下的点值。
将 次单位根的 到 次幂带入多项式的系数表示,所得点值向量 称为其系数向量 的离散傅里叶变换。
这个过程是 的。
#快速傅里叶变换(FFT)
FFT 是一种高效实现 DFT 的算法,其时间复杂度为 。
#递归分治
考虑将多项式按照系数下标的奇偶分为两部分:
令
则有
假设 ,现在要求
这一步转化利用了单位根的性质一。
对于
这一步转化除性质一外,还利用到了性质二和 这一显然的结论。
注意到,当 取遍 时, 和 取遍了 。
也就是说,如果已知 和 在 处的点值,就可以在 的时间内求得 在 处的取值。而关于 和 的问题都是相对于原问题规模缩小了一半的子问题,分治的边界为一个常数项 。
根据主定理,该分治算法的时间复杂度为
这就是最常用的 FFT 算法 —— Cooley-Tukey 算法。
#迭代优化
递归实现的 FFT 效率不高,实际中一般采用迭代实现。
#二进制位翻转
考虑递归 FFT 分治到边界时,每个数的顺序,及其二进制位。
000 001 010 011 100 101 110 111
0 1 2 3 4 5 6 7
0 2 4 6 - 1 3 5 7
0 4 - 2 6 - 1 5 - 3 7
0 - 4 - 2 - 6 - 1 - 5 - 3 - 7
000 100 010 110 001 101 011 111
发现规律,分治到边界后的下标等于原下标的二进制位翻转。
代码实现,枚举每个二进制位即可。
1 |
|
#蝴蝶操作
考虑合并两个子问题的过程,假设 和 分别存在 和 中, 和 将要被存放在 和 中,合并的单位操作可表示为
考虑加入一个临时变量 ,使得这个过程可以在原地完成,不需要另一个数组 ,也就是说,将 和 存放在 和 中,覆盖原来的值
这一过程被称为 蝴蝶操作。
#离散傅里叶逆变换(IDFT)
将点值表示的多项式转化为系数表示,同样可以使用快速傅里叶变换,这个过程叫做 傅里叶逆变换(Inverse Discrete Fourier Transform,IDFT)。
设 为 的傅里叶变换。考虑另一个向量 满足
即多项式 在 处的点值表示。
将上式展开,得
考虑一个式子
当 时,两边同时乘上 得
两式相减,整理后得
分子为零,分母不为零,所以
当 时,显然 。
继续考虑上式
当 时,,否则 ,即
所以,使用单位根的倒数代替单位根,做一次类似快速傅里叶变换的过程,再将结果每个数除以 ,即为傅里叶逆变换的结果。
但是这样实现起来有点麻烦,而 可以看做 次本原单位根每次逆时针旋转本原单位根幅角的弧度,因此 和 是一一对应的。具体的,。因此我们只需要使用 FFT 的方法,求出 在 各个幂次下的值,然后数组反过来,即令 即可。
这一步快速计算插值的过程叫做 快速傅里叶逆变换(Inverse Fast Fourier Transform,IFFT)。
#代码实现
1 |
|
1 |
|
#参考资料
本文的大部分内容转载自 FFT 学习笔记 - Menci’s OI Blog,在此表示感谢。此外,本文还参考了以下文章:
- P3803 【模板】多项式乘法(FFT) 题解,—扶苏—,2019 年 12 月 25 日。
- 快速傅里叶变换(FFT)详解,自为风月马前卒,2018 年 2 月 11 日。