傅里叶变换在多项式乘法中的应用(一)
\(A(x) = a_0 + a_1 x + a_2 x^2 + ··· + a_{n-1} x^{n-1}\)
求多项式的点值表达式
为了得到\(A(x)\)的点值表达式,选择一些比较特殊的\(x:=w_n^k\),\(k \in \{0,1,2,···,n-1\}\)代入。
已知\(e^{i\theta} = cos \theta + i sin \theta\)
记 \(w_n^k = e^{i*(\frac{2\pi}{n}*k)} = cos(\frac{2\pi}{n}*k) + i sin(\frac{2\pi}{n}*k)\),则\(w_n^{-k}\)表示\(w_n^k\)的共轭复数。
从向量角度看,\(w_n^k\)和复平面中与实轴正向夹角为\(\frac{2\pi}{n}*k\)的单位向量一一对应。
根据上面那个表达式可推得:
- \(w_{mn}^{mk} = w_n^k\)
- \(w_n^{k + \frac{n}{2}} = w_n^{-k}\)
先将\(A(x)\)按照\(x\)的阶的奇偶性分成两组,既:
令
则
当\(x = w_n^k\),\(k \in \{0,1,2,···,\frac{n}{2} - 1\}\),有
当\(x = w_n^{k + \frac{n}{2}}\),\(k \in \{0,1,2,···,\frac{n}{2} - 1\}\),有
如果已知\(A_1(x)\)和\(A_2(x)\)在\(x = w_{\frac{n}{2}}^k\),\(k \in \{0,1,2,···,\frac{n}{2} - 1\}\)处的值,那么就可以在\(O(n)\)的时间内求得\(A(x)\)的点值表达式:\(((w_n^0,A(w_n^0)),(w_n^1,A(w_n^1)),···,(w_n^{n-1},A(w_n^{n-1})))\)。
求解\(A_1(x)\)和\(A_2(x)\)在\(x = w_{\frac{n}{2}}^k\),\(k \in \{0,1,2,···,\frac{n}{2} - 1\}\)处的值与求解\(A(x)\)在\(x = w_n^k\),\(k \in \{0,1,2,···, n-1\}\)处的值的过程是一样的,只是规模减少了一半,运用递归分析法可概括得:
拿个多项式模拟一下上述表达式蕴含的递归过程,如图:
code
struct complex_number {
double a, b;
complex_number() { a = 0, b = 0; }
complex_number(double a, double b): a(a), b(b) {}
complex_number operator + (const complex_number& o) const {
return complex_number(a + o.a, b + o.b);
}
complex_number operator - (const complex_number& o) const {
return complex_number(a - o.a, b - o.b);
}
complex_number operator * (const complex_number& o) const {
return complex_number(a * o.a - b * o.b, a * o.b + b * o.a);
}
complex_number complex_conjugate() {
return complex_number(a, -b);
}
};
const int maxn = 100005;
complex_number cn[maxn];
complex_number w(int n, int k) {
return complex_number(cos(2 * atan(1) * 4.0 * k / n), sin(2 * atan(1) * 4.0 * k / n));
}
void DFT(complex_number* A, int n) {
if (n == 1) return;
const int m = n >> 1;
static complex_number buf[maxn];
for (int i = 0; i < m; i++) {
buf[i] = A[i << 1];
buf[i + m] = A[i << 1 | 1];
}
memcpy(A, buf, sizeof(complex_number) * n);
complex_number *A1 = A, *A2 = A + m;
DFT(A1, m);
DFT(A2, m);
for (int i = 0; i < m; i++) {
complex_number temp = w(n, i);
buf[i] = A1[i] + temp * A2[i];
buf[i + m] = A1[i] - temp * A2[i];
}
memcpy(A, buf, sizeof(complex_number) * n);
}
点值表达式 \(\rightarrow\) 系数表达式 [ Inverse Discrete Fourier Transform ]
假设 \(d_k = A(w_n^k) = \sum_{i=0}^{n-1}a_i (w_n^k)^i\),\(k \in \{0,1,2,···, n-1\}\),构造如下多项式:
假设 \(c_k = F(w_n^{-k}) = \sum_{i = 0}^{n-1}d_i(w_n^{-k})^i\),\(k \in \{0,1,2,···,n-1\}\),展开\(d_i\)得:
当\(j \neq k\)时,令\(j - k = \theta\),有:
故
所以只要求得了\(F(x)\)的点值表达式:\(((w_n^0, c_1), (w_n^{-1}, c_2), ···, (w_n^{-(n-1)},c_{n-1}))\),那么就可以在\(O(n)\)的时间内求得\(F(x)\)的系数表达式:\((a_0, a_1, a_2, ···, a_{n-1})\)。
参考
- 一小时学会快速傅里叶变换(Fast Fourier Transform) - 白空谷的文章 - 知乎
https://zhuanlan.zhihu.com/p/31584464