当前位置:   article > 正文

快速傅立叶变换(FFT)_idftkw

idftkw

FFT

作用:快速求两个多项式的乘积/卷积

前置知识

多项式乘法(点表示法):用任意n + 1个不同点,均可确定为一个n次多项式。

好处:如果有两个多项式A,B:
A ( x ) = a 0 + a 1 x 1 + a 2 x 2 + a 3 x 3 + . . . . B ( x ) = b 0 + b 1 x 1 + b 2 x 2 + b 3 x 3 + . . . . 多 项 式 A 点 表 示 法 为 ( x 1 , A ( x 1 ) )   ,   ( x 2 , A ( x 2 )   ,   ( x 3 , A ( x 3 ) ) ) . . . 多 项 式 B 点 表 示 法 为 ( x 1 , B ( x 1 ) )   ,   ( x 2 , B ( x 2 )   ,   ( x 3 , B ( x 3 ) ) ) . . . 那 么 C 可 以 表 示 为 A ( x i ) ∗ B ( x i ) ; 多 项 式 C 点 表 示 法 为 ( x 1 , C ( x 1 ) )   ,   ( x 2 , C ( x 2 )   ,   ( x 3 , C ( x 3 ) ) ) . . . ( o ( n ) ) A(x) = a_0 + {a_1}x^1+ {a_2}x^2+ {a_3}x^3+....\\ B(x) = b_0 + {b_1}x^1+ {b_2}x^2+ {b_3}x^3+....\\ 多项式A点表示法为(x_1 , A(x_1))~,~(x_2 , A(x_2)~,~(x_3 , A(x_3)))...\\ 多项式B点表示法为(x_1 , B(x_1))~,~(x_2 , B(x_2)~,~(x_3 , B(x_3)))...\\ 那么C可以表示为A(x_i)*B(x_i);\\ 多项式C点表示法为(x_1 , C(x_1))~,~(x_2 , C(x_2)~,~(x_3 , C(x_3)))...(o(n)) A(x)=a0+a1x1+a2x2+a3x3+....B(x)=b0+b1x1+b2x2+b3x3+....A(x1,A(x1)) , (x2,A(x2) , (x3,A(x3)))...B(x1,B(x1)) , (x2,B(x2) , (x3,B(x3)))...CA(xi)B(xi);C(x1,C(x1)) , (x2,C(x2) , (x3,C(x3)))...o(n)

复数(Complex)

定义:1.形如 a + b i a+bi a+bi​​,其中a为实部,b为虚部。

​ 2.模长为: a 2 + b 2 \sqrt{a^2 + b ^ 2} a2+b2

​ 3.幅角:设逆时针方向为正方向,从x正半轴到向量的转角

运算法则:(满足结合律,交换律,分配律)

​ 1.加法满足平行四边形法则

​ 2.乘法:几何意义:幅角相加,模长相乘

​ 代数意义: ( a c − b d ) + ( a d + b c ) i (ac-bd) + (ad + bc)i (acbd)+(ad+bc)i

单位根

定义: w n k w_n^k wnk​​为n次单位根

性质:

  • w n j ≠ w n i ( ∀ i ≠ j ) w_n^j \ne w_n^i(\forall_{i \ne j}) wnj=wni(i=j)
  • w n k = c o s 2 k π n + s i n 2 k π n w_n^k = cos\frac{2k\pi}{n} + sin\frac{2k\pi}{n} wnk=cosn2kπ+sinn2kπ
  • w n 0 = w n n = 1 w_n^0 = w_n^n = 1 wn0=wnn=1
  • w 2 n 2 k = w n k w_{2n}^{2k} = w_n^k w2n2k=wnk​​(折半引理)
  • w n k + n 2 = − w n k w_n^{k + \frac{n}{2}} = -w_n^k wnk+2n=wnk​(消去引理)

离散傅立叶变换(Discrete Fourier Transform , DFT)

将系数矩阵转换为点表达式

设一个长度为n的数列 d [ i ] d[i] d[i],这个数列的第k项为 A ( x ) A(x) A(x)在n次单位根第k次幂处的点值。

得:
d k = ∑ i = 0 n − 1 a i × w n i d_k = \sum_{i = 0}^{n - 1}a_i \times w_n^i dk=i=0n1ai×wni
但是这样仍然是 o ( n 2 ) o(n ^ 2) o(n2)​​,由单位根的性质,我们可得快速傅立叶变换(Fast Fourier transform , FFT)

快速傅立叶变换(Fast Fourier transform , FFT)

对于 A ( x ) = a 0 + a 1 x 1 + a 2 x 2 + a 3 x 3 + . . . . + a n − 1 x n − 1 A(x) = a_0 + {a_1}x^1+ {a_2}x^2+ {a_3}x^3+....+{a_{n - 1}x^{n -1}} A(x)=a0+a1x1+a2x2+a3x3+....+an1xn1

按照奇偶分组:
A ( x ) = a 0 + a 1 x 1 + a 2 x 2 + a 3 x 3 + . . . . + a n − 1 x n − 1 A ( x ) = ( a 0 + a 2 x 2 + . . . + a n − 2 x n − 2 ) + x ( a 1 + a 3 x 2 + . . . + a n − 1 x n − 2 ) 以 x 2 代 x , 不 妨 令 : A 1 ( x ) = ( a 0 + a 2 x + a 4 x 2 + . . . + a n − 2 x n − 2 2 ) A 2 ( x ) = ( a 1 + a 3 x + a 5 x 2 + . . . + a n − 1 x n − 2 2 ) 显 然 : A ( x ) = A 1 ( x 2 ) + x A 2 ( x 2 ) A(x) = a_0 + {a_1}x^1+ {a_2}x^2+ {a_3}x^3+....+{a_{n - 1}x^{n -1}} \\ A(x) = (a_0 + {a_2}x^2+...+a_{n - 2}x^{n - 2}) + x(a_1 + {a_3}x^2+...+{a_{n - 1}x^{n - 2}}) \\ \\ 以x^2代x,不妨令:\\ A_1(x) = (a_0 + a_2x+a_4x^2 + ... + a_{n - 2}x^{\frac{n - 2}{2}})\\ A_2(x) = (a_1 + a_3x+a_5x^2 + ... + a_{n - 1}x^{\frac{n - 2}{2}})\\ 显然:A(x) = A_1(x^2) + xA_2(x^2)\\ A(x)=a0+a1x1+a2x2+a3x3+....+an1xn1A(x)=(a0+a2x2+...+an2xn2)+x(a1+a3x2+...+an1xn2)x2x,A1(x)=(a0+a2x+a4x2+...+an2x2n2)A2(x)=(a1+a3x+a5x2+...+an1x2n2):A(x)=A1(x2)+xA2(x2)
根据K的大小分类讨论: n

  1. k ∈ [ 0   ,   n 2 − 1 ] k \in [0 ~,~ \frac{n}{2}-1] k[0 , 2n1]

A ( w n k ) = A 1 ( w n 2 k ) + w n k A 2 ( w n 2 k ) 由 折 半 引 理 得 : A ( w n k ) = A 1 ( w n 2 k ) + w n k A 2 ( w n 2 k ) A(w_n^k) = A_1(w_n^{2k}) + {w_n^k}A_2(w_{n}^{2k}) \\ 由折半引理得: A(w_n^k) = A_1(w_{\frac{n}{2}}^{k}) + {w_n^k}A_2(w_{\frac{n}{2}}^{k})\\ A(wnk)=A1(wn2k)+wnkA2(wn2k)A(wnk)=A1(w2nk)+wnkA2(w2nk)

2. k + n 2 ∈ [ n 2 , n − 1 ] k + \frac{n}{2} \in[\frac{n}{2} , n - 1] k+2n[2n,n1]
A ( w n k + n 2 ) = A 1 ( w n 2 k + n ) + w n k + n 2 A 2 ( w n 2 k + n ) 由 消 去 引 理 得 : A ( w n k + n 2 ) = A 1 ( w n 2 k ) − w n k A 2 ( w n 2 k ) A(w_n^{k + \frac{n}{2}} ) = A_1(w_n^{2k + n}) + {w_n^{k + \frac{n}{2}}}A_2(w_{n}^{2k + n})\\ 由消去引理得: A(w_n^{k + \frac{n}{2}} ) = A_1(w_{\frac{n}{2}}^{k}) - {w_n^{k}}A_2(w_{\frac{n}{2}}^{k})\\ A(wnk+2n)=A1(wn2k+n)+wnk+2nA2(wn2k+n)A(wnk+2n)=A1(w2nk)wnkA2(w2nk)
综上得: 若已知 w n 2 i w_{\frac{n}{2}} ^ i w2ni 那么可以快速求得 A ( x ) A(x) A(x)点表达式。

A 1 ( x ) , A 2 ( x ) A_1(x) , A_2(x) A1(x),A2(x),为 A ( x ) A(x) A(x)的一半规模,可以递归求解问题,那么时间复杂度为 o ( n l o g n ) o(nlogn) o(nlogn)

离散傅立叶逆变换(Inverse Discrete Fourier Transform , IDFT)

既然有了 o ( n l o g n ) o(nlogn) o(nlogn)的将系数表达式转化为点表达式的方法,那么我们自然也想用一个理想的方式将点表达式转化为系数表达式。

已知多项式 A ( x ) = ∑ i = 0 n − 1 a i x i A(x) = \sum_{i=0}^{n-1}a_ix^i A(x)=i=0n1aixi,和其点表达式 d k = ∑ i = 0 n − 1 a i × w n i k d_k = \sum_{i = 0}^{n - 1}a_i\times w_n^{ik} dk=i=0n1ai×wnik,求 a i {a_i} ai​.

结论:
c k = ∑ i = 0 n − 1 d i ( w n − k ) i c_k = \sum_{i = 0} ^ {n - 1} d_i(w_n^{-k}) ^ i ck=i=0n1di(wnk)i
证明:
构 造 一 多 项 式 F ( x ) = d 0 + d 1 x + d 2 x 2 + . . . + d n − 1 x n − 1 设 C k 为 x = w n − k 下 的 点 表 示 , 即 c k = ∑ i = 0 n − 1 d i ( w n − k ) i 即 多 项 式 B ( x ) = d 0 , d 1 x , d 2 x 2 , . . . , d n − 1 x n − 1 在 w n 0 , w n − 1 , . . . . , w n − 1 − ( n − 1 ) 的 点 表 达 式 ( c 0 , c 1 , c 2 , c 3 , . . . , c n − 1 ) 满 足 : c k = ∑ i = 0 n − 1 d i ( w n − k ) i ⇌ d i = ∑ j = 0 n − 1 a j × ( w n i ) j c k = ∑ i = 0 n − 1 d i ( w n − k ) i = ∑ i = 0 n − 1 ( ∑ j = 0 n − 1 a j ( w n i ) j ) ( w n − k ) i = ∑ i = 0 n − 1 ( ∑ j = 0 n − 1 a j ( w n j ) i ) ( w n − k ) i = ∑ i = 0 n − 1 ( ∑ j = 0 n − 1 a j ( w n j ) i ( w n − k ) i ) = ∑ i = 0 n − 1 ∑ j = 0 n − 1 a j ( w n j − k ) i = ∑ j = 0 n − 1 a j ( ∑ i = 0 n − 1 ( w n j − k ) i ) 设 S ( x ) = ∑ i = 0 n − 1 x i 代 入 x = w n k , 得 : S ( w n k ) = 1 + ( w n k ) + ( w n k ) 2 + . . . + ( w n k ) n − 1 分 情 况 讨 论 得 : 当 k ≠ 0 时 , 两 边 同 时 × w n k , 得 w n k S ( w n k ) = w n k + ( w n k ) 2 + ( w n k ) 3 + . . . + ( w n k ) n w n k s ( w n k ) − s ( w n k ) = ( w n k ) n − 1 s ( w n k ) = ( w n k ) n − 1 w n k − 1 s ( w n k ) = ( w n n ) k − 1 w n k − 1 s ( w n k ) = 0 w n k − 1 s ( w n k ) = 0 当 k = 0 时 , s ( w n k ) = 1 + 1 + . . . + 1 = n 由 于 c k = ∑ j = 0 n − 1 a j ( ∑ i = 0 n − 1 ( w n j − k ) i ) 当 且 仅 当 j = k 时 , c k = n a k a k = c k n c k = ∑ i = 0 n − 1 d i w n − k i a k = 1 n ∑ i = 0 n − 1 d i w n − k i 构造一多项式F(x) = d_0 + d_1x + d_2x^2 + ... + d_{n-1}x^{n-1} \\ 设C_k为x = w_{n}^{-k}下的点表示,即c_k = \sum_{i = 0} ^ {n - 1} d_i(w_n^{-k}) ^ i\\ 即多项式B(x) = d_0,d_1x,d_2x_2,...,d_{n - 1}x^{n - 1}在w_n^0,w_n^{-1},....,w_{n - 1} ^ {-(n - 1)}的点表达式\\(c_0,c_1,c_2,c_3,...,c_{n - 1})满足:\\ c_k = \sum_{i = 0} ^ {n - 1} d_i(w_n^{-k}) ^ i\rightleftharpoons{d_i} = \sum_{j = 0}^{n - 1}a_j \times (w_n^i) ^ j\\ c_k = \sum_{i = 0} ^ {n - 1}d_i(w_n^{-k}) ^ i\\ =\sum_{i = 0}^{n - 1}(\sum_{j = 0}^{n - 1}a_j(w_n^i)^j)(w_n^{-k})^ i\\ =\sum_{i = 0}^{n - 1}(\sum_{j = 0}^{n - 1}a_j(w_n^j)^i)(w_n^{-k})^ i\\ =\sum_{i = 0}^{n - 1}(\sum_{j = 0}^{n - 1}a_j(w_n^j)^i(w_n^{-k})^ i)\\ =\sum_{i = 0}^{n - 1}\sum_{j = 0}^{n - 1}a_j(w_n^{j - k})^i\\ =\sum_{j = 0}^{n - 1}a_j(\sum_{i = 0}^{n - 1}(w_n^{j - k})^i)\\ 设S(x) = \sum_{i = 0}^{n - 1}x_i\\ 代入x = w_n^k,得: S(w_n^k) = 1 + (w_n^k) + (w_n^k)^2+...+(w_n^k)^{n-1}\\ 分情况讨论得:\\ 当k\ne 0时,两边同时\times w_n^k,得\\ w_n^kS(w_n^k) = w_n^k + (w_n^k)^2 + (w_n^k)^3+...+(w_n^k)^{n}\\ w_n^ks(w_n^k) - s(w_n^k) = (w_n^k)^n-1\\ s(w_n^k) = \frac{(w_n^k)^n - 1}{w_n^k-1}\\ s(w_n^k) = \frac{(w_n^n)^k - 1}{w_n^k-1}\\ s(w_n^k) = \frac{0}{w_n^k - 1}\\ s(w_n^k) = 0\\ 当k = 0时,s(w_n^k) = 1 + 1 + ... + 1 = n\\ 由于c_k=\sum_{j = 0} ^ {n - 1}a_j(\sum_{i = 0}^{n - 1}(w_n^{j - k})^i)\\ 当且仅当j = k 时,c_k = na_k\\ a_k = \frac{c_k}{n}\\ c_k = \sum_{i = 0}^{n - 1}d_iw_n^{-ki}\\ a_k = \frac{1}{n}\sum_{i = 0}^{n - 1}d_iw_n^{-ki}\\ F(x)=d0+d1x+d2x2+...+dn1xn1Ckx=wnk,ck=i=0n1di(wnk)iB(x)=d0,d1x,d2x2,...,dn1xn1wn0,wn1,....,wn1(n1)(c0,c1,c2,c3,...,cn1):ck=i=0n1di(wnk)idi=j=0n1aj×(wni)jck=i=0n1di(wnk)i=i=0n1(j=0n1aj(wni)j)(wnk)i=i=0n1(j=0n1aj(wnj)i)(wnk)i=i=0n1(j=0n1aj(wnj)i(wnk)i)=i=0n1j=0n1aj(wnjk)i=j=0n1aj(i=0n1(wnjk)i)S(x)=i=0n1xix=wnkS(wnk)=1+(wnk)+(wnk)2+...+(wnk)n1k=0×wnk,wnkS(wnk)=wnk+(wnk)2+(wnk)3+...+(wnk)nwnks(wnk)s(wnk)=(wnk)n1s(wnk)=wnk1(wnk)n1s(wnk)=wnk1(wnn)k1s(wnk)=wnk10s(wnk)=0k=0s(wnk)=1+1+...+1=nck=j=0n1aj(i=0n1(wnjk)i)j=k,ck=nakak=nckck=i=0n1diwnkiak=n1i=0n1diwnki
这个是DFT的逆变换,称为IDFT,可以通过这个式子求出多项式的系数表示法.

快速傅立叶逆变换(Inverse Fast Fourier transform,IFFT)

我们考虑的问题是如何快速求解: A ( x ) = ∑ i = 0 n − 1 b i x i A(x)=\sum_{i = 0}^{n - 1}b_ix^i A(x)=i=0n1bixi w n − k i 于 [ 0 , n ) w_n^{-ki}于[0 , n) wnki[0,n)​处的点值。

从图上,我们得到:
w n − k = w n k + n w_n^{-k} = w_n^{k + n} wnk=wnk+n

只要先算出 A ( X ) A(X) A(X) w n w_n wn​在各个幂次的弧度,然后反过来计算就可以
a k = 1 n ∑ i = 0 n A ( w n k ) ( w n − k ) − 1 a_k=\frac{1}{n}\sum_{i = 0} ^ n A(w_n^{k})(w_n^{-k})^{-1} ak=n1i=0nA(wnk)(wnk)1
至此通过FFT,IFFT我们可以 o ( n l o g n ) o(nlogn) o(nlogn)的时间复杂度由系数表达式变成点表达式,由点表达式变成系数表达式。

蝴蝶变换

由于递归写法常数太大,我们采用神奇的蝴蝶变换,就可以让他十分高效。
第 一 层 : a 0   ,   a 1   ,   a 2   ,   a 3   ,   a 4   ,   a 5   ,   a 6   ,   a 7 第 二 层 : a 0   ,   a 2   ,   a 4 ,   ,   a 6   ,   a 1   ,   a 3   ,   a 5   ,   a 7 第 三 层 : a 0   ,   a 4   ,   a 2   ,   a 6   ,   a 1   ,   a 5   ,   a 3   ,   a 7 第一层:a_0~,~a_1~,~a_2~,~a_3~,~a_4~,~a_5~,~a_6~,~a_7\\ 第二层:a_0~,~a_2~,~a_4,~,~a_6~,~a_1~,~a_3~,~a_5~,~a_7\\ 第三层:a_0~,~a_4~,~a_2~,~a_6~,~a_1~,~a_5~,~a_3~,~a_7\\ a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7a0 , a2 , a4, , a6 , a1 , a3 , a5 , a7a0 , a4 , a2 , a6 , a1 , a5 , a3 , a7
可以发现:
原 来 : 0   1   2   3   4   5   6   7 变 换 : 0   4   2   6   1   5   3   7 实 际 上 就 是 二 进 制 位 翻 转 原来:0~1~2~3~4~5~6~7\\ 变换:0~4~2~6~1~5~3~7\\ 实际上就是二进制位翻转 0 1 2 3 4 5 6 70 4 2 6 1 5 3 7

递 推 式 : r e v [ i ] = ( r e v [ i > > 1 ] > > 1 )   ∣   ( i   &   1 ) < < ( b i t   −   1 ) 递推式:rev[i] = (rev[i >> 1] >> 1)~ |~(i ~\&~1) << (bit~ - ~1) rev[i]=(rev[i>>1]>>1)  (i & 1)<<(bit  1)

OK,到这就完全结束了。

例题

例题:多项式乘法

y总代码(注释版):

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

const int N = 300010;
const double PI = acos(-1);

int n, m;
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];//A,B,两个多项式
//rev存的是逆转后的序列,也就是递归后的位置。
//bit为最高次幂。
//tot为总共多少项,补充到2的整数次项。
int rev[N], bit, tot;

void fft(Complex a[], int inv)
{
  	//先将序列按照rev排序
    for (int i = 0; i < tot; i ++ )
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
  	//枚举每个分块的长度,开始迭代,一开始是1.
    for (int mid = 1; mid < tot; mid <<= 1)
    {
      	//w1是单位根,乘inv是为了方便倒着作fft
        auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)});
      	//从第i块开始枚举,每次加两块
        for (int i = 0; i < tot; i += mid * 2)
        {
          	//从w0开始枚举
            auto wk = Complex({1, 0});
          	//每次都++,算出x,y,存下来
            for (int j = 0; j < mid; j ++, wk = wk * w1)
            {
                auto 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);
    while ((1 << bit) < n + m + 1) 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

参考:acwing,
【学习笔记】超简单的快速傅里叶变换(FFT)(含全套证明)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/395948
推荐阅读
相关标签
  

闽ICP备14008679号