FFT学习笔记

FFT(快速傅立叶变换)

向量基本知识

  1. 向量加法

在这里插入图片描述

AB=r1eiθ1=r1(cos⁡θ1+isin⁡θ1)AC=r2eiθ2=r2(cos⁡θ2+isin⁡θ2)AB+AC=r1eiθ1+r2eiθ2=r1(cos⁡θ1+isin⁡θ1)+r2(cos⁡θ2+isin⁡θ2)AB+AC=r1eiθ1+r2eiθ2=(r1cos⁡θ1+r2cos⁡θ2)+i(r1sin⁡θ1+r2sin⁡θ2) AB=r_1e^{i\theta_1}=r_1(\cos{\theta_1}+i\sin{\theta_1})\\ AC=r_2e^{i\theta_2}=r_2(\cos{\theta_2}+i\sin{\theta_2})\\ \\ AB+AC=r_1e^{i\theta_1}+r_2e^{i\theta_2}=r_1(\cos{\theta_1}+i\sin{\theta_1})+r_2(\cos{\theta_2}+i\sin{\theta_2})\\ AB+AC=r_1e^{i\theta_1}+r_2e^{i\theta_2}=(r_1\cos{\theta_1}+r_2\cos{\theta_2})+i(r_1\sin{\theta_1}+r_2\sin{\theta_2})\\ AB=r1eiθ1=r1(cosθ1+isinθ1)AC=r2eiθ2=r2(cosθ2+isinθ2)AB+AC=r1eiθ1+r2eiθ2=r1(cosθ1+isinθ1)+r2(cosθ2+isinθ2)AB+AC=r1eiθ1+r2eiθ2=(r1cosθ1+r2cosθ2)+i(r1sinθ1+r2sinθ2)

  1. 向量乘法

    向量乘法可以简单看成向量的旋转和放缩

    在这里插入图片描述

    如图所示最后AB×AC=ADAB\times AC=ADAB×AC=AD,iii可以想象成逆时针旋转90∘90^{\circ}90,所以总结出来 r1eiθ1×r2eiθ2=r1r2ei(θ1+θ2)r_1e^{i\theta_1}\times r_2e^{i\theta_2}=r_1r_2e^{i(\theta_1+\theta_2)}r1eiθ1×r2eiθ2=r1r2ei(θ1+θ2)

复数的单位根ωn\omega_nωn

multi

复数的单位根是指满足方程ωn=1\omega^n=1ωn=1的复数 ω\omegaω,其中 nnn是一个正整数。这些根称为 nnn 次单位根,它们在复平面上均匀分布在单位圆上,形成一个正nnn 边形的顶点。

满足的性质:
  1. ωn=1\omega^n=1ωn=1
  2. ωn+k=ωk\omega^{n+k}=\omega^{k}ωn+k=ωk
  3. ωnk=ω2n2k\omega_n^k=\omega_{2n}^{2k}ωnk=ω2n2k
  4. ωnn−k=ωnk‾\omega_n^{n-k} = \overline{\omega_n^k}ωnnk=ωnk 共轭复数
  5. ωnk+n2=−ωnk\omega_n^{k+\frac{n}{2}}=-\omega_{n}^{k}ωnk+2n=ωnk ,其实就是逆时针转半圆
  6. ωnk+ωnn−k=Re(ωnk)\omega_n^k+\omega_n^{n-k}=Re(\omega_n^k)ωnk+ωnnk=Re(ωnk)
  7. ωnk−ωnn−k=Im(ωnk)\omega_n^k-\omega_n^{n-k}=Im(\omega_n^k)ωnkωnnk=Im(ωnk)

FFT 整体思路

FFT其实就是通过分治策略利用单位根的对称性和周期性加速多项式乘法

  • 多项式插值转换:将多项式 (f(x)=∑k=0n−1akxkf(x) = \sum_{k=0}^{n-1} a_k x^kf(x)=k=0n1akxk) 在单位根 (ωn0,ωn1,…,ωnn−1\omega_n^0, \omega_n^1, \dots, \omega_n^{n-1}ωn0,ωn1,,ωnn1) 处求值DFT。

  • 分治步骤

    • 偶数项和奇数项分别递归计算:
      f(x)=feven(x2)+x⋅fodd(x2) f(x) = f_{\text{even}}(x^2) + x \cdot f_{\text{odd}}(x^2) f(x)=feven(x2)+xfodd(x2)
    • 利用 (ωnk+n/2=−ωnk\omega_n^{k + n/2} = -\omega_n^kωnk+n/2=ωnk) 合并结果(蝴蝶变换):
      {f(ωnk)=feven(ωn/2k)+ωnk⋅fodd(ωn/2k)f(ωnk+n/2)=feven(ωn/2k)−ωnk⋅fodd(ωn/2k) \begin{cases} f(\omega_n^k) = f_{\text{even}}(\omega_{n/2}^k) + \omega_n^k \cdot f_{\text{odd}}(\omega_{n/2}^k) \\ f(\omega_n^{k + n/2}) = f_{\text{even}}(\omega_{n/2}^k) - \omega_n^k \cdot f_{\text{odd}}(\omega_{n/2}^k) \end{cases} {f(ωnk)=feven(ωn/2k)+ωnkfodd(ωn/2k)f(ωnk+n/2)=feven(ωn/2k)ωnkfodd(ωn/2k)

多项式乘法
f(x)∗g(x)=a0b0+∑i=01aib1−ix1+⋯+∑i=0n+m−2aibn+m−2j,这显然是个O((n+m)2)的算法 f(x)*g(x)=a_0b_0+\sum_{i=0}^1a_ib_{1-i}x^1+\cdots+\sum_{i=0}^{n+m-2}a_ib_{n+m-2j} , 这显然是个O((n+m)^2)的算法 f(x)g(x)=a0b0+i=01aib1ix1++i=0n+m2aibn+m2j,这显然是个O((n+m)2)的算法

DFT和IDFT

我们先来看看转化成频域之后会发生什么,将多项式 (f(x)=∑k=0n−1akxkf(x) = \sum_{k=0}^{n-1} a_k x^kf(x)=k=0n1akxk) 在单位根 ( ωn0,ωn1,…,ωnn−1\omega_n^0, \omega_n^1, \dots, \omega_n^{n-1}ωn0,ωn1,,ωnn1) 处求值
f(x)=∑k=0n−1akxk设置dk=∑i=0n−1aiωnki,那么ak=∑i=0n−1dkwn−ki f(x) = \sum_{k=0}^{n-1} a_k x^k \\ 设置d_k=\sum_{i=0}^{n-1}a_i\omega_{n}^{ki},那么a_k=\sum_{i=0}^{n-1}d_k{w_n^{-ki}} f(x)=k=0n1akxk设置dk=i=0n1aiωnki,那么ak=i=0n1dkwnki

这实际上是线性变换,映射到新的基上,dkd_kdk其实是基于nnn个基{ωn0,⋯ ,wn(n−1)k}\{\omega_n^0,\cdots,w_n^{(n-1)k}\}{ωn0,,wn(n1)k}表示的一个向量{a0,⋯ ,an−1}\{a_0,\cdots,a_{n-1}\}{a0,,an1}

那么一共有nnn 种基
W=(ωn0ωn0⋯ωn0ωn0ωn1×1⋯ωn(n−1)×1⋮⋮⋱⋮ωn0ωn1×(n−1)⋯ωn(n−1)(n−1)) W = \begin{pmatrix} \omega_n^0 & \omega_n^0 & \cdots & \omega_n^0 \\ \omega_n^0 & \omega_n^{1 \times 1} & \cdots & \omega_n^{(n-1)\times 1} \\ \vdots & \vdots & \ddots & \vdots \\ \omega_n^0 & \omega_n^{1 \times (n-1)} & \cdots & \omega_n^{(n-1)(n-1)} \end{pmatrix} W=ωn0ωn0ωn0ωn0ωn1×1ωn1×(n1)ωn0ωn(n1)×1ωn(n1)(n1)

我们要证明的是:

对于 k≠lk\ne lk=l,这两个向量是正交的:
uk⃗=[1,ωnk,ωn2k,⋯ ,ωn(n−1)k]ul⃗=[1,ωnl,ωn2l,⋯ ,ωn(n−1)l] \vec{u_k} =\left[1, \omega_n^k, \omega_n^{2k}, \cdots, \omega_n^{(n-1)k}\right] \\ \vec{u_l} = \left[1, \omega_n^l, \omega_n^{2l}, \cdots, \omega_n^{(n-1)l}\right] uk=[1,ωnk,ωn2k,,ωn(n1)k]ul=[1,ωnl,ωn2l,,ωn(n1)l]
它们的内积为 0:
⟨uk⃗,ul⃗⟩=∑m=0n−1ωnkm⋅ωnlm‾=∑m=0n−1ωnkm⋅ωn−lm=∑m=0n−1ωn(k−l)m=ωnn−1ωn−1=0 \begin{align*} \langle\vec{u_k},\vec{u_l}\rangle & =\sum_{m=0}^{n-1} \omega_n^{km} \cdot \overline{\omega_n^{lm}}\\ &= \sum_{m=0}^{n-1} \omega_n^{km} \cdot \omega_n^{-lm}\\ &= \sum_{m=0}^{n-1} \omega_n^{(k-l)m}\\ &=\frac{\omega_n^n-1}{\omega_n-1}=0 \end{align*} uk,ul=m=0n1ωnkmωnlm=m=0n1ωnkmωnlm=m=0n1ωn(kl)m=ωn1ωnn1=0
k=lk=lk=l 时,显然
⟨uk⃗,uk⃗⟩=∑m=0n−1ωn0=n \langle\vec{u_k},\vec{u_k}\rangle= \sum_{m=0}^{n-1} \omega_n^{0}=n uk,uk=m=0n1ωn0=n

神奇,因此
WWH=nIn WW^{H}=nI_n WWH=nIn

因此DFT就是构造了这样一组正交基
(d0d1⋮dn−1)=(ωn0⋅0ωn0⋅1⋯ωn0⋅(n−1)ωn1⋅0ωn1⋅1⋯ωn1⋅(n−1)⋮⋮⋱⋮ωn(n−1)⋅0ωn(n−1)⋅1⋯ωn(n−1)⋅(n−1))(a0a1⋮an−1) \begin{pmatrix} d_0 \\ d_1\\ \vdots\\ d_{n-1} \end{pmatrix}= \begin{pmatrix} \omega_n^{0\cdot0} & \omega_n^{0\cdot1} & \cdots & \omega_n^{0\cdot(n-1)} \\ \omega_n^{1\cdot0} & \omega_n^{1\cdot1} & \cdots & \omega_n^{1\cdot(n-1)} \\ \vdots & \vdots & \ddots & \vdots \\ \omega_n^{(n-1)\cdot0} & \omega_n^{(n-1)\cdot1} & \cdots & \omega_n^{(n-1)\cdot(n-1)} \end{pmatrix} \begin{pmatrix} a_0 \\ a_1\\ \vdots \\ a_{n-1} \end{pmatrix} d0d1dn1=ωn00ωn10ωn(n1)0ωn01ωn11ωn(n1)1ωn0(n1)ωn1(n1)ωn(n1)(n1)a0a1an1
ωn=e2πin\omega_n=e^{\frac{2πi}{n}}ωn=en2πi,是 DFT 的单位根,上式就是 DFT 的矩阵乘法形式,d⃗=Wa⃗\vec{d} = W \vec{a}d=Wa,因此na⃗=WHd⃗n\vec{a} = W^H \vec{d}na=WHd

WHW^HWH求解a⃗\vec{a}a的过程就是DFT的逆变换IDFT

DFT本质上是将输入信号在复平面上进行周期性旋转。不同的旋转角度代表不同的频率成分,这些频率成分合成后就形成了原始信号在频域中的表示。

在IDFT中,频域信号不仅要按相反的方向旋转回去,还需要通过 1n\frac{1}{n}n1 进行缩放,这样才能正确地恢复原始信号。每个频率成分在时间域的表示受到频域中其幅度的影响。

题外话

有没有发现这个WWW与范德蒙矩阵很相似。

范德蒙矩阵的一般形式为:
V=(1x0x02⋯x0n−11x1x12⋯x1n−1⋮⋮⋮⋱⋮1xn−1xn−12⋯xn−1n−1) V = \begin{pmatrix} 1 & x_0 & x_0^2 & \cdots & x_0^{n-1} \\ 1 & x_1 & x_1^2 & \cdots & x_1^{n-1} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & x_{n-1} & x_{n-1}^2 & \cdots & x_{n-1}^{n-1} \end{pmatrix} V=111x0x1xn1x02x12xn12x0n1x1n1xn1n1
范德蒙矩阵可以用来做多项式插值。

多项式乘法

原始时域卷积表达式

多项式乘法对应的是离散卷积
f(x)⋅g(x)=∑k=0n+m−2(∑j=0kajbk−j)xk f(x) \cdot g(x) = \sum_{k=0}^{n+m-2} \left( \sum_{j=0}^{k} a_j b_{k-j} \right) x^k f(x)g(x)=k=0n+m2(j=0kajbkj)xk
也就是:
ck=∑j=0kajbk−j c_k = \sum_{j=0}^{k} a_j b_{k-j} ck=j=0kajbkj

转化到频域

DFT(c)k=∑i=0n−1ciωnki=∑i=0n−1∑j=0iajbi−jwnik=∑i=0n−1∑j=0i(ajwnjk)(bi−jwn(i−j)k)=DFT(a)k∗DFT(b)k \begin{align*} DFT(c)_k &= \sum_{i=0}^{n-1}c_i\omega_{n}^{ki}\\ &= \sum_{i=0}^{n-1} \sum_{j=0}^{i} a_j b_{i-j} w_n^{ik}\\ &= \sum_{i=0}^{n-1} \sum_{j=0}^{i} (a_jw_n^{jk})( b_{i-j} w_n^{(i-j)k})\\ &=DFT(a)_k*DFT(b)_k \end{align*} DFT(c)k=i=0n1ciωnki=i=0n1j=0iajbijwnik=i=0n1j=0i(ajwnjk)(bijwn(ij)k)=DFT(a)kDFT(b)k

因此多项式卷积的频域就是多项式频域点积的积。

实际上就是将全局纠缠的时域操作解耦为局部独立的频域操作

multi

FFT

说了那么多,最后该说FFT了,FFT其实就是利用分治进行DFT操作。

FFT其实就是通过分治策略利用单位根的对称性和周期性加速多项式乘法

  • 多项式插值转换:将多项式 (f(x)=∑k=0n−1akxkf(x) = \sum_{k=0}^{n-1} a_k x^kf(x)=k=0n1akxk) 在单位根 (ωn0,ωn1,…,ωnn−1\omega_n^0, \omega_n^1, \dots, \omega_n^{n-1}ωn0,ωn1,,ωnn1) 处求值DFT。

  • 分治步骤

    • 偶数项和奇数项分别递归计算:
      f(x)=feven(x2)+x⋅fodd(x2) f(x) = f_{\text{even}}(x^2) + x \cdot f_{\text{odd}}(x^2) f(x)=feven(x2)+xfodd(x2)

    • 利用 (ωnk+n/2=−ωnk\omega_n^{k + n/2} = -\omega_n^kωnk+n/2=ωnk) 合并结果(蝴蝶变换):
      {bk=f(ωnk)=feven(ωn/2k)+ωnk⋅fodd(ωn/2k)bk+n2=f(ωnk+n/2)=feven(ωn/2k)−ωnk⋅fodd(ωn/2k) \begin{cases} b_k=f(\omega_n^k) = f_{\text{even}}(\omega_{n/2}^k) + \omega_n^k \cdot f_{\text{odd}}(\omega_{n/2}^k) \\ b_{k+\frac{n}{2}}=f(\omega_n^{k + n/2}) = f_{\text{even}}(\omega_{n/2}^k) - \omega_n^k \cdot f_{\text{odd}}(\omega_{n/2}^k) \end{cases} {bk=f(ωnk)=feven(ωn/2k)+ωnkfodd(ωn/2k)bk+2n=f(ωnk+n/2)=feven(ωn/2k)ωnkfodd(ωn/2k)

奇偶分组

由于不断要奇偶分组,我们需要把每个数字移动到该有的位置,我们可以进行一个模拟,假设数组的长度为2p2^p2p

位置iii的数应该在哪:

最终的位置pos=0pos=0pos=0,很明显可以进行一个递归,如果iii是奇数,p=p−1,pos=pos+2p,i=i/2p=p-1,pos=pos+2^p,i=i/2p=p1pos=pos+2pi=i/2 这明显是对二进制位进行一个反转。

即假设数组长度为888, 那么位置4=0100,pos=00104=0100,pos=00104=0100,pos=0010,位置3=0011,pos=11003=0011,pos=11003=0011,pos=1100,同理,pospospos位置上的数来自于对pospospos二进制的反转。

可以设计一个递推对于一个数字iii的反转来自于最低位和其他高位即

reverse[i]=reverse[i>>1]>>1+(i&1)<<(p-1)
分治

在进行奇偶分组后,利用变换计算bk+n2b_{k+\frac{n}{2}}bk+2n 可以利用分治解决了,为了更清楚的看分治,把式子改写下
若l<k<mid,利用对称性和周期性Bl,r[k]=fl,mid(ωmid−lk)+ωnk⋅fmid,r(ωr−midk+r−mid)Bl,r[k]=Bl,mid[k]+ωnk⋅Bmid,r[k+r−mid]Bl,r[k+r−mid]=Bl,mid[k]−ωnk⋅Bmid,r[k+r−mid] 若l<k<mid,利用对称性和周期性\\ \begin{align*} B_{l,r}[k]&= f_{\text{l,mid}}(\omega_{mid-l}^k) + \omega_n^k \cdot f_{\text{mid,r}}(\omega_{r-mid}^{k+r-mid}) \\ B_{l,r}[k]&= B_{l,mid}[k] + \omega_n^k \cdot B_{mid,r}[k+r-mid] \\ B_{l,r}[k+r-mid]&= B_{l,mid}[k] - \omega_n^k \cdot B_{mid,r}[k+r-mid] \\ \end{align*} l<k<mid,利用对称性和周期性Bl,r[k]Bl,r[k]Bl,r[k+rmid]=fl,mid(ωmidlk)+ωnkfmid,r(ωrmidk+rmid)=Bl,mid[k]+ωnkBmid,r[k+rmid]=Bl,mid[k]ωnkBmid,r[k+rmid]

代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
const double PI=acos(-1.0);
struct Complex{
    double x,y;
    Complex():x(0),y(0){};
    Complex(double _x,double _y):x(_x),y(_y){};
    Complex operator-(const Complex &b){return Complex(x-b.x,y-b.y); }
    Complex operator+(const Complex &b){return Complex(x+b.x,y+b.y);}
    Complex operator*(const Complex &b){return Complex(x*b.x-y*b.y,x*b.y+y*b.x);}
    Complex operator/(const double &b){return Complex(x/b,y/b);}
};
vector<int> re,omega;
int len=1;
void init(int n){
    while(len<n) len<<=1;re.resize(len,0);
    for(int i=1,t=(len>>1);i<len;i++) re[i]=((re[i>>1]>>1)|((i&1)?t:0));
}
void move(vector<Complex> &f){for(int i=0;i<len;i++)if(i<re[i]) swap(f[i],f[re[i]]);}
void FFT(vector<Complex> &f,double flag){
    move(f);
    double num=flag?PI:-PI;
    for(int l=2,preL=1;l<=len;preL=l,l<<=1,num/=2){
        Complex wn(cos(num),sin(num));
        for(int j=0;j<len;j+=l){
            Complex w(1,0);
            for(int k=j;k<j+preL;k++){
                Complex u=f[k],v=f[k+preL]*w;
                f[k]=u+v,f[k+preL]=u-v;
                w=w*wn;
            }
        }
    }
    if(flag) for(int i=0;i<len;i++) f[i]=f[i]/len;
}
vector<Complex>a,b;
int main(){
    int n,m;cin>>n>>m;n++;m++;
    init(n+m);
    a.resize(len,Complex(0,0));
    b.resize(len,Complex(0,0));
    for(int i=0;i<n;i++) cin>>a[i].x;
    for(int i=0;i<m;i++) cin>>b[i].x;
    FFT(a,0);
    FFT(b,0);
    for(int i=0;i<len;i++) a[i]=a[i]*b[i];
    FFT(a,1);
    for(int i=0;i<n+m-1;i++) cout<<(int)(a[i].x+0.5)<<" ";cout<<endl;
}

NTT(快速数论变换)-代更

  • 上面我们说利用复数单位根来进行加速,复数的单位根是指满足方程$ \omega^n=1$的复数 ω\omegaω,那么NTT就是利用原根来代替复数单位根。

原根

原根的定义

定义:设正整数n≥2n\ge 2n2,整数gggnnn互质(即gcd(g,n)=1gcd(g,n)=1gcd(g,n)=1)。如果gggnnn的阶等于ϕ(n)\phi(n)ϕ(n),则称ggg是模nnn的一个原根

其中:

  • ϕ(n)\phi(n)ϕ(n)是欧拉函数,表示小于nnn且与nnn互质的正整数的个数
  • gggnnn的阶是最小的正整数ddd,使得gd≡ 1(modn)g^d \equiv \ 1\pmod ngd 1(modn)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值