【知识总结】快速傅里叶变换(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\times 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(0\leq k < n)\)

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

单位根具有如下性质:

1.折半引理

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

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

2.消去引理

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

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

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

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

这个也很好理解:\(w_n^n\)的辐角是\(2\pi\),也就是转了一整圈回到了实轴正半轴上,这个复数就是实数\(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(0\leq k<n)\)代入多项式\(A(x)\)后得到的点值为\(b_k\),令多项式\(B(x)\):

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

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

\[
\begin{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_j\sum_{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 \neq k\)时上述等比数列值为\(0\),所以\(c_k=a_kn\),即\(a_k=\frac{c_k}{n}\)

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

五、优化

(未完待续……

原文地址:https://www.cnblogs.com/zyt1253679098/p/9961191.html

时间: 2024-08-12 00:32:19

【知识总结】快速傅里叶变换(FFT)的相关文章

[学习笔记] 多项式与快速傅里叶变换(FFT)基础

引入 可能有不少OIer都知道FFT这个神奇的算法, 通过一系列玄学的变化就可以在 $O(nlog(n))$ 的总时间复杂度内计算出两个向量的卷积(或者多项式乘法/高精度乘法), 而代码量却非常小. 博主一年半前曾经因COGS的一道叫做"神秘的常数 $\pi$"的题目而去学习过FFT, 但是基本就是照着板子打打完并不知道自己在写些什么鬼畜的东西OwO 不过...博主这几天突然照着算法导论自己看了一遍发现自己似乎突然意识到了什么OwO然后就打了一道板子题还1A了OwO再加上午考试差点AK

快速傅里叶变换FFT

快速傅里叶变换FFT DFT是信号分析与处理中的一种重要变换.但直接计算DFT的计算量与变换区间长度N的平方成正比,当N较大时,计算量太大,直接用DFT算法进行谱分析和信号的实时处理是不切实际的. 1.直接计算DFT 长度为N的有限长序列x(n)的DFT为: 2.减少运算量的思路和方法 思路:N点DFT的复乘次数等于N2.把N点DFT分解为几个较短的DFT,可使乘法次数大大减少.另外,旋转因子WmN具有周期性和对称性. (考虑x(n)为复数序列的一般情况,对某一个k值,直接按上式计算X(k)值需

浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理

浅谈范德蒙德(Vandermonde)方阵的逆矩阵与拉格朗日(Lagrange)插值的关系以及快速傅里叶变换(FFT)中IDFT的原理 只要稍微看过一点线性代数的应该都知道范德蒙德行列式. \[V(x_0,x_1,\cdots ,x_{n-1})=\begin{bmatrix} {1}&{1}&{\cdots}&{1}\{x_{0}}&{x_{1}}&{\cdots}&{x_{n-1}}\{x_{0}^2}&{x_{1}^2}&{\cdots

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

目录 前言 多项式 系数表示法 点值表示法 复数 向量 圆的弧度制 平行四边形定则 复数 运算法则 单位根 单位根的性质 快速傅里叶变换 快速傅里叶逆变换 理论总结 递归实现 迭代实现 本文只讨论FFT在信息学奥赛中的应用 文中内容均为个人理解,如有错误请指出,不胜感激 回到顶部 前言 先解释几个比较容易混淆的缩写吧 DFT:离散傅里叶变换->O(n2)O(n2)计算多项式乘法 FFT:快速傅里叶变换->O(n?log(n)O(n?log?(n)计算多项式乘法 FNTT/NTT:快速傅里叶变换

【笔记篇】(理论向)快速傅里叶变换(FFT)学习笔记w

现在真是一碰电脑就很颓废啊... 于是早晨把电脑锁上然后在旁边啃了一节课多的算导, 把FFT的基本原理整明白了.. 但是我并不觉得自己能讲明白... Fast Fourier Transformation, 快速傅里叶变换, 是DFT(Discrete Fourier Transform, 离散傅里叶变换)的快速实现版本. 据说在信号处理领域广泛的应用, 而且在OI中也有广泛的应用(比如SDOI2017 R2至少考了两道), 所以有必要学习一波.. 划重点: 其实学习FFT最好的教材是<算法导论

图像傅里叶变换(快速傅里叶变换FFT)

学习DIP第7天,图像傅里叶变换 习惯性,开篇废话 今天公司的网不知怎么了,死活打不开CSDN,公司有100多架客机,也有极限速度60kb/s的网速,还有3K的工资. 图像FFT 上篇已经介绍了关于2D FFT的相关知识,这篇只介绍在图像中的应用,对于一幅图像,做二维FFT后,即可得到其傅里叶变换,傅里叶变换后是二维复数矩阵,因为二维数组,如果是实数,是可以通过变换到0~255通过灰度图像显示出来,而变换结果是复数,所以我们通过显示其幅度,即复数的模,来显示傅里叶谱(幅度谱),不废话,上图: 原

快速傅里叶变换FFT学习小记

FFT学得还是有点模糊,原理那些基本还是算有所理解了吧,不过自己推这个推不动. 看的资料主要有这两个: http://blog.miskcoo.com/2015/04/polynomial-multiplication-and-fast-fourier-transform https://www.zybuluo.com/397915842/note/37965 这儿简单做做笔记. 多项式点值表示 首先$FFT$用来快速计算两个多项式的乘积. 一个$n$次多项式(最高次为$n$),可以用系数表示法

几种快速傅里叶变换(FFT)的C++实现

DFT的的正变换和反变换分别为(1)和(2)式.假设有N个数据,则计算一个频率点需要N次复数乘法和N-1次复数加法,整个DFT需要N*N次复数乘 法和N(N-1)次复数加法:由于一次的复数乘法需要进行4次的实数乘法和2次的复数加法,一次的复数加法需要两次的实数加法,因此整个DFT需要 4*N*N次的实数乘法和2*N(N-1)+2*N*N≍4*N*N次的复数加法.当N比较大时,所需的计算工作量相当大,例如N=1024时大约需要 400万次乘法运算,对于实时信号处理来说,将对计算设备提出非常苛刻的要

快速傅里叶变换(FFT)模板

//有多少项N取多至少8~10倍 #include <algorithm> #include <cmath> #include <complex> #include <cstdlib> using namespace std; typedef complex<double> cn; const int N=1e7+1; const double Pi=3.1415926535;//Pi通常取10位即可(随意) cn a[N],b[N]; int

快速傅里叶变换(FFT):COGS 2216. 你猜是不是KMP

2216. 你猜是不是KMP ★★★☆   输入文件:guess.in   输出文件:guess.out   简单对比时间限制:1 s   内存限制:256 MB [题目描述] XX在玩两个串的游戏.首先,他拿出了两个字符串 S 和 T,XX想知道 T在 S 中出现了几次,分别在哪些位置出现.注意 T 中可能有“?”字符,这个字符可以匹配任何字符. [输入格式] 两行两个字符串,分别代表 S 和 T [输出格式] 第一行一个正整数 k,表示 T 在 S 中出现了几次. 接下来 k 行正整数, 分