【总结】FFT算法在信息竞赛中的应用
2018-02-05 11:45
204 查看
FFT算法本身就是一种优化,优化(类似)卷积运算的时间复杂度
(卷积:∑i,jai∗bj−i∑i,jai∗bj−i)。
FFT的本质,其实是利用复数的一些特殊性质,将一个多项式快速地在点值和系数两种表示方法间来回切换。再利用两个多项式点值表示法相乘的复杂度为O(n),来达到降时间的目的。
复数:一种形如a+bia+bi的数称为复数,其中aa为实部,bb为虚部,ii为虚部单位(i2=−1)(i2=−1)
对于每个a+bia+bi,a−bia−bi为它的共轭复数
其实可以将复数看作一个二项式,其运算法则与实数概念上的二项式几乎相同,只需要注意i即可。
另外,复数也可以用向量来表示:表示为在复平面上(a,b)的向量。向量的模长为a2+b2−−−−−−√a2+b2。
所以,同样地,复数还可以用三角函数来表示:即
a+bi=a2+b2−−−−−−√∗(cosθ+sinθi)a+bi=a2+b2∗(cosθ+sinθi)
在这种表示方法下,两个复数相乘,就可以表示为其角度相加,模长相乘
p∗(cosα+sinαi)∗q∗(cosβ+sinβi)p∗(cosα+sinαi)∗q∗(cosβ+sinβi)
=p∗q∗(sinα∗cosβi+cosα∗sinβi−sinα∗sinβ+cosα∗cosβ)=p∗q∗(sinα∗cosβi+cosα∗sinβi−sinα∗sinβ+cosα∗cosβ)
=p∗q∗(cos(α+β)+sin(α+β)i)=p∗q∗(cos(α+β)+sin(α+β)i)
然后是多项式的一些概念:
首先我们将A(x)=anxn+an−1xn−1+an−2xn−2...a1x+a0A(x)=anxn+an−1xn−1+an−2xn−2...a1x+a0称为一个多项式,同时这种形式也就是系数表示法。
点值表示法:
我们可以将n-1次(包括常数一共n项)的多项式,用位于A(x)上的n个点来表示。
很容易就会发现,点值表示时,两多项式相乘复杂度为O(n)
单位复根就是ωn=1ωn=1的n个解
设ωkn=cosθ+sinθi,θ=2kπn(k=0,1,2...n−1)ωnk=cosθ+sinθi,θ=2kπn(k=0,1,2...n−1)
很容易发现:ωkn=ωk+nnωnk=ωnk+n即有周期性
首先1n∑n−1k=0ωvkn=[vmodn=0]1n∑k=0n−1ωnvk=[vmodn=0]
将v换为p+q
[(p+q)modn=r]=[(p+q−r)=0]=1n∑n−1k=0ω(p+q−r)kn=1n∑n−1k=0ωpknωqknω−rkn[(p+q)modn=r]=[(p+q−r)=0]=1n∑k=0n−1ωn(p+q−r)k=1n∑k=0n−1ωnpkωnqkωn−rk
设crcr为多项式相乘后第i项的系数
有:cr=∑p,q[(p+q)mod n=r]apbq=1n∑n−1k=0ωpknωqknω−rknapbq=1n∑n−1k=0ω−rknωpknapωqknbqcr=∑p,q[(p+q)mod n=r]apbq=1n∑k=0n−1ωnpkωnqkωn−rkapbq=1n∑k=0n−1ωn−rkωnpkapωnqkbq
现在,令dm=∑n−1k=0ωmknakdm=∑k=0n−1ωnmkak
gm=∑n−1k=0ωmknbkgm=∑k=0n−1ωnmkbk
hm=dm×gmhm=dm×gm
则cm=1n∑n−1k=0ω−mkhkcm=1n∑k=0n−1ω−mkhk
将a,b转化为d,e以及将h转化为c,都是离散傅里叶变换,其复杂度均为O(n2)O(n2)。
一个很简单的思路是这样的:
首先只针对于n为2的整次幂的情况
将A(x)按照奇偶次项分离,记为A0(x),A1(x)A0(x),A1(x)
对于任意m<n2m<n2
A(ωmn)=A0(ω2mn)+ωmnA1(ω2mn)=A0(ωmn/2)+ωmnA1(ω2mn)A(ωnm)=A0(ωn2m)+ωnmA1(ωn2m)=A0(ωn/2m)+ωnmA1(ωn2m)
A(ωm+n/2n)=A0(ω2mn)+ωm+n/2nA1(ω2mn)=A0(ωmn/2)−ωmnA1(ω2mn)A(ωnm+n/2)=A0(ωn2m)+ωnm+n/2A1(ωn2m)=A0(ωn/2m)−ωnmA1(ωn2m)
这样一来,我们就可以通过二分来计算离散傅里叶变换。这就是快速傅里叶变换
缺点是常数过大。
因此,我们需要迭代版本:
观察递归时函数的参数:
(a0,a1,a2,a3,a4,a5,a6,a7)(a0,a1,a2,a3,a4,a5,a6,a7)
(a0,a2,a4,a6)(a1,a3,a5,a7)(a0,a2,a4,a6)(a1,a3,a5,a7)
(a0,a4)(a2,a6)(a1,a5)(a3,a7)(a0,a4)(a2,a6)(a1,a5)(a3,a7)
(a0)(a4)(a2)(a6)(a1)(a5)(a3)(a7)(a0)(a4)(a2)(a6)(a1)(a5)(a3)(a7)
在二进制下,可以表示为:
000,100,010,110,001,101,011,111
如果你从前向后看,会发现其实是一个倒序进位的二进制数!
这样,就能知道每一层的参数中系数的顺序了。
迭代版的模板:
看到这里,建议再往回看开头,也许能加深印象
提供一道模板题:UOJ34多项式乘法
(卷积:∑i,jai∗bj−i∑i,jai∗bj−i)。
FFT的本质,其实是利用复数的一些特殊性质,将一个多项式快速地在点值和系数两种表示方法间来回切换。再利用两个多项式点值表示法相乘的复杂度为O(n),来达到降时间的目的。
FFT算法的前导概念
首先介绍关于复数的一些定义及性质复数:一种形如a+bia+bi的数称为复数,其中aa为实部,bb为虚部,ii为虚部单位(i2=−1)(i2=−1)
对于每个a+bia+bi,a−bia−bi为它的共轭复数
其实可以将复数看作一个二项式,其运算法则与实数概念上的二项式几乎相同,只需要注意i即可。
另外,复数也可以用向量来表示:表示为在复平面上(a,b)的向量。向量的模长为a2+b2−−−−−−√a2+b2。
所以,同样地,复数还可以用三角函数来表示:即
a+bi=a2+b2−−−−−−√∗(cosθ+sinθi)a+bi=a2+b2∗(cosθ+sinθi)
在这种表示方法下,两个复数相乘,就可以表示为其角度相加,模长相乘
p∗(cosα+sinαi)∗q∗(cosβ+sinβi)p∗(cosα+sinαi)∗q∗(cosβ+sinβi)
=p∗q∗(sinα∗cosβi+cosα∗sinβi−sinα∗sinβ+cosα∗cosβ)=p∗q∗(sinα∗cosβi+cosα∗sinβi−sinα∗sinβ+cosα∗cosβ)
=p∗q∗(cos(α+β)+sin(α+β)i)=p∗q∗(cos(α+β)+sin(α+β)i)
然后是多项式的一些概念:
首先我们将A(x)=anxn+an−1xn−1+an−2xn−2...a1x+a0A(x)=anxn+an−1xn−1+an−2xn−2...a1x+a0称为一个多项式,同时这种形式也就是系数表示法。
点值表示法:
我们可以将n-1次(包括常数一共n项)的多项式,用位于A(x)上的n个点来表示。
很容易就会发现,点值表示时,两多项式相乘复杂度为O(n)
FFT算法介绍
首先还需要引入一个单位复根的概念:单位复根就是ωn=1ωn=1的n个解
设ωkn=cosθ+sinθi,θ=2kπn(k=0,1,2...n−1)ωnk=cosθ+sinθi,θ=2kπn(k=0,1,2...n−1)
很容易发现:ωkn=ωk+nnωnk=ωnk+n即有周期性
离散傅里叶变换
下面开始玄学推理:首先1n∑n−1k=0ωvkn=[vmodn=0]1n∑k=0n−1ωnvk=[vmodn=0]
将v换为p+q
[(p+q)modn=r]=[(p+q−r)=0]=1n∑n−1k=0ω(p+q−r)kn=1n∑n−1k=0ωpknωqknω−rkn[(p+q)modn=r]=[(p+q−r)=0]=1n∑k=0n−1ωn(p+q−r)k=1n∑k=0n−1ωnpkωnqkωn−rk
设crcr为多项式相乘后第i项的系数
有:cr=∑p,q[(p+q)mod n=r]apbq=1n∑n−1k=0ωpknωqknω−rknapbq=1n∑n−1k=0ω−rknωpknapωqknbqcr=∑p,q[(p+q)mod n=r]apbq=1n∑k=0n−1ωnpkωnqkωn−rkapbq=1n∑k=0n−1ωn−rkωnpkapωnqkbq
现在,令dm=∑n−1k=0ωmknakdm=∑k=0n−1ωnmkak
gm=∑n−1k=0ωmknbkgm=∑k=0n−1ωnmkbk
hm=dm×gmhm=dm×gm
则cm=1n∑n−1k=0ω−mkhkcm=1n∑k=0n−1ω−mkhk
将a,b转化为d,e以及将h转化为c,都是离散傅里叶变换,其复杂度均为O(n2)O(n2)。
快速傅里叶变换
现在我们要降低离散傅里叶变换的时间复杂度。一个很简单的思路是这样的:
首先只针对于n为2的整次幂的情况
将A(x)按照奇偶次项分离,记为A0(x),A1(x)A0(x),A1(x)
对于任意m<n2m<n2
A(ωmn)=A0(ω2mn)+ωmnA1(ω2mn)=A0(ωmn/2)+ωmnA1(ω2mn)A(ωnm)=A0(ωn2m)+ωnmA1(ωn2m)=A0(ωn/2m)+ωnmA1(ωn2m)
A(ωm+n/2n)=A0(ω2mn)+ωm+n/2nA1(ω2mn)=A0(ωmn/2)−ωmnA1(ω2mn)A(ωnm+n/2)=A0(ωn2m)+ωnm+n/2A1(ωn2m)=A0(ωn/2m)−ωnmA1(ωn2m)
这样一来,我们就可以通过二分来计算离散傅里叶变换。这就是快速傅里叶变换
代码实现
首先是比较浅显易懂,与讲解结合较紧密的递归版本缺点是常数过大。
因此,我们需要迭代版本:
观察递归时函数的参数:
(a0,a1,a2,a3,a4,a5,a6,a7)(a0,a1,a2,a3,a4,a5,a6,a7)
(a0,a2,a4,a6)(a1,a3,a5,a7)(a0,a2,a4,a6)(a1,a3,a5,a7)
(a0,a4)(a2,a6)(a1,a5)(a3,a7)(a0,a4)(a2,a6)(a1,a5)(a3,a7)
(a0)(a4)(a2)(a6)(a1)(a5)(a3)(a7)(a0)(a4)(a2)(a6)(a1)(a5)(a3)(a7)
在二进制下,可以表示为:
000,100,010,110,001,101,011,111
如果你从前向后看,会发现其实是一个倒序进位的二进制数!
这样,就能知道每一层的参数中系数的顺序了。
迭代版的模板:
void fft(cpx *a,int f){ int i,j,k; for(i=1,j=0;i<N-1;i++){//N为大于两多项式长度之和的最小的2的整次幂 for(int d=N;j^=d>>=1,~j&d;); if(i<j) swap(a[i],a[j]); } for(i=1;i<N;i<<=1){ cpx wn(cos(Pi/i),f*sin(Pi/i)); for(j=0;j<N;j+=i<<1){ cpx w(1,0); for(k=0;k<i;k++,w=w*wn){ cpx x=a[j+k],y=w*a[j+k+i]; a[j+k]=x+y; a[j+k+i]=x-y; } } } if(f==-1) for(i=0;i<N;i++) a[i].r/=N; }
看到这里,建议再往回看开头,也许能加深印象
提供一道模板题:UOJ34多项式乘法
相关文章推荐
- 【算法】排序算法及其应用总结
- 颜色不变性算法及应用总结
- 颜色不变性算法及应用总结
- Linux服务器各应用版本信息查看总结
- 数据挖掘十大算法总结--核心思想,算法优缺点,应用领域
- 20135328信息安全系统设计基础第一周学习总结(Linux应用)
- 【算法总结--数组相关】双指针法的常见应用。
- 【Jason's_Knowledge】算法竞赛中的快速排序及其应用
- 基于局部均方差相关信息的图像去噪及其在实时磨皮美容算法中的应用。
- 颜色不变性算法及应用总结
- 算法总结系列之八:复读机的故事 - 散列表.NET应用的研究(下集)
- 颜色不变性算法及应用总结
- 机器视觉学习总结(二)—— LDA,PCA算法与应用
- 一句话总结各个算法以及应用场景?
- 基于局部均方差相关信息的图像去噪及其在实时磨皮美容算法中的应用。
- 算法之路二:刘汝佳算法竞赛入门经典 信息解码 UVA213
- 基于局部均方差相关信息的图像去噪及其在实时磨皮美容算法中的应用。
- 在成都七中的寒假信息竞赛集训总结
- 基于DSP的FFT算法在无功补偿控制器上的应用
- 算法总结--数组相关】双指针法的常见应用