快速傅里叶变换

一类用于以 O ( n log ⁡ n ) O(n\log n) O(nlogn)的时间复杂度将多项式的系数表示法变为点值表示法或反之的方法。
系数表示法: f ( x ) = ∑ i = 0 n a i x i f(x) = \sum_{i=0}^na_ix^i f(x)=i=0naixi
点值表示法:多项式函数 f ( x ) f(x) f(x) n n n个不同的点 ( x i , y i ) (x_i,y_i) (xi,yi),可以证明次数最小的 f ( x ) f(x) f(x)只有一个(次数为 n − 1 n-1 n1),那么可以用这 n n n个不同的点 ( x i , y i ) (x_i,y_i) (xi,yi)表示这个唯一的 f ( x ) f(x) f(x),注意 f ( x ) f(x) f(x)反过来并不是唯一对应,可以任意选这个函数上 n n n个不同的点来表示 f ( x ) f(x) f(x)

那么这个算法就是找不同的 n n n x i x_i xi并把这 n n n个点的 y i y_i yi算出来。
用单位根 ω n i \omega_n^i ωni作为 x i x_i xi
如果 n n n 2 2 2的倍数

对于 k < n 2 k<\frac n2 k<2n
A n ( w n k ) = ∑ i = 0 n 2 − 1 a 2 i w n 2 k i + ∑ i = 0 n 2 − 1 a 2 i + 1 w n 2 k i + k = ∑ i = 0 n 2 − 1 a 2 i w n 2 k i + w n k ∑ i = 0 n 2 − 1 a 2 i + 1 w n 2 k i \begin{aligned} A_n(w_n^k) =& \sum_{i=0}^{\frac n2-1}a_{2i}w^{2ki}_n + \sum_{i=0}^{\frac n2-1}a_{2i+1}w^{2ki+k}_n\\ =& \sum_{i=0}^{\frac n2-1}a_{2i}\large w^{ki}_{\frac n2} + \large w_{n}^{k}\sum_{i=0}^{\frac n2-1}a_{2i+1}\large w^{ki}_{\frac n2} \end{aligned} An(wnk)==i=02n1a2iwn2ki+i=02n1a2i+1wn2ki+ki=02n1a2iw2nki+wnki=02n1a2i+1w2nki

对于 k > = n 2 k>=\frac n2 k>=2n
A ( ω n k + n 2 ) = A 1 ( ω n 2 k + n ) + ω n k + n 2 A 2 ( ω n 2 k + n ) = A 1 ( ω n 2 k × ω n n ) + ω n k + n 2 A 2 ( ω n 2 k × ω n n ) = A 1 ( ω n 2 k ) − ω n k A 2 ( ω n 2 k ) \begin{aligned} A(\omega_n^{k + \frac{n}{2}}) &= A_1(\omega_n^{2k + n}) + \omega_n^{k + \frac{n}{2}}A_2(\omega_n^{2k + n}) \\ &= A_1(\omega_{\frac{n}{2}}^{k} \times \omega_n^n) + \omega_n^{k + \frac{n}{2}} A_2(\omega_{\frac{n}{2}}^{k} \times \omega_n^n) \\ &= A_1(\omega_{\frac{n}{2}}^{k}) - \omega_n^kA_2(\omega_{\frac{n}{2}}^{k}) \end{aligned} A(ωnk+2n)=A1(ωn2k+n)+ωnk+2nA2(ωn2k+n)=A1(ω2nk×ωnn)+ωnk+2nA2(ω2nk×ωnn)=A1(ω2nk)ωnkA2(ω2nk)

相当于将这个我们生成函数奇偶分拆然后利用 ω k n k i = ω n i \omega _{kn}^{ki}=\omega _{n}^i ωknki=ωni发现只需要知道在 ω n 2 i \large \omega _{\frac n2}^i ω2ni的两个 n 2 \frac n2 2n次多项式的点值表达式就可以 O ( n ) O(n) O(n) 合并答案。
T ( n ) = 2 T ( n / 2 ) + O ( n ) = O ( n log ⁡ n ) T(n) = 2T(n/2)+O(n) = O(n \log n) T(n)=2T(n/2)+O(n)=O(nlogn)
其实好像这个分治也没有怎么重复利用信息,怎么就快了呢?
其实是因为点值表达式的优秀性质导致合并十分快,说来也有趣,我们要求点值表达式,反而还要用点值表达式优化算法。。。。。。这类分治思想是利用所求答案本身的关系加快算法,没啥感觉。

然后发现奇偶分组好像合并时不一定我们需要的数字挨在一起,但是我们可以发现,一个数的二进制中从小到大第i位为0则在第i次奇偶分组中为偶数,反之为奇数,那么第i次分组中同一组的,较小i位都一样,那么就按由低位比到高位的顺序排序可以发现同一组的总挨在一起,那么就可以非递归计算了。

从点值表达式变回系数表达式可以看文后的博客。
需要 w n i n = w n i w_{n}^{in} = w_{n}^{i} wnin=wni
这样,IDFT 就相当于把 DFT 过程中的 ω n i ω^i_n ωni 换成 ω n − i ω^{-i}_n ωni,然后做一次 DFT,之后结果除以 n n n就可以了。

FFT合并
两个实函数 A ( x ) , B ( x ) A(x),B(x) A(x),B(x)的傅里叶变换可以用一次FFT完成。
这位dalao讲的十分清楚
具体就是 P ( x ) = A ( x ) + i B ( x ) 与 Q ( x ) = A ( x ) − i B ( x ) P(x)=A(x)+iB(x)与Q(x)=A(x)-iB(x) P(x)=A(x)+iB(x)Q(x)=A(x)iB(x)
他们的 D F T DFT DFT后的多项式:
D F T [ P ( w k ) ] = ∑ j ( a j + i b j ) w j k = ∑ j ( a j + i b j ) ( c o s ( x ) + i s i n ( x ) ) DFT[P(w^k)] = \sum_{j}(a_j+ib_j)w^{jk}=\sum_{j}(a_j+ib_j)(cos(x)+isin(x)) DFT[P(wk)]=j(aj+ibj)wjk=j(aj+ibj)(cos(x)+isin(x))
D F T [ Q ( w k ) ] = ∑ j ( a j − i b j ) w j k = ∑ j ( a j − i b j ) ( c o s ( x ) + i s i n ( x ) ) DFT[Q(w^k)] = \sum_{j}(a_j-ib_j)w^{jk}=\sum_{j}(a_j-ib_j)(cos(x)+isin(x)) DFT[Q(wk)]=j(ajibj)wjk=j(ajibj)(cos(x)+isin(x))
x x x是一定存在的,我们只是设一下。
D F T [ Q ( w k ) ] = ∑ j ( a j − i b j ) ( c o s ( − x ) + i s i n ( − x ) ) = ∑ j ( a j c o s ( − x ) − b j s i n ( − x ) ) − i ( a j s i n ( − x ) + b j c o s ( − x ) ) = c o n j ( D F T ( P ( w − k ) ) ) = c o n j ( D F T ( P ( w L − k ) ) ) DFT[Q(w^k)]=\sum_{j}(a_j-ib_j)(cos(-x)+isin(-x))=\\ \sum_{j}(a_jcos(-x)-b_jsin(-x))-i(a_jsin(-x)+b_jcos(-x)) = conj(DFT(P(w^{-k}))) = conj(DFT(P(w^{L-k}))) DFT[Q(wk)]=j(ajibj)(cos(x)+isin(x))=j(ajcos(x)bjsin(x))i(ajsin(x)+bjcos(x))=conj(DFT(P(wk)))=conj(DFT(P(wLk)))
所以我们可以只 D F T [ P ( x ) ] DFT[P(x)] DFT[P(x)]而得到 D F T [ Q ( x ) ] DFT[Q(x)] DFT[Q(x)],从而解出 D F T [ A ( x ) ] DFT[A(x)] DFT[A(x)] D F T [ B ( x ) ] DFT[B(x)] DFT[B(x)]
D F T DFT DFT具有结合律。。。

无优化版本

#include<bits/stdc++.h>
#define maxn 300005
#define Pi 3.1415926535897932384626433832795
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define db double
using namespace std;

struct cp{
	db r,i;cp(db r=0,db i=0):r(r),i(i){}
	cp operator +(const cp &B)const{ return cp(r+B.r,i+B.i); }
	cp operator -(const cp &B)const{ return cp(r-B.r,i-B.i); }
	cp operator *(const cp &B)const{ return cp(r*B.r-i*B.i,r*B.i+i*B.r);  }   
};

int Wl,lg[maxn],r[maxn];
cp W[maxn];
void init(int n){
	for(Wl=1;n>=2*Wl;Wl<<=1);
	rep(i,0,Wl<<1) W[i]=cp(cos(i*Pi/Wl),sin(i*Pi/Wl)),(i>1)&&(lg[i]=lg[i>>1]+1);
}
void FFT(cp *A,int n,int tp){
	rep(i,1,n-1) (i<(r[i]=(r[i>>1]>>1)|((i&1)<<(lg[n]-1))))&&(swap(A[i],A[r[i]]),0);cp t;
	for(int L=1,B=Wl;L<n;L<<=1,B>>=1) for(int s=0;s<n;s+=L<<1) for(int k=s,x=0;k<s+L;k++,x+=B) 
		t=(tp==1?W[x]:W[(Wl<<1)-x])*A[k+L],A[k+L]=A[k]-t,A[k]=A[k]+t;
	if(tp^1) rep(i,0,n-1) A[i].r/=n;
}
int n,m;cp A[maxn],B[maxn];
int main(){
	scanf("%d%d",&n,&m);
	double x;
	rep(i,0,n) scanf("%lf",&A[i].r);
	rep(i,0,m) scanf("%lf",&B[i].r);
	init(n+m);
	FFT(A,Wl<<1,1),FFT(B,Wl<<1,1);
	rep(i,0,(Wl<<1)-1) A[i]=A[i]*B[i];
	FFT(A,Wl<<1,-1);
	rep(i,0,n+m) printf("%d%c",(int)round(A[i].r)," \n"[i==n+m]);
}

合并FFT版本:

#include<bits/stdc++.h>
#define maxn 300005
#define Pi 3.1415926535897932384626433832795
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define db double
#define Ct const
using namespace std;

struct cp{
	db r,i;cp(db r=0,db i=0):r(r),i(i){}
	cp operator +(Ct cp &B)Ct{ return cp(r+B.r,i+B.i); }
	cp operator -(Ct cp &B)Ct{ return cp(r-B.r,i-B.i); }
	cp operator *(Ct cp &B)Ct{ return cp(r*B.r-i*B.i,r*B.i+i*B.r);  }   
	cp conj()Ct{ return cp(r,-i); }
};

int Wl,lg[maxn],r[maxn];
cp W[maxn];
void init(int n){
	for(Wl=1;n>=2*Wl;Wl<<=1);
	rep(i,0,Wl<<1) W[i]=cp(cos(i*Pi/Wl),sin(i*Pi/Wl)),(i>1)&&(lg[i]=lg[i>>1]+1);
}
void FFT(cp *A,int n,int tp){
	rep(i,1,n-1) (i<(r[i]=(r[i>>1]>>1)|((i&1)<<(lg[n]-1))))&&(swap(A[i],A[r[i]]),0);cp t;
	for(int L=1,B=Wl;L<n;L<<=1,B>>=1) for(int s=0;s<n;s+=L<<1) for(int k=s,x=0;k<s+L;k++,x+=B) 
		t=(tp==1?W[x]:W[(Wl<<1)-x])*A[k+L],A[k+L]=A[k]-t,A[k]=A[k]+t;
	if(tp^1) rep(i,0,n-1) A[i].r/=n;
}
int n,m;cp A[maxn],B[maxn];
int main(){
	scanf("%d%d",&n,&m);
	double x;
	rep(i,0,n) scanf("%lf",&A[i].r);
	rep(i,0,m) scanf("%lf",&A[i].i);
	init(n+m);
	FFT(A,Wl<<1,1);
	rep(i,0,(Wl<<1)-1) B[i]=(A[i]+A[((Wl<<1)-i)&((Wl<<1)-1)].conj())*(A[i]-A[((Wl<<1)-i)&((Wl<<1)-1)].conj())*cp(0,-0.25);
	FFT(B,Wl<<1,-1);
	rep(i,0,n+m) printf("%d%c",(int)round(B[i].r)," \n"[i==n+m]);
}

上大佬链接:http://www.cnblogs.com/zhouzhendong/p/8831887.html
http://blog.miskcoo.com/tag/fft

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值