假定你拥有高中数学基础。
复数
复数的三角形式可以表示为 a=r(cosθ+isinθ)。
那么假定 a=r1(cosα+isinα),b=r2(cosβ+isinβ),有 ab=r1r2(cosαcosβ−sinαsinβ+icosαsinβ+icosβsinα)=r1r2(cos(α+β)+isin(α+β)),也就是说,模长相乘,辐角相加,即棣莫弗定理。
由复数的三角形式,我们画一个单位圆,以 (1,0) 为起点,在圆上等间距画出 n 个点,这样得到的 n 个复数便是 n 次单位根,转到的第一个记作 ωn,有 ωnn=1。
欧拉公式:eix=cosx+isinx。
快速傅里叶变换
设 F(x)=i=0∑n−1ai×xi,那么这是多项式的系数表示法。
由拉格朗日插值可知,我们搞 n 个不重复的点也可以确定一个 n−1 次多项式,这是多项式的点值表示法。
FFT
我们现在假定(n 为偶数):
F1(x)=a0+a2×x+a4×x2+⋯an−2×xn/2−1F2(x)=a1+a3×x+a5×x2+⋯an−1×xn/2−1
那么 F(x)=F1(x2)+xF2(x2)。并且:
F(ωnk)=F1(ωn2k)+ωnkF2(ωn2k)=F1(ωn/2k)+ωnkF2(ωn/2k)F(ωnk+n/2)=F1(ωn2k)+ωnk+n/2F2(ωn2k)=F1(ωn2k)−ωnkF2(ωn2k)
通过这样的方式,我们可以以 O(nlogn) 的时间复杂度在已知一个多项式的系数表示时求得它的点值表示,在求出两个多项式的点值表示后,我们就可以计算 H(x)=F(x)G(x),进而得到两多项式乘积的点值表示。
IFFT
我们现在要将点值表示转化为系数表示,我们现在得到了 n 个点 (ωni,yi=F(ωni))。
略。
迭代实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void FFT(int L, Complex *f, int type) { static int r[N]; for (int i = 1; i < L; ++i) { r[i] = (r[i >> 1] >> 1) + (i & 1 ? L >> 1 : 0); if (i < r[i]) swap(f[i], f[r[i]]); } for (int d = 1; d < L; d <<= 1) { Complex wd(cos(PI / d), sin(PI / d)); wd.b *= type; static Complex w[N]; w[0] = Complex(1, 0); for (int j = 1; j < d; ++j) w[j] = w[j - 1] * wd; for (int i = 0; i < L; i += d << 1) for (int j = 0; j < d; ++j) { Complex x = f[i | j], y = w[j] * f[i | j | d]; f[i | j] = x + y, f[i | j | d] = x - y; } } if (type == -1) for (int i = 0; i < L; ++i) f[i] = f[i] / L; }
|
三次变两次优化
我们考虑 (f+gi)2=f2−g2+2fgi,因此有 三次变两次。