FFT

Posted guoshaoyang

tags:

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

多项式

系数表示法

A(x)A(x)表示一个n-1n1次多项式

A(x)=\sum_i=0^n a_i * x^iA(x)=i=0n?ai?xi

例如:A(3)=2+3*x+x^2A(3)=2+3x+x2

利用这种方法计算多项式乘法复杂度为O(n^2)O(n2)

(第一个多项式中每个系数都需要与第二个多项式的每个系数相乘)

点值表示法

nn互不相同的xx带入多项式,会得到nn个不同的取值yy

则该多项式被这nn个点(x_1,y_1),(x_2,y_2),\dots,(x_n,y_n)(x1?,y1?),(x2?,y2?),,(xn?,yn?)唯一确定

其中y_i=\sum_j=0^n-1 a_j*x_i^jyi?=j=0n1?aj?xij?

例如:上面的例子用点值表示法可以为(0,2),(1,5),(2,12)(0,2),(1,5),(2,12)

利用这种方法计算多项式乘法的时间复杂度仍然为O(n^2)O(n2)

(选点O(n)O(n),每次计算O(n)O(n))

我们可以看到,两种方法的时间复杂度都为O(n^2)O(n2),我们考虑对其进行优化

对于第一种方法,由于每个点的系数都是固定的,想要优化比较困难

对于第二种方法,貌似也没有什么好的优化方法,不过当你看完下面的知识,或许就不这么想了

 复数

在介绍复数之前,首先介绍一些可能会用到的东西

向量

同时具有大小和方向的量

在几何中通常用带有箭头的线段表示

圆的弧度制

等于半径长的圆弧所对的圆心角叫做1弧度的角,用符号rad表示,读作弧度。用弧度作单位来度量角的制度叫做弧度制

公式:

1^\circ =\dfrac\pi180rad1°=180π?rad

180^\circ =\pi rad180°=πrad

平行四边形定则

平行四边形定则:AB+AD=AC

复数

定义

a,ba,b为实数,i^2=-1i2=1,形如a+bia+bi的数叫负数,其中ii被称为虚数单位,复数域是目前已知最大的域

在复平面中,xx代表实数,yy轴(除原点外的点)代表虚数,从原点(0,0)(0,0)到(a,b)(a,b)的向量表示复数a+bia+bi

模长:从原点(0,0)(0,0)到点(a,b)(a,b)的距离,即\sqrta^2+b^2a2+b2?

幅角:假设以逆时针为正方向,从xx轴正半轴到已知向量的转角的有向角叫做幅角

运算法则

加法:

因为在复平面中,复数可以被表示为向量,因此复数的加法与向量的加法相同,都满足平行四边形定则(就是上面那个)

乘法:

几何定义:复数相乘,模长相乘,幅角相加

代数定义:(a+bi)*(c+di)(a+bi)(c+di)

=ac+adi+bci+bdi^2=ac+adi+bci+bdi2

=ac+adi+bci-bd=ac+adi+bcibd

=(ac-bd)+(bc+ad)i=(acbd)+(bc+ad)i

单位根

下文中,默认nn为22的正整数次幂

在复平面上,以原点为圆心,11为半径作圆,所得的圆叫单位圆。以圆点为起点,圆的nn等分点为终点,做nn个向量,设幅角为正且最小的向量对应的复数为\omega_nωn?,称为nn次单位根。

根据复数乘法的运算法则,其余n-1n1个复数为\omega_n^2,\omega_n^3,\ldots,\omega_n^nωn2?,ωn3?,,ωnn?

注意\omega_n^0=\omega_n^n=1ωn0?=ωnn?=1(对应复平面上以xx轴为正方向的向量)

那么如何计算它们的值呢?这个问题可以由欧拉公式解决\omega_n^k=\cos\ k *\frac2\pin+i\sin k*\frac2\pinωnk?=cos kn2π?+isinkn2π?

单位根的幅角为周角的\dfrac1nn1?

在代数中,若z^n=1zn=1,我们把zz称为nn次单位根

单位根的性质

$\omega _n^k=\cos k\dfrac2\pin+i\sin k\dfrac 2\pi n$ωnk?=coskn2π?+isinkn2π?(即上面的公式) \omega _2n^2k=\omega _n^kω2n2k?=ωnk? 证明:

\omega _2n^2k=\cos 2k*\frac2\pi2n+i\sin2k*\frac2\pi2nω2n2k?=cos2k2n2π?+isin2k2n2π?

=\omega _n^k=ωnk?

\omega _n^k+\fracn2=-\omega _n^kωnk+2n??=ωnk?\omega _n^\fracn2=\cos\fracn2*\frac2\pin+i\sin\fracn2*\frac2\pinωn2n??=cos2n?n2π?+isin2n?n2π?

=\cos \pi+i\sin\pi=cosπ+isinπ

=-1=1

\omega _n^0=\omega _n^n=1ωn0?=ωnn?=1 讲了这么多,貌似跟我们的正题没啥关系啊。。

OK!各位坐稳了,前方高能!

快速傅里叶变换

我们前面提到过,一个nn次多项式可以被nn个点唯一确定。

那么我们可以把单位根的00到n-1n1次幂带入,这样也可以把这个多项式确定出来。但是这样仍然是O(n^2)O(n2)的呀!

我们设多项式A(x)A(x)的系数为(a_o,a_1,a_2,\ldots,a_n-1)(ao?,a1?,a2?,,an1?)

那么A(x)=a_0+a_1*x+a_2*x^2+a_3*x^3+a_4*x^4+a_5*x^5+ \dots+a_n-2*x^n-2+a_n-1*x^n-1A(x)=a0?+a1?x+a2?x2+a3?x3+a4?x4+a5?x5+?+an2?xn2+an1?xn1

将其下标按照奇偶性分类

A(x)=(a_0+a_2*x^2+a_4*x^4+\dots+a_n-2*x^n-2)+(a_1*x+a_3*x^3+a_5*x^5+ \dots+a_n-1*x^n-1)A(x)=(a0?+a2?x2+a4?x4+?+an2?xn2)+(a1?x+a3?x3+a5?x5+?+an1?xn1)

A_1(x)=a_0+a_2*x+a_4*x^2+\dots+a_n-2*x^\fracn2-1A1?(x)=a0?+a2?x+a4?x2+?+an2?x2n?1

A_2(x)=a_1*x+a_3*x+a_5*x^2+ \dots+a_n-1*x^\fracn2-1A2?(x)=a1?x+a3?x+a5?x2+?+an1?x2n?1

那么不难得到

A(x)=A_1(x^2)+xA_2(x^2)A(x)=A1?(x2)+xA2?(x2)

我们将\omega_n^k (k<\fracn2)ωnk?(k<2n?)代入得

A(\omega_n^k)=A_1(\omega_n^2k)+\omega_n^kA_2(\omega_n^2k)A(ωnk?)=A1?(ωn2k?)+ωnk?A2?(ωn2k?)

=A_1(\omega_\fracn2^k)+\omega_n^kA_2(\omega_\fracn2^k)=A1?(ω2n?k?)+ωnk?A2?(ω2n?k?)

同理,将\omega_n^k+\fracn2ωnk+2n??代入得

A(\omega_n^k+\fracn2)=A_1(\omega_n^2k+n)+\omega_n^k+\fracn2(\omega_n^2k+n)A(ωnk+2n??)=A1?(ωn2k+n?)+ωnk+2n??(ωn2k+n?)

=A_1(\omega_n^2k*\omega_n^n)-\omega_n^kA_2(\omega_n^2k*\omega_n^n)=A1?(ωn2k?ωnn?)ωnk?A2?(ωn2k?ωnn?)

=A_1(\omega_n^2k)-\omega_n^kA_2(\omega_n^2k)=A1?(ωn2k?)ωnk?A2?(ωn2k?)

大家有没有发现什么规律?

没错!这两个式子只有一个常数项不同!

那么当我们在枚举第一个式子的时候,我们可以O(1)O(1)的得到第二个式子的值

又因为第一个式子的kk在取遍[0,\fracn2-1][0,2n?1]时,k+\fracn2k+2n?取遍了[\fracn2,n-1][2n?,n1]

所以我们将原来的问题缩小了一半!

而缩小后的问题仍然满足原问题的性质,所以我们可以递归的去搞这件事情!

直到多项式仅剩一个常数项,这时候我们直接返回就好啦

时间复杂度:

不难看出FFT是类似于线段树一样的分治算法。

因此它的时间复杂度为O(nlogn)O(nlogn)

快速傅里叶逆变换

不要以为FFT到这里就结束了。

我们上面的讨论是基于点值表示法的。

但是在平常的学习和研究中很少用点值表示法来表示一个多项式。

所以我们要考虑如何把点值表示法转换为系数表示法,这个过程叫做傅里叶逆变换

(y_0,y_1,y_2,\dots,y_n-1)(y0?,y1?,y2?,,yn1?)为(a_0,a_1,a_2,\dots,a_n-1)(a0?,a1?,a2?,,an1?)的傅里叶变换(即点值表示)

设有另一个向量(c_0,c_1,c_2,\dots,c_n-1)(c0?,c1?,c2?,,cn1?)满足

c_k=\sum_i=0^n-1y_i(\omega_n^-k)^ick?=i=0n1?yi?(ωnk?)i

即多项式B(x)=y_0,y_1x,y_2x^2,\dots,y_n-1x^n-1B(x)=y0?,y1?x,y2?x2,,yn1?xn1在\omega_n^0,\omega_n^-1,\omega_n^-2,\dots,\omega_n-1^-(n-1)ωn0?,ωn1?,ωn2?,,ωn1(n1)?处的点值表示

emmmm又到推公式时间啦

(c_0,c_1,c_2,\dots,c_n-1)(c0?,c1?,c2?,,cn1?)满足c_k=\sum_i=0^n-1y_i(\omega_n^-k)^ick?=i=0n1?yi?(ωnk?)i

=\sum_i=0^n-1(\sum_j=0^n-1a_j(\omega_n^i)^j)(\omega_n^-k)^i=i=0n1?(j=0n1?aj?(ωni?)j)(ωnk?)i

=\sum_i=0^n-1(\sum_j=0^n-1a_j(\omega_n^j)^i)(\omega_n^-k)^i=i=0n1?(j=0n1?aj?(ωnj?)i)(ωnk?)i

=\sum_i=0^n-1(\sum_j=0^n-1a_j(\omega_n^j)^i(\omega_n^-k)^i)=i=0n1?(j=0n1?aj?(ωnj?)i(ωnk?)i)

=\sum_i=0^n-1\sum_j=0^n-1a_j(\omega_n^j)^i(\omega_n^-k)^i=i=0n1?j=0n1?aj?(ωnj?)i(ωnk?)i

=\sum_i=0^n-1\sum_j=0^n-1a_j(\omega_n^j-k)^i=i=0n1?j=0n1?aj?(ωnjk?)i

=\sum_j=0^n-1a_j(\sum_i=0^n-1(\omega_n^j-k)^i)=j=0n1?aj?(i=0n1?(ωnjk?)i)

S(x)=\sum_i=0^n-1x^iS(x)=i=0n1?xi

\omega_n^kωnk?代入得

S(\omega_n^k)=1+(\omega_n^k)+(\omega_n^k)^2+\dots(\omega_n^k)^n-1S(ωnk?)=1+(ωnk?)+(ωnk?)2+(ωnk?)n1

k!=0k!=0时

等式两边同乘\omega_n^kωnk?

\omega_n^kS(\omega_n^k)=\omega_n^k+(\omega_n^k)^2+(\omega_n^k)^3+\dots(\omega_n^k)^nωnk?S(ωnk?)=ωnk?+(ωnk?)2+(ωnk?)3+(ωnk?)n

两式相减得

\omega_n^kS(\omega_n^k)-S(\omega_n^k)=(\omega_n^k)^n-1ωnk?S(ωnk?)S(ωnk?)=(ωnk?)n1

S(\omega_n^k)=\frac(\omega_n^k)^n-1\omega_n^k-1S(ωnk?)=ωnk?1(ωnk?)n1?

S(\omega_n^k)=\frac(\omega_n^n)^k-1\omega_n^k-1S(ωnk?)=ωnk?1(ωnn?)k1?

S(\omega_n^k)=\frac1-1\omega_n^k-1S(ωnk?)=ωnk?111?

观察这个式子,不难看出它分母不为0,但是分子为0

因此,当K!=0K!=0时,S(\omega^k_n)=0S(ωnk?)=0

那当k=0k=0时呢?

很显然,S(\omega^0_n)=nS(ωn0?)=n

继续考虑刚刚的式子

c_k=\sum_j=0^n-1a_j(\sum_i=0^n-1(\omega_n^j-k)^i)ck?=j=0n1?aj?(i=0n1?(ωnjk?)i)当j!=kj!=k时,值为00 当j=kj=k时,值为nn 因此,c_k=na_kck?=nak?a_k=\fracc_knak?=nck??

这样我们就得到点值与系数之间的表示啦

以上是关于FFT的主要内容,如果未能解决你的问题,请参考以下文章

解释 numpy.fft.fft2 输出

通过对 FFT 结果进行共轭来使用 FFT 进行 IFFT

如何在matlab的powergui里面进行fft分析

DFT 和 FFT 之间有啥区别使 FFT 如此之快?

FFT算法理解

MATLAB中fft的频率轴怎么计算