【模板】快速傅里叶变换

P3803

给定一个 \(n\) 次多项式 \(F(x)\),和一个 \(m\) 次多项式 \(G(x)\)。 请求出 \(F(x)\)\(G(x)\) 的卷积。

\(n, m \leq {10}^6\)

快速傅里叶变换

FFT变换

\(n=2^k\),已知\(f(x)=\sum_{i\in[0,2^k)}a_ix^i\),求所有\(y_j = f(\omega_{2^k}^{j})\)


做法:由于

\[y_j=f(\omega_{2^k}^{j})=\sum_{i\in[0,2^k)}a_i(\omega_{2^k}^{j})^i\\ y_{j+2^{k-1}}=f(\omega_{2^k}^{j+2^{k-1}})=\sum_{i\in[0,2^k)}a_i(\omega_{2^k}^{j+2^{k-1}})^i=\sum_{i\in[0,2^k)}a_i(-\omega_{2^k}^j)^i \]

\(f_i(x) = \sum_{j\in[0,2^{k-1})}a_{2j+i}x^j\quad(i\in\{0,1\})\)

\[y_j = f_0((\omega_{2^k}^j)^2)+\omega_{2^k}^j f_1((\omega_{2^k}^j)^2) = f_0(\omega_{2^{k-1}}^j)+\omega_{2^k}^j f_1(\omega_{2^{k-1}}^j) \\ y_{j+2^{k-1}} = f_0((-\omega_{2^k}^j)^2)-\omega_{2^k}^{j}f_1((-\omega_{2^k}^j)^2) = f_0(\omega_{2^{k-1}}^j)-\omega_{2^k}^j f_1(\omega_{2^{k-1}}^j) \]

故只需求出所有\(y'_j=f_1(\omega_{2^{k-1}}^j),y''_j=f_2(\omega_{2^{k-1}}^j)\)即可。(成功划分成了两个子问题)

此时\(y_j=y'_j+\omega_{2^k}^jy''_j, y_{j+2^{k-1}}=y'_j-\omega_{2^k}^jy''_j\)

FFT逆变换

假设已有\(y_j=\sum_{i\in[0,n)}a_i\omega_n^{ij}\),要求\(a_i\)


考虑对\(y_j\)再做一个反的FFT,得到:

\[\begin{aligned} x_i &= \sum_{j\in[0,n)}y_j\omega_n^{-ij} \\ &= \sum_{j\in[0,n)}\sum_{k\in[0,n)}a_k\omega_n^{kj}\omega_n^{-ij} \\ &= \sum_{j\in[0,n)}\sum_{k\in[0,n)}a_k\omega_n^{(k-i)j} \\ &= \sum_{k\in[0,n)}a_k\sum_{j\in[0,n)}(\omega_n^{k-i})^j \\ &= na_{i} \end{aligned} \]

即得\(a_i=x_i/n\)

递归实现[1]

void fast_fast_tle(int limit, complex *a, int type) {
	if (limit == 1) return ;
	complex a1[limit >> 1], a2[limit >> 1];
	for (int i = 0; i <= limit; i += 2)
		a1[i >> 1] = a[i], a2[i >> 1] = a[i + 1];
	fast_fast_tle(limit >> 1, a1, type);
	fast_fast_tle(limit >> 1, a2, type);
	complex Wn = complex(cos(2.0 * Pi / limit) , type * sin(2.0 * Pi / limit)), w = complex(1, 0);
	for (int i = 0; i < (limit >> 1); i++, w = w * Wn)
		a[i] = a1[i] + w * a2[i],
			   a[i + (limit >> 1)] = a1[i] - w * a2[i];
}

非递归实现[1:1]

预处理r[i]i的二进制表示的反转):

for (int i = 0; i < limit; i++)
	r[i] = ( r[i >> 1] >> 1 ) | ( (i & 1) << (l - 1) ) ;

代码:

void fast_fast_tle(complex *A, int type) {
	for (int i = 0; i < limit; i++)
		if (i < r[i]) swap(A[i], A[r[i]]); //r[r[i]] == i
	for (int mid = 1; mid < limit; mid <<= 1) { //自底向上,mid表示区间长度的一半
		complex Wn( cos(Pi / mid) , type * sin(Pi / mid) ); //mid<<1次单位根。
		for (int R = mid << 1, j = 0; j < limit; j += R) { //R为区间长度,j为当前的最右端点
			complex w(1, 0);
			for (int k = 0; k < mid; k++, w = w * Wn) {
				complex x = A[j + k], y = w * A[j + mid + k];
				A[j + k] = x + y;
				A[j + mid + k] = x - y;
			}
		}
	}
}

有关complex

C++的complex<T>

手写struct complex[1:2]

const double Pi = acos(-1.0);
struct complex {
    double x, y;
    complex (double xx = 0, double yy = 0) {x = xx, y = yy;}
} a[MAXN], b[MAXN];
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);}

  1. 参考文章 ↩︎ ↩︎ ↩︎

posted @ 2021-05-20 19:44  frank3215  阅读(107)  评论(0编辑  收藏  举报