赞
踩
快速傅里叶变换 (Fast Fourier Transform)**,即利用计算机计算离散傅里叶变换(DFT)的高效、快速计算方法的统称,简称FFT,于1965年由J.W.库利和T.W.图基提出。
对于多项式
定义其乘积fg为
显然我们可以以 O(n2) 的复杂度计算这个乘积的每一项的系数。
但FFT可以以 O(nlogn) 的时间复杂度来计算这个乘积。
自1965年提出至今,FFT算法在一维的信号处理和二维的图像处理中都起到了重要的作用,在人工智能、自动化等领域都有着不可替代的作用
FFT的具体实现方法有很多,这里举一个递归实现的方法
- #include <bits/stdc++.h>
- const int MAX_COEFFICIENTS = 1 << 22;
- const double EPSILON = 1e-6, PI = acos(-1.0);
-
- using namespace std;
-
- complex<double> coefficientsA[MAX_COEFFICIENTS], coefficientsB[MAX_COEFFICIENTS];
-
- int degreeA, degreeB;
-
- // FFT: Fast Fourier Transform
- // Parameters:
- // - coefficients: Array representing the coefficients of a polynomial
- // - degree: Degree of the polynomial
- // - inv: If inv is 1, perform FFT; if inv is -1, perform IFFT
- void FFT(complex<double> *coefficients, int degree, int inv) {
- if (degree == 1) // If there is only one term, return directly
- return;
-
- int mid = degree / 2;
-
- // Split the polynomial into two parts
- complex<double> A1[mid + 1], A2[mid + 1];
- for (int i = 0; i <= degree; i += 2) {
- A1[i / 2] = coefficients[i];
- A2[i / 2] = coefficients[i + 1];
- }
-
- // Recursively apply FFT or IFFT
- FFT(A1, mid, inv);
- FFT(A2, mid, inv);
-
- complex<double> w0(1, 0), wn(cos(2 * PI / degree), inv * sin(2 * PI / degree)); // Unit root
-
- // Merge the polynomials
- for (int i = 0; i < mid; ++i, w0 *= wn) {
- coefficients[i] = A1[i] + w0 * A2[i];
- coefficients[i + degree / 2] = A1[i] - w0 * A2[i];
- }
- }
-
- int main() {
- scanf("%d%d", °reeA, °reeB);
-
- // Input coefficients for the first polynomial
- for (int i = 0; i <= degreeA; ++i) {
- double x;
- scanf("%lf", &x);
- coefficientsA[i].real(x);
- }
-
- // Input coefficients for the second polynomial
- for (int i = 0; i <= degreeB; ++i) {
- double x;
- scanf("%lf", &x);
- coefficientsB[i].real(x);
- }
-
- int length = 1 << max((int)ceil(log2(degreeA + degreeB)), 1); // Determine the length for FFT
- FFT(coefficientsA, length, 1); // Convert coefficient representation to point-value representation
- FFT(coefficientsB, length, 1);
-
- // Perform polynomial multiplication in O(n) time
- for (int i = 0; i <= length; ++i)
- coefficientsA[i] = coefficientsA[i] * coefficientsB[i];
-
- FFT(coefficientsA, length, -1); // Convert point-value representation back to coefficient representation
-
- // Output the result
- for (int i = 0; i <= degreeA + degreeB; ++i)
- printf("%.0f ", coefficientsA[i].real() / length + EPSILON); // Divide by length; EPSILON to handle precision issues
-
- return 0;
- }
-
以下是针对这个递归实现的优化
- #include <bits/stdc++.h>
- const int MAX_COEFFICIENTS = 1 << 22;
- const double EPSILON = 1e-6, PI = acos(-1.0);
-
- using namespace std;
-
- complex<double> coefficientsA[MAX_COEFFICIENTS], coefficientsB[MAX_COEFFICIENTS];
-
- int degreeA, degreeB;
-
- // FFT: 快速傅里叶变换
- // 参数:
- // - coefficients: 表示多项式的系数数组
- // - degree: 多项式的阶数
- // - inv: 如果 inv 为 1,执行 FFT;如果 inv 为 -1,执行 IFFT
- void FFT(complex<double> *coefficients, int degree, int inv) {
- // 位反转置换
- for (int i = 1, j = 0; i < degree; ++i) {
- for (int k = degree >> 1; (j ^= k) < k; k >>= 1);
- if (i < j) swap(coefficients[i], coefficients[j]);
- }
-
- // Cooley-Tukey算法迭代过程
- for (int len = 2; len <= degree; len <<= 1) {
- double ang = inv * 2 * PI / len;
- complex<double> wlen(cos(ang), sin(ang));
-
- for (int i = 0; i < degree; i += len) {
- complex<double> w(1, 0);
-
- for (int j = 0, mid = len >> 1; j < mid; ++j) {
- complex<double> u = coefficients[i + j], v = w * coefficients[i + j + mid];
- coefficients[i + j] = u + v;
- coefficients[i + j + mid] = u - v;
- w *= wlen;
- }
- }
- }
- }
-
- int main() {
- scanf("%d%d", °reeA, °reeB);
-
- // 输入第一个多项式的系数
- for (int i = 0; i <= degreeA; ++i) {
- double x;
- scanf("%lf", &x);
- coefficientsA[i].real(x);
- }
-
- // 输入第二个多项式的系数
- for (int i = 0; i <= degreeB; ++i) {
- double x;
- scanf("%lf", &x);
- coefficientsB[i].real(x);
- }
-
- int length = 1 << max((int)ceil(log2(degreeA + degreeB)), 1); // 确定 FFT 的长度
- FFT(coefficientsA, length, 1); // 将系数表示转换为点值表示
- FFT(coefficientsB, length, 1);
-
- // 在 O(n) 时间内执行多项式乘法
- for (int i = 0; i <= length; ++i)
- coefficientsA[i] = coefficientsA[i] * coefficientsB[i];
-
- FFT(coefficientsA, length, -1); // 将点值表示转换回系数表示
-
- // 输出结果
- for (int i = 0; i <= degreeA + degreeB; ++i)
- printf("%.0f ", coefficientsA[i].real() / length + EPSILON); // 除以长度;EPSILON 用于处理精度问题
-
- return 0;
- }
-
优化点:
位反转置换(Bit-reverse permutation): 避免了递归过程中的重复计算,通过位反转置换一次性完成了重新排列。
Cooley-Tukey算法的迭代实现: 使用迭代而非递归方式,减少了递归调用的开销。
减少内存使用: 不再需要额外的临时数组,直接在原数组上进行计算,减少了内存占用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。