本文最后更新于 2025年3月22日 晚上
多项式和快速傅里叶变换
FFT算法
多项式用点值表示
首先设一个多项式 $A(x)$
$A(x)=\sum_{i=0}^{n-1}a_ix^i=a_0+a_1x+a_2x^2+…+a_{n-1}x^{n-1}$
按$A(x)$下标的奇偶性把 $A(x)$ 分成两半,右边再提一个$x$
${A(x)=(a_0+a_2x^2+…+a_{n-2}x^{n-2})+(a_1x+a_3x^3+…+a_{n-1}x^{n-1})}$
${=(a_0+a_2x^2+…+a_{n-2}x^{n-2})+x(a_1+a_3x^2+…+a_{n-1}x^{n-2})}$
两边好像非常相似,于是再设两个多项式 $A_1(x),A_2(x)$,令
$A_1(x)=a_0+a_2x+a_4x^2+…+a_{n-2}x^{{n \over 2}-1}$
$A_2(x)=a_1+a_3x+a_5x^2+…+a_{n-1}x^{{n \over 2}-1}$
比较明显得出
$A(x)=A_1(x^2)+xA_2(x^2)$
单位根:
复数$\omega$满足$\omega^n=1$,称$\omega$是n次单位根
$\omega_n^k=\cos{k\over n}2\pi +i\sin{k\over n} 2\pi$
再设 $k< {n\over 2}$ ,把 $\omega^k_n$ 作为 $x$ 代入 $A(x)$(接下来几步变换要多想想单位根的性质)
$A(\omega^k_n)$
$=A_1( (\omega^k_n)^2 )+\omega^k_nA_2( ( \omega^k_n)^2 )$
$=A_1(\omega_n^{2k})+\omega_n^kA_2(\omega_n^{2k})$
$=A_1( \omega^k_{n\over2} )+\omega^k_nA_2( \omega^k_{n \over 2} )$
那么对于${A(\omega^{k+{n\over2}}_n)}$的话,代入
${\omega^{k+{n \over 2}}_n}$
$A(\omega^{k+{n\over 2}}_n)=A_1(\omega^{2k+n}_n)+\omega^{k+{n\over 2}}_nA_2(\omega^{2k+n}_n)$
$=A_1(\omega^{2k}_n\omega^n_n)-\omega^k_nA_2(\omega^{2k}_n\omega^n_n)$
$=A_1( \omega_n^{2k} )-\omega_n^kA_2(\omega_n^{2k})$
$=A_1( \omega_{n \over 2}^k )-\omega^k_nA_2(\omega_{n \over 2}^k)$
如果已知 $A_1(\omega^k_{n\over 2})和 A_2(\omega^k_{n\over 2})$的值,我们就可以同时知道 $A(\omega^k_n)$和 $A(\omega^{k+{n\over2}}_n)$的值
现在我们就可以递归分治来搞 FFT 了
FFT逆变换
一个多项式在分治的过程中乘上单位根的共轭复数(即$\omega_n^k=\cos{k\over n}2\pi-i\sin{k\over n} 2\pi$),分治完的每一项除以 n 即为原多项式的每一项系数
注意,fft只能处理$2^k$的项,所以每次fft前,需要找到相乘后大于最高系数的最小的$2^k$作为limit
具体代码实现
递归版
时间复杂度为$nlogn$
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
| #include <bits/stdc++.h>
using namespace std; const int N=pow(2,18)+100; const double pi = acos(-1.0);
struct Complex{ double x, y; Complex(double xx = 0, double yy = 0){x = xx, y = yy;} }a[N]; Complex operator + (Complex a, Complex b){ return Complex(a.x + b.x, a.y + b.y);} Complex operator - (Complex a, Complex b){ return Complex(a.x - b.x, a.y - b.y);} Complex operator * (Complex a, Complex b){ return Complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);}
void fft(int limit, Complex* a, int type) { if (limit == 1) return;
int mid = limit >> 1; Complex* a1 = (Complex*)malloc(sizeof(Complex) * mid); Complex* a2 = (Complex*)malloc(sizeof(Complex) * mid);
for (int i = 0; i < limit; i += 2) { a1[i >> 1] = a[i]; a2[i >> 1] = a[i + 1]; }
fft(mid, a1, type); fft(mid, a2, type);
Complex Wn(cos(2.0 * pi / limit), type * sin(2.0 * pi / limit)); Complex w(1, 0); for (int i = 0; i < mid; i++, w = w * Wn) { Complex tmp = w * a2[i]; a[i] = a1[i] + tmp; a[i + mid] = a1[i] - tmp; }
free(a1); free(a2); }
|
非递归版(效率高,但是难理解)
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
| #include <bits/stdc++.h> using namespace std; const int N=100200; const double pi = acos(-1.0); struct Complex{ double x, y; Complex(double xx = 0, double yy = 0){x = xx, y = yy;} }a[N],b[N];
int rev[N*2];
Complex operator + (Complex a, Complex b){ return Complex(a.x + b.x, a.y + b.y);} Complex operator - (Complex a, Complex b){ return Complex(a.x - b.x, a.y - b.y);} Complex operator * (Complex a, Complex b){ return Complex(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);}
int limit=1;
void fft(Complex *a, int type){ Complex Wn, w; for(int i = 0; i < limit; i++){ if(i < rev[i]) swap(a[i], a[rev[i]]); } for(int mid = 1; mid < limit; mid <<= 1){ Wn = Complex{cos(pi / mid), type * sin(pi / mid)}; for(int R = (mid << 1), j = 0; j < limit; j += R){ w = Complex{1, 0}; for(int k = 0; k < mid; k++, w = w * Wn){ Complex x = a[j + k], y = a[j + mid + k] * w; a[j + k] = x + y; a[j + mid + k] = x - y; } } } }
|