初探多项式
快速傅里叶变换
前置知识
复数
1.前言
快速傅里叶变换是用来加速多项式乘法的,对于两个多项式 A 和B ,朴素计算时的n2是满足不了需求的,快速傅里叶变换可以在O(logn)的时间内计算A×B 。
先看一下单位元的几个性质,在接下来的算法中有很大的用途。
ωnk=en2πik(1,1)
ωdndk=ωnk(1,2)
ωnk=a+biωn−k=a−bi(1,3)
ωnk+2n=−ωnk(1,4)
以上变换均可由欧拉公式 eiθ=cosθ+i⋅sinθ 推得。
2.离散傅里叶变换
离散傅里叶变换(DFT) 主要是利用分治思想,
首先将多项式
A(x)=i=0∑naixi(2,1)
其系数进行奇偶性分类,得到,
A0(x)=a0+a2x1+a4x2+⋯A1(x)=a1+a3x1+a5x2+⋯(2,2)
所以我们可以表示为 :
A(x)=A0(x2)+x⋅A1(x2)(2,3)
将 ωnk 与 ωnk+2n代入得:
{A(ωnk)A(ωnk+2n)==A(ωn2k)+ωnkA1(ωn2k)A(ωn2k)−ωnkA1(ωn2k)(2,4)
同时我们可以发现两个式子只有常数一样,递归计算即可。
时间复杂度O(nlogn) 。
在这里我们将系数变成了点值
3. 离散傅里叶逆变换
离散傅里叶逆变换IDFT 可以将点值快速转化为系数,从而得出结果多项式。
需要用到单位根的一个性质:
n1i=0∑n−1ωnx∗i=[xmodn=0]
证明 :
由于 ωnx∗i=ωnx∗(i−1)∗ωnx
所以ωnx∗i 为等比数列
∴n1i=0∑n−1ωnx∗i=⎩⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎧n1i=0∑n−11i=nn=1n1⋅1−ωnx1−ωnn∗x=n1⋅1−ωnx1−1x=0xmodn=0xmodn=0(3,1)
证明:
设c=a∗bcinci=j=0∑iaj⋅bi−j=p=0∑q=0∑ap⋅bq[(p+q)modn=0]=p=0∑q=0∑ap⋅bqj=0∑ωn(p+q−i)j=j=0∑ωn(−i)j(p=0∑ωnpjap)(q=0∑ωnqjbq)设fa(j)=i=0∑ωnijai,fa−1(j)=i=0∑ωn(−i)jainci=j=0∑ωn(−i)jfa(j)fb(j)=j=0∑ωn(−i)jfc(j)=ffc−1(i)(3,2)
因为 fa 就是 a 在 DFT 后的结果,所以fa−1就是 对应的IDFT,最后除以对应长度n,即为所求。
4.代码实现
在写FFT 时可以将DFT和IDFT合在一起这样会更简便,同时使用位逆序变换,更加简便快捷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include<bits/stdc++.h> using namespace std; const int maxn=1e7+10; const double pi=acos(-1.0); struct Complex{ double a,b; Complex(double x=0,double y=0):a(x),b(y) {} friend Complex operator + (Complex x,Complex y){ return Complex(x.a+y.a,x.b+y.b); } friend Complex operator - (Complex x,Complex y){ return Complex(x.a-y.a,x.b-y.b); } friend Complex operator * (Complex x,Complex y){ return Complex(x.a*y.a-x.b*y.b,x.b*y.a+y.b*x.a); } }; int n,m,recover[maxn]; Complex F[maxn],G[maxn],H[maxn]; void FFT(Complex *a,int len,int type) { for(int i=0;i<len;i++) if(i<recover[i])swap(a[i],a[recover[i]]);
for(int k=1;k<len;k<<=1) { Complex x(cos(pi/k),type*sin(pi/k)); for(int i=0;i<len;i+=(k<<1)) { Complex w(1,0); for(int j=0;j<k;j++) { Complex y=a[i+j]; Complex z=w*a[i+j+k]; a[i+j]=y+z;a[i+j+k]=y-z; w=w*x; } } } if(type==-1) for(int i=0;i<len;i++)a[i].a/=len; } int main() { scanf("%d%d",&n,&m); for(int i=0;i<=n;i++) { double x; scanf("%lf",&x); F[i].a=x; } for(int i=0;i<=m;i++) { double x; scanf("%lf",&x); G[i].a=x; } int len=1,cnt=0; while(len<=(n+m))len<<=1,cnt++; for(int i=0;i<len;i++) recover[i]=(recover[i>>1]>>1)|((i&1)<<(cnt-1)); FFT(F,len,1);FFT(G,len,1); for(int i=0;i<=len;i++)H[i]=F[i]*G[i]; FFT(H,len,-1); for(int i=0;i<=n+m;i++) printf("%d ",(int)(H[i].a+0.5)); return 0; }
|