FFT学习笔记

FFT可用于解决一些卷积问题。
一般问题形式如下:
C=AB
C[i]=ij=0A[i]B[ij]

若把A,B看成两个次数为n多项式
A(x)=ni=0a[i]xi,B(x)=ni=0b[i]xi
原问题等于两个多项式相乘,C的次数等于2n-1

点值

一个次数界为n的多项式A的点值表达为n个点值对所组成的集合。形如
{(x0,y0),(x1,y1)…(xn−1,yn−1)}
其中yi=A(xi),x各不相同。

插值

插值运算为点值运算的逆运算,如果给定一个n个点值对的点值表达,我们可以确定唯一一个次数界为n的多项式。

对于多项式乘法,
我们可以求出A的点值表达{(x0,a0),(x1,a1)…(xn−1,an−1)}和B的点值表达{(x0,b0),(x1,b1)…(xn−1,bn−1)}
则C的点值表达为{(x0,a0*b0),(x1,a1*b1)…(xn−1,an−1*bn-1)}
再通过插值运算,则可求出C的每一项的系数。

直接暴力,复杂度是O(n^2)的,FFT的关键就在于x的选取。

复数

形如a+bi( i=1 )
如果建立平面直角坐标系,a作为x坐标,b作为y坐标,我们可以用一个点对(a,b)来表示一个复数,也相当于一个平面向量。
用三角函数简单推导,可以发现,复数乘法得到的新向量的模长等于原向量的模长之积,与x轴的夹角等于原向量的夹角之和。

n次单位复数根

n次单位复数根是满足 wn=1 的复数w
w1 ~ wn 把平面均匀的分成n份
w 为主n次单位复数根,则w=(cos(2π/n),sin( 2π/n ))
wi=wi1w=(cos(2iπ/n ),sin( 2iπ/n))
为了区分其他次单位复数根,我们添一个下标来表示( win )

消去引理

wdkdn=wkn
wdkdn=(cos(2dkπ/(dn)),sin(2dkπ/(dn)))=(cos(2kπ/n),sin(2kπ/n))=wkn

折半引理

2n次单位复数根的平方的集合,等于n次单位复数根的集合。
易知对于任意一个<=n的k,存在唯一一个<=n的k’满足 wkn=wkn
(wk2n)2=(wk2n)2=wkn

求和引理

k!=0,n>0时 n1i=0wkin=0
相当于等比数列求和, n1i=0wkin=(wknnw0n)/(wk1)=0

FFT

为了方便,我们只考虑次数n=2^k的多项式乘法
如何求出单个数x的函数值A(x)?我们定义两个多项式
A0(x)=a0+a2x+a4x2an2xn/2
A1(x)=a1+a3x+a5x2an1xn/2
A(x)=A0(x2)+xA1(x2)
原问题:求A(x)在n次单位复数根上的函数值。
转化成:求A0(x)和A1(x)在n/2次单位复数根上的值。
于是可以递归地对n/2的多项式A0(x)与A1(x)在n/2个n/2次单位复数根进行求值。

插值运算
如果把点值运算写成矩阵方程的形式,可以得到表达式 Y=VnA
那么插值运算相当于求A, A=V1nY

结论, V1n[j,k]=wjkn/n .
证明: [VnV1n][j,j']=n1i=0w(j'j)in/n ,如果j’=j,那后面为1,否则根据求和引理,为0。

实现优化

如何把空间复杂度降为O(n)呢?
不好讲,详见代码吧…

struct z{
    double x,y;
    z(double _x=0,double _y=0){x=_x,y=_y;}
}c[maxn],d[maxn],g[maxn],a[maxn],b[maxn],an[maxn];
z operator *(z a,z b){return z(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
z operator +(z a,z b){return z(a.x+b.x,a.y+b.y);}
z operator -(z a,z b){return z(a.x-b.x,a.y-b.y);}
void dft(ar *a,int sig){
    int i,m=1,k,half,j;
    fo(i,0,l-1) g[f[i]]=a[i];
    while (m<l){
        half=m,m+=m;
        fo(k,0,half-1){
            ar w=ar(cos(pi*sig*k/half),sin(pi*sig*k/half));
            for(i=k;i<l;i+=m){
                j=i+half;
                ar u=g[i],v=w*g[j];
                g[i]=u+v,g[j]=u-v;
            }
        }
    }fo(i,0,l-1)a[i]=g[i];
    if (sig<0) fo(i,0,l-1) a[i].x/=l;
}
void fft(){
    fo(i,0,l-1) a[i]=b[i]=z(0,0);
    fo(i,0,n) a[i].x=a1[i],b[i].x=b1[i];
    dft(a,1),dft(b,1);
    fo(i,0,l-1) an[i]=a[i]*b[i];
    dft(an,-1);
    fo(i,0,l-1) ans[i]=(ll)(an[i].x+eps);
}
void chu(){
    l=1,t=0;
    while (l<=n*2) l+=l,t++;
    fo(i,0,l-1){
        int x=i,y=t-1;f[i]=0;
        while (x) f[i]+=(x&1)<<y,y--,x>>=1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值