for k=0 to n/2-1
t=w*y0[k]
y[k]=y0[k]+t
y[k+(n/2)]=y0[k]-t
w=w*wn
for(int i = 0, j = 0; i < n; i++)
{
if(j > i) swap(a[i], a[j]);
int k = n;
while(j & (k >>= 1)) j &= ~k;
j |= k;
}
最后,得到了n个点值后,如何恢复出系数呢?答案是利用逆矩阵来求系数,因为这n个点值写成矩阵的计算形式如下:
可以看出,这个矩阵的系数矩阵V是一个范德蒙德矩阵,它的逆一定存在,而且可以证明,每个元素的逆等于下式:
这样,就可以顺利恢复出最终的系数了。
const double PI = acos(-1.0);
typedef complex CD;
// Cooley-Tukey的FFT算法,迭代实现。inverse = false时计算逆FFT
inline void FFT(vector &a, bool inverse)
{
int n = a.size();
for (int i = 0, j = 0; i < n; i++) // 原地快速bit reversal
{
if (j > i) swap(a[i], a[j]);
int k = n;
while (j & (k >>= 1)) j &= ~k;
j |= k;
}
double pi = inverse ? -PI : PI;
for (int step = 1; step < n; step <<= 1)
{ // 把每相邻两个“step点DFT”通过一系列蝴蝶操作合并为一个“2*step点DFT”
double alpha = pi / step;
// 为求高效,我们并不是依次执行各个完整的DFT合并,而是枚举下标k
// 对于一个下标k,执行所有DFT合并中该下标对应的蝴蝶操作,即通过E[k]和O[k]计算X[k]
// 蝴蝶操作参考:http://en.wikipedia.org/wiki/Butterfly_diagram
for (int k = 0; k < step; k++)
{ // 计算omega^k. 这个方法效率低,但如果用每次乘omega的方法递推会有精度问题。
// 有更快更精确的递推方法,为了清晰起见这里略去
CD omegak = exp(CD(0, alpha*k));
for (int Ek = k; Ek < n; Ek += step << 1)
{ // Ek是某次DFT合并中E[k]在原始序列中的下标
int Ok = Ek + step; // Ok是该DFT合并中O[k]在原始序列中的下标
CD t = omegak * a[Ok]; // 蝴蝶操作:x1 * omega^k
a[Ok] = a[Ek] - t; // 蝴蝶操作:y1 = x0 - t
a[Ek] += t; // 蝴蝶操作:y0 = x0 + t
}
}
}
if (inverse)
for (int i = 0; i < n; i++) a[i] /= n;
}
// 用FFT实现的快速多项式乘法
inline vector operator * (const vector& v1, const vector& v2)
{
int s1 = v1.size(), s2 = v2.size(), S = 2;
while (S < s1 + s2) S <<= 1;
vector a(S, 0), b(S, 0); // 把FFT的输入长度补成2的幂,不小于v1和v2的长度之和
for (int i = 0; i < s1; i++) a[i] = v1[i];
FFT(a, false);
for (int i = 0; i < s2; i++) b[i] = v2[i];
FFT(b, false);
for (int i = 0; i < S; i++) a[i] *= b[i];
FFT(a, true);
vector res(s1 + s2 - 1);
for (int i = 0; i < s1 + s2 - 1; i++) res[i] = a[i].real(); // 虚部均为0
return res;
}