专业网站建设品牌,十四年专业建站经验,服务6000+客户--广州京杭网络
免费热线:400-683-0016      微信咨询  |  联系我们

如何使用MATLAB对FFT的算法进行性能对比_python

当前位置:网站建设 > 技术支持
资料来源:网络整理       时间:2023/3/9 4:24:48       共计:3604 浏览

如何使用MATLAB对FFT的算法进行性能对比?

FFT是干什么的

FFT在算法竞赛中就有一个用途:加速多项式乘法(暴言)

简单来说,形如 a0X0+a1X1+a2X2+?+anXna0X0+a1X1+a2X2+?+anXn 的代数表达式叫做多项式,可以记作f(X)=a0X0+a1X1+a2X2+?+anXnf(X)=a0X0+a1X1+a2X2+?+anXn,其中a0,a1,?,ana0,a1,?,an叫做多项式的系数,XX是一个不定元(就是不可以合并),不表示任何值,不定元在多项式中最大项的次数称作多项式的次数

如果我们当前有两个多项式f(X),g(X)f(X),g(X),现在要把他们乘起来(求卷积),最朴素的做法就是

∑i=02n?1(∑j=0iai?bi?j)?xi

∑i=02n?1(∑j=0iai?bi?j)?xi

这样的复杂度是Θ(n2)Θ(n2)的,十分不美观,FFT就是要将这个过程优化为Θ(nlogn)Θ(nlog?n)

前置技能

多项式复数

复数形如a+bia+bi,其中i=?1???√i=?1

aa叫作复数的实部,bibi叫做复数的虚部

复数(a1+b1i)?(a2+b2i)(a1+b1i)?(a2+b2i)相乘的值,即a1a2?b1b2+(a1b2+a2b1)ia1a2?b1b2+(a1b2+a2b1)i也是一个复数,同时我们也得到了复数的乘法法则

复数c+dic+di可以用这种方式表示出来

(c+di)

复数乘法的在复平面中表现为辐角相加,模长相乘

单位根

复数ww满足wn=1wn=1称作ww是nn次单位根,下图包含了所有的88次单位根(图中圆的半径是1)

8次单位根

同样的,下图是所有的4次单位根

4次单位根

聪明的你也许已经发现了单位根的些许性质,即

w2m2n=wmnw2n2m=wnm

wmn=?wm+n2nwnm=?wnm+n2

这两个要记住,一会很有用

多项式的系数表达法

我们有多项式f(X)=a0X0+a1X1+a2X2+?+anXnf(X)=a0X0+a1X1+a2X2+?+anXn,令a? =(a0,a1,a2,...,an)a→=(a0,a1,a2,...,an),则称A(X)A(X)为多项式f(X)f(X)的系数表示法

在系数表示法下,计算多项式乘法是Θ(n2)Θ(n2)的

多项式的点值表达法

任取n+1n+1个互不相同的S={p1,p2,...,pn+1}S={p1,p2,...,pn+1},对f(X)f(X)分别求值得到f(p1),f(p2),...,f(pn+1)f(p1),f(p2),...,f(pn+1),那么称A(X)={(p1,f(p1)),(p2,f(p2)),...,(pn+1,f(pn+1))}A(X)={(p1,f(p1)),(p2,f(p2)),...,(pn+1,f(pn+1))}为多项式f(X)f(X)在SS下的点值表示法

可以把多项式想象成一个nn次函数,点值表示法就是取SS下每一个横坐标时对应的点,因为nn次函数可以由n+1n+1个点确定下来(可以将每一个点列一个nn次方程),所以nn维点值与nn维系数一一对应

更重要的一点,点值表示法下的乘法运算获得了简化

两个多项式PP,QQ分别取点(x,y1)(x,y1)和(x,y2)(x,y2),P?QP?Q就会取到点(x,y1?y2)(x,y1?y2);令C=P?QC=P?Q,因为C(X)=P(X)?Q(X)C(X)=P(X)?Q(X),所以C(x)=P(x)?Q(x)C(x)=P(x)?Q(x),即C(x)=y1?y2C(x)=y1?y2

所以在点值表示法下,计算多项式乘法是Θ(n)Θ(n)的

FFT的具体过程

FFT就是将系数表示法转化成点值表示法相乘,再由点值表示法转化为系数表示法的过程,第一个过程叫做求值(DFT),第二个过程叫做插值(IDFT)

求值

还记得我们之前提到的单位根吗?再回顾一下:

w2m2n=wmnw2n2m=wnm

wmn=?wm+n2nwnm=?wnm+n2

想要求出一个多项式的点值表示法,需要选出n+1n+1个数分别带入到多项式里面,带入一个数的复杂度是Θ(n)Θ(n)的,那么总复杂度就是Θ(n2)Θ(n2)的,因为单位根有上面两个优美的性质,所以我们尝试可以取nn次单位根组成SS,看看能不能加速我们的运算

设A0(X)A0(X)为A(X)A(X)偶次项的和,设A1(X)A1(X)为A(X)A(X)奇次项的和,即

A0(X)=a0x0+a2x1+...+anxn/2A0(X)=a0x0+a2x1+...+anxn/2

A1(X)=a1x0+a3x1+...+an?1xn/2A1(X)=a1x0+a3x1+...+an?1xn/2

因为A(wmn)=a0w0n+a1wmn+a2w2mn+a3w3mn+...+an?1w(n?1)?mn+anwnmnA(wnm)=a0wn0+a1wnm+a2wn2m+a3wn3m+...+an?1wn(n?1)?m+anwnnm

所以有

? A(wmn)==A0((wmn)2)+wmnA1((wmn)2)A0(wmn2)+wmnA1(wmn2)A(wnm)=A0((wnm)2)+wnmA1((wnm)2)=A0(wn2m)+wnmA1(wn2m)

? A(wm+n2n)==A0((wmn)2)+wm+n2nA1((wmn)2)A0(wmn2)?wmnA1(wmn2)A(wnm+n2)=A0((wnm)2)+wnm+n2A1((wnm)2)=A0(wn2m)?wnmA1(wn2m)

也就是说,只要有了A0(X)A0(X)和A1(X)A1(X)的点值表示,就能在Θ(n)Θ(n)时间算出A(X)A(X)的点值表示,对于当前层确定的位置ii,就可以用下一层的两个值更新当前的值,我们称这个操作为“蝴蝶变换”

因为这个过程一定要求每层都可以分成两大小相等的部分,所以多项式最高次项一定是2p2p(p∈Np∈N)次方,如果不是的话,直接在最高次项补零就可以啦

于是我们有了递归的写法:

void FFT(Complex* a,int len){

if(len==1) return;

Complex* a0=new Complex[len/2];

Complex* a1=new Complex[len/2];

for(int i=0;i<len;i+=2){

a0[i/2]=a[i];

a1[i/2]=a[i+1];

}

FFT(a0,len/2);FFT(a1,len/2);

Complex wn(cos(2*Pi/len),sin(2*Pi/len));

Complex w(1,0);

for(int i=0;i<(len/2);i++){

a[i]=a0[i]+w*a1[i];

a[i+len/2]=a0[i]-w*a1[i];

w=w*wn;

}

return;

但递归版的FFT常数巨大,实现起来比较复杂,于是又有了迭代的写法

重新考虑下递归FFT的过程,在第ii次求解中,我们将所有元素二进制ii位为00的放在了左面,ii位为11的放在了右面,事实上,每个元素最终到的是他二进制颠倒过来的位置

这里写图片描述

再拿一张别人的图

这里写图片描述

迭代写法:

inline void DFT(Complex a[]){

for(int i=0;i<len;i++)///pos[i]代表反转后的位置

if(i<pos[i])

swap(a[i],a[pos[i]]);

for(int i=2,mid=1;i<=len;i<<=1,mid<<=1){///len代表多项式最高次项

///第一层i是枚举合并到了哪一层。

Complex wm(cos(2.0*pi/i),sin(2.0*pi/i));

for(int j=0;j<len;j+=i){///第二层j是枚举合并区间。

Complex w(1,0);

for(int k=j;k<j+mid;k++,w=w*wm){///第三层k是枚举区间内的下标。

Complex l=a[k],r=w*a[k+mid];

a[k]=l+r;a[k+mid]=l-r;

}

}

}

return;

}

插值

有人证出来插值只要将所有wmnwnm换成wm+n2nwnm+n2,也就是所有的虚部取相反数,再将最终结果除以lenlen,就是插值的过程了

究竟为什么?我觉得人生一定要有点遗憾可以参考这里,我就不多说了

支持插值的迭代写法:

const double DFT=2.0,IDFT=-2.0;///进行求值,第二个参数传DFT,插值传IDFT

inline void FFT(Complex a[],double mode){

for(int i=0;i<len;i++)

if(i<pos[i])

swap(a[i],a[pos[i]]);

for(int i=2,mid=1;i<=len;i<<=1,mid<<=1){

Complex wm(cos(2.0*pi/i),sin(mode*pi/i));

for(int j=0;j<len;j+=i){

Complex w(1,0);

for(int k=j;k<j+mid;k++,w=w*wm){

Complex l=a[k],r=w*a[k+mid];

a[k]=l+r;a[k+mid]=l-r;

}

}

}

if(mode==IDFT)

for(int i=0;i<len;i++)

a[i].x/=len;

return;

}

现在,你已经会写FFT了!

版权说明:
本网站凡注明“广州京杭 原创”的皆为本站原创文章,如需转载请注明出处!
本网转载皆注明出处,遵循行业规范,如发现作品内容版权或其它问题的,请与我们联系处理!
欢迎扫描右侧微信二维码与我们联系。
·上一条:server2016安装进入不了可视化界面_服务器 | ·下一条:sqlplus导入脚本卡死_服务器

Copyright © 广州京杭网络科技有限公司 2005-2025 版权所有    粤ICP备16019765号 

广州京杭网络科技有限公司 版权所有