FFT学得还是有点模糊,原理那些基本还是算有所理解了吧,不过自己推这个推不动。

看的资料主要有这两个:

http://blog.miskcoo.com/2015/04/polynomial-multiplication-and-fast-fourier-transform

https://www.zybuluo.com/397915842/note/37965

这儿简单做做笔记。

多项式点值表示

首先$FFT$可以用来快速计算两个多项式的乘积。

一个$n$次多项式(最高次为$n$),可以用系数表示法表示和点值表示法表示。

  • 系数表示法:$A(x)=\sum_{i=0}^{n}c_ix^i$,可以知道用系数表示法进行多项式乘法时间复杂度是$\Theta (n^2)$。
  • 点值表示法:用$n+1$个点$(x_k,y_k),k=0\dots n$,其中$x_k$互不相同,$y_k=A(x_k)$。

这$n+1$个点是可以唯一表示一个$n$次多项式的,因为:$$\begin{bmatrix} 1 & x_0 & x_0^2 & ... & x_0^n \\ 1 & x_1 & x_1^2 & ... & x_1^n \\\vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & x_n & x_n^2 & ... & x_n^n \\\end{bmatrix}\begin{bmatrix} c_0 \\ c_1 \\ \vdots \\c_n \\\end{bmatrix} = \begin{bmatrix} y_0 \\ y_1 \\ \vdots \\y_n \\\end{bmatrix}$$

前面那个方阵是范德蒙德矩阵,由于$x_k$互不相同,其行列式可以知道是非0,所以该方阵存在逆矩阵,方程有解且有唯一解。

现在考虑两个n次多项式$A(x)$和$B(x)$,其乘积的多项式结果是$C(x)$,那么$C(x)$的次数为$2n$。

不妨给$A$和$B$取$2n+1$个点,这样求得两个多项式的点值表示为$(x_k,A(x_k))、(x_k,B(y_k)),k=0\dots 2n$,而$C(x)$的点值表示在$\Theta (n)$就能直接求出,即为$(x_k,A(x_k)B(y_k))$。

这样进行多项式乘法运算,原本用系数表示多项式需要$\Theta (n^2)$的时间复杂度,而点值则只需要$\Theta (n)$。

不过将一个多项式转化成点值的形式,需要枚举$2n$个点,然后对于每一个点$\Theta (n)$求得多项式的值,这样时间复杂度是$\Theta (n^2)$的;另外,求得结果后也是一个点值的表示,需要还原回系数形式,直接解上面矩阵表示的方程,这要立方的时间复杂度。

这时就是$FFT$做的事了。不妨把前面转化为点值形式看作$DFT$,后面逆转化回系数形式看作$IDFT$,而这两个过程利用$FFT$这个算法就能快速进行并得出结果。

DFT

对于多项式$A(x)$,比如我们要取$n$个点$(x_k,y_k)$,$x_k$选取的是$n$次单位根(满足$z^n=1$的复数解$z$),即:$$e^{\frac{2\pi ki}{n}}, k = 0\cdots n - 1, i=\sqrt{-1}$$

另外,计算这个可以用欧拉公式:$$e^{\theta i}=\cos\theta + i\sin\theta$$

记$\omega_n=e^{\frac{2\pi i}{n}}$,那么点值表示即为$$(\omega_n^0, A(\omega_n^0)), (\omega_n^1, A(\omega_n^1)),\cdots ,(\omega_n^{n-1}, A(\omega_n^{n-1}))$$

现在如何求$A(\omega_n^0), A(\omega_n^1), \cdots, A(\omega_n^{n-1})$?

看下面:$$A(x) = c_0 + c_1x + c_2x^2 + c_3x^3 + \cdots + c_nx^{n-1}\\A^{[0]}(x) = c_0 + c_2x + c_4x^2 + \cdots + c_{n - 2}x^{n/2 - 1} \\A^{[1]}(x) = c_1 + c_3x + c_5x^2 + \cdots + c_{n - 1}x^{n/2 - 1}$$

然后,可以发现$$A(x) = A^{[0]}(x^2) + xA^{[1]}(x^2)$$

而这边是将$\omega_n^k,k=0\cdots n-1$代入$x$,其中$x^2$也就是$(\omega_n^k)^2$可以化简:$$(\omega_n^k)^2=\left(e^{\frac{2\pi ki}{n}}\right)^2=e^{\frac{2\pi ki}{n/2}}=\omega_{\frac{n}{2}}^k$$而$\omega_{\frac{n}{2}}^k$复数根有$n/2$个。

那么可以看到,把$A(x)$分成两个子问题,而这两个子问题选取的点也缩减了一半。这样时间复杂度就是$\Theta (nlogn)$了。于是可以用递归去实现这个分治的算法。另外一开始$n$拓展成$2$的次幂,多出来的补$0$即可。

不过,事实上可以改用迭代实现,而不用递归。画出递归的每一次划分。。$此处没图$。。

然后对比最初和最后一层各个下标的二进制,可以发现相当于是每一个下标的二进制反转了。

那么可以一开始直接反转,从最后一层开始,一层层一个个迭代上去去求得结果。

另外,有个现成的$Rader$雷德算法可以直接求得反转结果。

IDFT

上面就在$\Theta (nlogn)$下将系数表示转化成点值表示;而现在需要逆过来,把点值表示还原成所需要的结果,系数表示。$$ \begin{bmatrix} (\omega_n^0)^0 & (\omega_n^0)^1 & \cdots &(\omega_n^0)^{n-1} \\ (\omega_n^1)^0 & (\omega_n^1)^1 & \cdots & (\omega_n^1)^{n-1} \\ \vdots & \vdots & \ddots & \vdots \\ (\omega_n^{n-1})^0 & (\omega_n^{n-1})^1 & \cdots & (\omega_n^{n-1})^{n-1} \end{bmatrix} \begin{bmatrix} c_0 \\ c_1 \\ \vdots \\ c_{n-1} \end{bmatrix} = \begin{bmatrix} A(\omega_n^0) \\ A(\omega_n^1) \\ \vdots \\ A(\omega_n^{n-1}) \end{bmatrix}$$

其实就是求$c_k$。

而那个范德蒙德矩阵的逆也有结论的,就是:$$\frac{1}{n}\begin{bmatrix} (\omega_n^{-0})^0 & (\omega_n^{-0})^1 & \cdots & (\omega_n^{-0})^{n-1} \\ (\omega_n^{-1})^0 & (\omega_n^{-1})^1 & \cdots & (\omega_n^{-1})^{n-1} \\ \vdots & \vdots & \ddots & \vdots \\ (\omega_n^{-(n-1)})^0 & (\omega_n^{-(n-1)})^1 & \cdots & (\omega_n^{-(n-1)})^{n-1} \end{bmatrix}$$

证明过程。。两个矩阵相乘。。我就不搬运了= =。。

这样的话等式两边同时乘上方阵的逆:$$\begin{bmatrix} c_0 \\ c_1 \\ \vdots \\ c_{n-1} \end{bmatrix} = \frac{1}{n} \begin{bmatrix} (\omega_n^{-0})^0 & (\omega_n^{-0})^1 & \cdots & (\omega_n^{-0})^{n-1} \\ (\omega_n^{-1})^0 & (\omega_n^{-1})^1 & \cdots & (\omega_n^{-1})^{n-1} \\ \vdots & \vdots & \ddots & \vdots \\ (\omega_n^{-(n-1)})^0 & (\omega_n^{-(n-1)})^1 & \cdots & (\omega_n^{-(n-1)})^{n-1} \end{bmatrix} \begin{bmatrix} A(\omega_n^0) \\ A(\omega_n^1) \\ \vdots \\ A(\omega_n^{n-1}) \end{bmatrix}$$

那么可以发现,这可以用$DFT$一样的过程去求了,不同的是选的点是$\omega_n^{-k}$,最后结果除以$n$。

总之

总的过程大概就是:

  1. 进行一些预处理,把次数$\times 2$,并拓展成$2$的次幂,高位多出来的设置成$0$;
  2. 用$FFT$进行$DFT$的过程,在$\Theta (nlogn)$分别将两个多项式$A(x)$和$B(x)$转化成点值的形式;
  3. 利用点值表示在$\Theta (n)$直接就能求得$A(x)\times B(x)$的点值表示;
  4. 用$FFT$进行$IDFT$的过程,在$\Theta (nlogn)$将乘积结果的点值表示还原成系数表示。

代码的实现抄的,稍微改了点,写了一遍:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAXN 262144
const double PI=acos(-1.0);struct Complex{double real,imag;Complex(){}Complex(double _real,double _imag):real(_real),imag(_imag){}Complex operator-(const Complex &cp) const{return Complex(real-cp.real,imag-cp.imag);}Complex operator+(const Complex &cp) const{return Complex(real+cp.real,imag+cp.imag);}Complex operator*(const Complex &cp) const{return Complex(real*cp.real-imag*cp.imag,real*cp.imag+imag*cp.real);}void setValue(double _real=0,double _imag=0){real=_real; imag=_imag;}
};int len;
Complex wn[MAXN],wn_anti[MAXN];void FFT(Complex y[],int op){// Rader, 位逆序置换 for(int i=1,j=len>>1,k; i<len-1; ++i){if(i<j) swap(y[i],y[j]);k=len>>1;while(j>=k){j-=k;k>>=1;}if(j<k) j+=k;}// h=1,Wn^0=1 for(int h=2; h<=len; h<<=1){// Wn = e^(2*PI*i/n),如果是插值Wn = e^(-2*PI*i/n),i虚数单位 Complex Wn = (op==1 ? wn[h] : wn_anti[h]);for(int i=0; i<len; i+=h){Complex W(1,0);for(int j=i; j<i+(h>>1); ++j){Complex u=y[j],t=W*y[j+(h>>1)];y[j]=u+t;y[j+(h>>1)]=u-t;W=W*Wn;}}}if(op==-1){for(int i=0; i<len; ++i) y[i].real/=len;}
}
void Convolution(Complex A[],Complex B[],int n){// 初始化 for(len=1; len<(n<<1); len<<=1);for(int i=n; i<len; ++i){A[i].setValue();B[i].setValue();}// e^(θi) = cosθ+isinθ -> Wn = cos(2*PI/n)+isin(2*PI/n)for(int i=0; i<=len; ++i){ // 预处理 wn[i].setValue(cos(2.0*PI/i),sin(2.0*PI/i)); wn_anti[i].setValue(wn[i].real,-wn[i].imag);}FFT(A,1); FFT(B,1);for(int i=0; i<len; ++i){A[i]=A[i]*B[i];}FFT(A,-1);
}

转载于:https://www.cnblogs.com/WABoss/p/FFT_Note.html

快速傅里叶变换FFT学习小记相关推荐

  1. 快速傅里叶变换 FFT 学习笔记

    文章目录 FFT ( 快速傅里叶变换 ) 学习笔记 参考文章: 多项式 系数表示法 点值表示法 复数 前置芝士 向量 弧度制 定义 运算法则 单位根 快速傅里叶变换: 快速傅里叶逆变换 (IFFT): ...

  2. 【文文殿下】快速傅里叶变换(FFT)学习笔记

    多项式 定义 形如\(A(x)=\sum_{i=0}^{n-1} a_i x^i\)的式子称为多项式. 我们把\(n\)称为该多项式的次数界. 显然,一个\(n-1\)次多项式的次数界为\(n\). ...

  3. OpenCV快速傅里叶变换(FFT)用于图像和视讯流的模糊检测

    OpenCV快速傅里叶变换(FFT)用于图像和视频流的模糊检测 翻译自[OpenCV Fast Fourier Transform (FFT) for blur detection in images ...

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

    学习DIP第7天,图像傅里叶变换 转载请标明出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对 ...

  5. 基于python的快速傅里叶变换FFT(二)

    基于python的快速傅里叶变换FFT(二) 本文在上一篇博客的基础上进一步探究正弦函数及其FFT变换. 知识点   FFT变换,其实就是快速离散傅里叶变换,傅立叶变换是数字信号处理领域一种很重要的算 ...

  6. 基于python的快速傅里叶变换FFT(一)

    基于python的快速傅里叶变换FFT(一) FFT可以将一个信号变换到频域.有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易看出特征了.这就是很多信号分析采用FFT变换的原因. ...

  7. MIT 线性代数 Linear Algebra 26:复矩阵,傅里叶矩阵, 快速傅里叶变换 FFT

    这一讲我们来讲一下复矩阵.线性代数中,复矩阵是避免不了的话题,因为一个简单实矩阵都有可能有复数特征值. 复矩阵 我们着重看一下复矩阵和实矩阵在运算上的区别. 距离 首先,一个复数向量的的距离求法发生了 ...

  8. Java中实现快速傅里叶变换FFT

    Java中实现快速傅里叶变换FFT 一.概述 1.傅里叶变换(FT) 2.离散傅里叶变换(DFT) 3.快速傅里叶变换(FFT) 1)单位根 2)快速傅里叶变换的思想 3)蝶形图 4)快速傅里叶变换的 ...

  9. Matlab如何进行利用离散傅里叶变换DFT (快速傅里叶变换FFT)进行频谱分析

    文章目录 1. 定义 2. 变换和处理 3. 函数 4. 实例演示 例1:单频正弦信号(整数周期采样) 例2:单频正弦信号(非整数周期采样) 例3:含有直流分量的单频正弦信号 例4:正弦复合信号 例5 ...

最新文章

  1. Oracle 10g新增DROP DATABASE命令
  2. “解剖”HIGO徐易容:关于创业、后悔、喝酒和滑雪
  3. 设计模式十三:proxy(代理)——对象结构型模式
  4. POJ 1743 (后缀数组+不重叠最长重复子串)
  5. 换脸软件ZAO被“锤”!微信:我的封杀可能会迟到,但不会缺席
  6. 微信公众号 分享接口 签名通过 分享无效果(JSSDK自定义分享接口的策略调整)...
  7. 周期性行业是什么意思_什么样的股票适合长期持有?股票知识学习
  8. 优秀架构师是怎么炼成的?
  9. 190228每日一句
  10. 运筹学那些事,专科学生学习运筹学之运输问题,No.5
  11. ssh远程重装Centos系统
  12. 13个适合上班时做的保健小动作
  13. 论文参考文献生成代码(2021.2.25)
  14. 用户行为分析系统架构
  15. 为什么Hashtab的大小通常取远离2^n 的素数
  16. Java分解整型质因数
  17. S-DES加密算法介绍与实现
  18. 微信输入法语音转文字设计点
  19. apache dubbo 源码分析系列汇总
  20. 线控转向,包含设计说明书,carsim模型,MATLAB Simulink模型全套

热门文章

  1. vue中使用类似html中a标签的锚链接,实现点击定位到当前页面的某个位置
  2. macOS npm -g 安装路径
  3. 教你看懂车牌号——全国车牌详解细表
  4. 注册测绘师20180301-CNSS
  5. 前端-网站性能优化——CDN加速
  6. COMSOL仿真教程—激光烧蚀
  7. 小白友好 | PyCharm新手教程
  8. linux下使用tar命令解压.tar.gz文件是参数的说明
  9. tar命令 文件压缩与解压
  10. android判断主线程_惊天秘密!从Thread开始,揭露Android线程通讯的诡计和主线程的阴谋...