快速傅里叶变换(FFT)

前置知识

多项式

一个多项式表示为 A ( x ) = a 0 + a 1 x + a 2 x 2 + . . . a n x n A(x) = a_0 + a_1 x + a_2 x^2 + ... a_n x^n A(x)=a0+a1x+a2x2+...anxn

系数表示法: A ( x ) = ∑ i = 0 n a i x i A(x) = \sum _{i=0}^{n} a_i x^i A(x)=i=0naixi

点值表示法:用 n n n 个不同的点 ( x , A ( x ) ) (x, A(x)) (x,A(x)) 唯一表示多项式 A ( x ) A(x) A(x)

复数

前置知识:向量、三角函数

定义

i 2 = − 1 i^2 = -1 i2=1 ,则一个复数表示为 a + b i ( a , b ∈ R ) a + bi (a,b\in R) a+bi(a,bR)

其中 b i bi bi 为虚部, a a a 为实部

复平面上,从 ( 0 , 0 ) (0,0) (0,0) ( a , b ) (a,b) (a,b) 的向量表示 a + b i a+bi a+bi

棱长: ∣ Z ∣ = a 2 + b 2 |Z| = \sqrt{a^2+b^2} Z=a2+b2

幅角:为从 x x x 正半轴逆时针旋转的角度

运算

加法: ( a + b i ) + ( c + d i ) = ( a + c ) + ( b + d ) i (a+bi)+(c+di) = (a+c)+(b+d)i (a+bi)+(c+di)=(a+c)+(b+d)i

乘法几何意义:复数相乘,模长相乘,幅角相加

乘法代数意义: ( a + b i ) ( c + d i ) = ( a c − b d ) + ( a d + b c ) i (a+bi)(c+di) = (ac-bd)+(ad+bc)i (a+bi)(c+di)=(acbd)+(ad+bc)i

共轭复数

复数 Z = a + b i Z = a+bi Z=a+bi 的共轭复数 Z ˉ \bar{Z} Zˉ a − b i a-bi abi

∣ Z ∣ = ∣ Z ˉ ∣          Z ⋅ Z ˉ = a 2 + b 2 |Z|=|\bar{Z}|\ \ \ \ \ \ \ \ Z\cdot \bar{Z} = a^2+b^2 Z=Zˉ        ZZˉ=a2+b2

复数除法

z 1 = a 1 + b 1 i , z 2 = a 2 + b 2 i z_1=a_1+b_1i,z_2=a_2+b_2i z1=a1+b1i,z2=a2+b2i

z 0 = z 1 z 2 z_0=\frac{z_1}{z_2} z0=z2z1

上下同乘 z 2 z_2 z2 的共轭复数 z 2 ˉ \bar{z_2} z2ˉ

z 1 z 2 ˉ a 2 2 + b 2 2 \frac{z_1 \bar{z_2}}{a_2^2 + b_2^2} a22+b22z1z2ˉ

单位根 ω

为在复平面上以原点为圆心作半径为 1 1 1 的圆。

ω n k \omega_n^k ωnk 表示为将圆分成 n n n 份,从 x x x 正半轴逆时针取 k k k 份下的复数。

  • ω n k = ω n k − 1 ⋅ ω n 1 \omega_n^k = \omega _{n}^{k-1} \cdot \omega _n^1 ωnk=ωnk1ωn1

  • ω n 0 = ω n n = 1 \omega_n^0 = \omega_n^n=1 ωn0=ωnn=1

  • ω 2 n 2 k = ω n k \omega_{2n}^{2k} = \omega_n^k ω2n2k=ωnk

  • ω n k + n 2 = − ω n k \omega_n^{k+\frac{n}{2}} = - \omega_n^k ωnk+2n=ωnk


算法流程

1101696-20180211213536607-322408834.png


快速傅里叶变换

快速傅里叶变换( Fast   Fourier   Transform   ,   FFT \texttt{Fast Fourier Transform , FFT} Fast Fourier Transform , FFT)

使用 ω n k ( 0 ≤ k < n ) \omega_n^k (0\leq k < n) ωnk(0k<n) n n n 个不同的数来当作点值表示法中的 n n n x x x

已知

A ( x ) = ∑ i = 0 n − 1 a i ∗ x i A(x) = \sum_{i=0}^{n-1} a_i * x^i A(x)=i=0n1aixi

A 1 ( x ) = a 0 + a 2 ⋅ x + a 4 ⋅ x 2 + ⋯ + a n − 2 x n 2 − 1 A_1(x) = a_0 + a_2 \cdot x + a_4 \cdot x^2 + \cdots + a_{n-2} x^{\frac{n}{2}-1} A1(x)=a0+a2x+a4x2++an2x2n1

A 2 ( x ) = a 1 + a 3 ⋅ x + a 5 ⋅ x 2 + ⋯ + a n − 1 x n 2 − 1 A_2(x) = a_1 + a_3 \cdot x + a_5 \cdot x^2 + \cdots + a_{n-1} x^{\frac{n}{2}-1} A2(x)=a1+a3x+a5x2++an1x2n1

A ( x ) = A 1 ( x 2 ) + x A 2 ( x 2 ) A(x) = A_1(x^2) + xA_2(x^2) A(x)=A1(x2)+xA2(x2)

ω n k ( 0 ≤ k < n ) \omega_n^k (0\leq k < n) ωnk(0k<n) 代入 A ( x ) A(x) A(x)

A ( ω n k ) = A 1 ( ω n 2 k ) + ω n k A 2 ( ω n 2 k ) A(\omega_n^k) = A_1(\omega_{\frac{n}{2}}^{k}) + \omega_n^k A_2(\omega_{\frac{n}{2}}^{k}) A(ωnk)=A1(ω2nk)+ωnkA2(ω2nk)

A ( ω n n 2 + k ) = A 1 ( ω n 2 k ) − ω n k A 2 ( ω n 2 k ) A(\omega_n^{\frac{n}{2}+k}) = A_1(\omega_{\frac{n}{2}}^{k}) - \omega_n^k A_2(\omega_{\frac{n}{2}}^{k}) A(ωn2n+k)=A1(ω2nk)ωnkA2(ω2nk)

两个式子 A 1 ( ) A_1() A1() A 2 ( ) A_2() A2() 相同。

当计算 [ 0 , n 2 ) [0,\frac{n}{2}) [0,2n) 时, [ n 2 , n ) [\frac{n}{2},n) [2n,n) 可以直接计算。

每次计算量减小一半,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

快速傅里叶逆变换

快速傅里叶逆变换 ( Inverse   Fast   Fourier   Transform   ,   IFFT ) (\texttt{Inverse Fast Fourier Transform , IFFT}) (Inverse Fast Fourier Transform , IFFT)

C ( x ) = c 0 + c 1 x + c 2 x 2 + ⋯ + c n − 1 x n − 1 C(x) = c_0 + c_1 x + c_2 x^2 + \cdots + c_{n - 1} x^{n-1} C(x)=c0+c1x+c2x2++cn1xn1

b i = ω n i , y i = C ( b i ) b_i = \omega_n^i, y_i = C(b_i) bi=ωni,yi=C(bi)

C ( x ) C(x) C(x) 经过 n n n 个点, ( b 0 , y 0 ) … ( b n − 1 , y n − 1 ) (b_0, y_0) \dots (b_{n - 1}, y_{n - 1}) (b0,y0)(bn1,yn1)


引理1

S ( x ) = 1 + x + x 2 + ⋯ + x n − 1 S(x) = 1 + x + x^2 + \cdots + x^{n-1} S(x)=1+x+x2++xn1

因为

S ( ω n k ) = ω n 0 + ω n k + ω n 2 k + ⋯ + ω n ( n − 1 ) k S(\omega_n^k) = \omega_n^0 + \omega_n^k + \omega_n^{2k} + \cdots + \omega_n^{(n-1)k} S(ωnk)=ωn0+ωnk+ωn2k++ωn(n1)k

ω n k S ( ω n k ) = ω n k + ω n 2 k + ⋯ + ω n ( n − 1 ) k + ω n n k \omega_n^k S(\omega_n^k) = \omega_n^k + \omega_n^{2k} + \cdots + \omega_n^{(n-1)k} + \omega_n^{nk} ωnkS(ωnk)=ωnk+ωn2k++ωn(n1)k+ωnnk

所以

k ≠ 0 k\not= 0 k=0

S ( ω n k ) = 0 1 − ω n k = 0 S(\omega_n^k) = \frac{0}{1 - \omega_n^k} = 0 S(ωnk)=1ωnk0=0

k = 0 k=0 k=0

S ( ω n k ) = n S(\omega_n^k) = n S(ωnk)=n


引理2

n c k = ∑ i = 0 n − 1 y i ( w n − k ) i nc_k = \sum_{i=0}^{n-1} y_i (w_n^{-k})^i nck=i=0n1yi(wnk)i

证明

∑ i = 0 n − 1 y i ( w n − k ) i \sum_{i=0}^{n-1} y_i (w_n^{-k})^i i=0n1yi(wnk)i

= ∑ i = 0 n − 1 ( ∑ j = 0 n − 1 c j ( w n i ) j ) ( w n − k ) i =\sum_{i=0}^{n-1} (\sum_{j=0}^{n-1} c_j {(w_n^i)}^{j}) (w_n^{-k})^i =i=0n1(j=0n1cj(wni)j)(wnk)i

= ∑ i = 0 n − 1 ( ∑ j = 0 n − 1 c j ( w n j ) i ) ( w n − k ) i =\sum_{i=0}^{n-1} (\sum_{j=0}^{n-1} c_j {(w_n^j)}^{i}) (w_n^{-k})^i =i=0n1(j=0n1cj(wnj)i)(wnk)i

= ∑ i = 0 n − 1 ( ∑ j = 0 n − 1 c j ( w n j − k ) i ) =\sum_{i=0}^{n-1} (\sum_{j=0}^{n-1} c_j {(w_n^{j - k})}^{i}) =i=0n1(j=0n1cj(wnjk)i)

= ∑ j = 0 n − 1 c j ∑ i = 0 n − 1 ( w n j − k ) i =\sum_{j=0}^{n-1} c_j \sum_{i=0}^{n-1} (w_n^{j-k})^{i} =j=0n1cji=0n1(wnjk)i

= ∑ j = 0 n − 1 c j S ( w n j − k ) =\sum_{j=0}^{n-1} c_j S(w_n^{j-k}) =j=0n1cjS(wnjk)

= n c k =nc_k =nck

证毕


D ( x ) = ∑ i = 0 n − 1 y i x i D(x) = \sum_{i=0}^{n-1} y_i x^i D(x)=i=0n1yixi

c k = D ( w n − k ) n c_k = \frac{D(w_n^{-k})}{n} ck=nD(wnk)

所以再做一次快速傅里叶变换即可


实现 实现 实现

#include <bits/stdc++.h>
using namespace std;

const int N = 400010;
const double PI = acos(-1);
int n, m, tot;
int rev[N];

struct Complex
{
    double x, y;
    Complex operator+ (const Complex& t) const
    {
        return {x + t.x, y + t.y};
    }
    Complex operator- (const Complex& t) const
    {
        return {x - t.x, y - t.y};
    }
    Complex operator* (const Complex& t) const
    {
        return {x * t.x - y * t.y, x * t.y + y * t.x};
    }
} a[N], b[N];

void fft(Complex a[], int inv)
{
    for (int i = 0; i < tot; i ++ )
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
    for (int mid = 1; mid < tot; mid <<= 1)
    {
        Complex w1 = {cos(PI / mid), inv * sin(PI / mid)};
        for (int i = 0; i < tot; i += mid * 2)
        {
            Complex wk = {1, 0};
            for (int j = 0; j < mid; j ++ , wk = wk * w1)
            {
                Complex x = a[i + j], y = wk * a[i + j + mid];
                a[i + j] = x + y, a[i + j + mid] = x - y;
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i <= n; i ++ ) scanf("%lf", &a[i].x);
    for (int i = 0; i <= m; i ++ ) scanf("%lf", &b[i].x);
    
    int bit = 0;
    while ((1 << bit) <= n + m) bit ++ ;
    tot = 1 << bit;
    
    for (int i = 0; i < tot; i ++ )
        rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (bit - 1));
    
    fft(a, 1), fft(b, 1);
    for (int i = 0; i < tot; i ++ ) a[i] = a[i] * b[i];
    
    fft(a, -1);
    for (int i = 0; i <= n + m; i ++ )
        printf("%d ", (int)(a[i].x / tot + 0.5));
    return 0;
}
  • 58
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值