FFT
Posted guoshaoyang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFT相关的知识,希望对你有一定的参考价值。
多项式
系数表示法
设A(x)A(x)表示一个n-1n−1次多项式
则A(x)=\sum_i=0^n a_i * x^iA(x)=∑i=0n?ai?∗xi
例如:A(3)=2+3*x+x^2A(3)=2+3∗x+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=0n−1?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+bci−bd
=(ac-bd)+(bc+ad)i=(ac−bd)+(bc+ad)i
单位根
下文中,默认nn为22的正整数次幂
在复平面上,以原点为圆心,11为半径作圆,所得的圆叫单位圆。以圆点为起点,圆的nn等分点为终点,做nn个向量,设幅角为正且最小的向量对应的复数为\omega_nωn?,称为nn次单位根。
根据复数乘法的运算法则,其余n-1n−1个复数为\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 k∗n2π?+isink∗n2π?
单位根的幅角为周角的\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?=cos2k∗2n2π?+isin2k∗2n2π?
=\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-1n−1次幂带入,这样也可以把这个多项式确定出来。但是这样仍然是O(n^2)O(n2)的呀!
我们设多项式A(x)A(x)的系数为(a_o,a_1,a_2,\ldots,a_n-1)(ao?,a1?,a2?,…,an−1?)
那么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+?+an−2?∗xn−2+an−1?∗xn−1
将其下标按照奇偶性分类
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+?+an−2?∗xn−2)+(a1?∗x+a3?∗x3+a5?∗x5+?+an−1?∗xn−1)
设
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+?+an−2?∗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+?+an−1?∗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?,n−1]
所以我们将原来的问题缩小了一半!
而缩小后的问题仍然满足原问题的性质,所以我们可以递归的去搞这件事情!
直到多项式仅剩一个常数项,这时候我们直接返回就好啦
时间复杂度:
不难看出FFT是类似于线段树一样的分治算法。
因此它的时间复杂度为O(nlogn)O(nlogn)
快速傅里叶逆变换
不要以为FFT到这里就结束了。
我们上面的讨论是基于点值表示法的。
但是在平常的学习和研究中很少用点值表示法来表示一个多项式。
所以我们要考虑如何把点值表示法转换为系数表示法,这个过程叫做傅里叶逆变换
(y_0,y_1,y_2,\dots,y_n-1)(y0?,y1?,y2?,…,yn−1?)为(a_0,a_1,a_2,\dots,a_n-1)(a0?,a1?,a2?,…,an−1?)的傅里叶变换(即点值表示)
设有另一个向量(c_0,c_1,c_2,\dots,c_n-1)(c0?,c1?,c2?,…,cn−1?)满足
c_k=\sum_i=0^n-1y_i(\omega_n^-k)^ick?=i=0∑n−1?yi?(ωn−k?)i
即多项式B(x)=y_0,y_1x,y_2x^2,\dots,y_n-1x^n-1B(x)=y0?,y1?x,y2?x2,…,yn−1?xn−1在\omega_n^0,\omega_n^-1,\omega_n^-2,\dots,\omega_n-1^-(n-1)ωn0?,ωn−1?,ωn−2?,…,ωn−1−(n−1)?处的点值表示
emmmm又到推公式时间啦
(c_0,c_1,c_2,\dots,c_n-1)(c0?,c1?,c2?,…,cn−1?)满足c_k=\sum_i=0^n-1y_i(\omega_n^-k)^ick?=i=0∑n−1?yi?(ωn−k?)i
=\sum_i=0^n-1(\sum_j=0^n-1a_j(\omega_n^i)^j)(\omega_n^-k)^i=i=0∑n−1?(j=0∑n−1?aj?(ωni?)j)(ωn−k?)i
=\sum_i=0^n-1(\sum_j=0^n-1a_j(\omega_n^j)^i)(\omega_n^-k)^i=i=0∑n−1?(j=0∑n−1?aj?(ωnj?)i)(ωn−k?)i
=\sum_i=0^n-1(\sum_j=0^n-1a_j(\omega_n^j)^i(\omega_n^-k)^i)=i=0∑n−1?(j=0∑n−1?aj?(ωnj?)i(ωn−k?)i)
=\sum_i=0^n-1\sum_j=0^n-1a_j(\omega_n^j)^i(\omega_n^-k)^i=i=0∑n−1?j=0∑n−1?aj?(ωnj?)i(ωn−k?)i
=\sum_i=0^n-1\sum_j=0^n-1a_j(\omega_n^j-k)^i=i=0∑n−1?j=0∑n−1?aj?(ωnj−k?)i
=\sum_j=0^n-1a_j(\sum_i=0^n-1(\omega_n^j-k)^i)=j=0∑n−1?aj?(i=0∑n−1?(ωnj−k?)i)
设S(x)=\sum_i=0^n-1x^iS(x)=∑i=0n−1?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?)n−1
当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?)n−1
S(\omega_n^k)=\frac(\omega_n^k)^n-1\omega_n^k-1S(ωnk?)=ωnk?−1(ωnk?)n−1?
S(\omega_n^k)=\frac(\omega_n^n)^k-1\omega_n^k-1S(ωnk?)=ωnk?−1(ωnn?)k−1?
S(\omega_n^k)=\frac1-1\omega_n^k-1S(ωnk?)=ωnk?−11−1?
观察这个式子,不难看出它分母不为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=0∑n−1?aj?(i=0∑n−1?(ωnj−k?)i)当j!=kj!=k时,值为00 当j=kj=k时,值为nn 因此,c_k=na_kck?=nak?a_k=\fracc_knak?=nck??
这样我们就得到点值与系数之间的表示啦
以上是关于FFT的主要内容,如果未能解决你的问题,请参考以下文章