一类用于以
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
n−1),那么可以用这
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=0∑2n−1a2iwn2ki+i=0∑2n−1a2i+1wn2ki+ki=0∑2n−1a2iw2nki+wnki=0∑2n−1a2i+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
ωn−i,然后做一次 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(aj−ibj)wjk=∑j(aj−ibj)(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(aj−ibj)(cos(−x)+isin(−x))=∑j(ajcos(−x)−bjsin(−x))−i(ajsin(−x)+bjcos(−x))=conj(DFT(P(w−k)))=conj(DFT(P(wL−k)))
所以我们可以只
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