AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

快速傅里叶变换(FFT)

作者: 作者的头像   清风qwq ,  2023-02-01 10:57:20 ,  所有人可见 ,  阅读 536


5


3

前言

宣传: 算法主页

更好的阅读体验


前置知识

多项式

一个多项式表示为 $A(x) = a_0 + a_1 x + a_2 x^2 + … a_n x^n$

系数表示法: $A(x) = \sum _{i=0}^{n} a_i x^i$

点值表示法:用 $n$ 个不同的点 $(x, A(x))$ 唯一表示多项式 $A(x)$

复数

前置知识:向量、三角函数

定义

设 $i^2 = -1$ ,则一个复数表示为 $a + bi (a,b\in R)$

其中 $bi$ 为虚部, $a$ 为实部

复平面上,从 $(0,0)$ 到 $(a,b)$ 的向量表示 $a+bi$

棱长: $|Z| = \sqrt{a^2+b^2}$

幅角:为从 $x$ 正半轴逆时针旋转的角度

运算

加法: $(a+bi)+(c+di) = (a+c)+(b+d)i$

乘法: 几何意义:复数相乘,模长相乘,幅角相加

$\ \ \ \ \ \ \ \ \ \ \ $代数意义:$(a+bi)(c+di) = (ac-bd)+(ad+bc)i$

共轭复数

复数 $Z = a+bi$ 的共轭复数 $\bar{Z}$ 为 $a-bi$

$|Z|=|\bar{Z}|\ \ \ \ \ \ \ \ Z\cdot \bar{Z} = a^2+b^2$

复数除法

$z_1=a_1+b_1i,z_2=a_2+b_2i$

设 $z_0=\frac{z_1}{z_2}$

上下同乘 $z_2$ 的共轭复数 $\bar{z_2}$

$$\frac{z_1 \bar{z_2}}{a_2^2 + b_2^2}$$

单位根 $\omega $

为在复平面上以原点为圆心作半径为 $1$ 的圆。

$\omega_n^k $ 表示为将圆分成 $n$ 份,从 $x$ 正半轴逆时针取 $k$ 份下的复数。

  • $\omega_n^k = \omega _{n}^{k-1} \cdot \omega _n^1$

  • $\omega_n^0 = \omega_n^n=1$

  • $\omega_{2n}^{2k} = \omega_n^k$

  • $\omega_n^{k+\frac{n}{2}} = - \omega_n^k$


算法流程

1101696-20180211213536607-322408834.png


快速傅里叶变换

快速傅里叶变换($\texttt{Fast Fourier Transform , FFT}$)

使用 $\omega_n^k (0\leq k < n)$ 这 $n$ 个不同的数来当作点值表示法中的 $n$ 个 $x$

已知

$$A(x) = \sum_{i=0}^{n-1} a_i * x^i$$

设

$$A_1(x) = a_0 + a_2 \cdot x + a_4 \cdot x^2 + \cdots + a_{n-2} x^{\frac{n}{2}-1}$$

$$A_2(x) = a_1 + a_3 \cdot x + a_5 \cdot x^2 + \cdots + a_{n-1} x^{\frac{n}{2}-1}$$

则

$$A(x) = A_1(x^2) + xA_2(x^2)$$

将 $\omega_n^k (0\leq k < n)$ 代入 $A(x)$ 中

$$A(\omega_n^k) = A_1(\omega_{\frac{n}{2}}^{k}) + \omega_n^k A_2(\omega_{\frac{n}{2}}^{k})$$

$$A(\omega_n^{\frac{n}{2}+k}) = A_1(\omega_{\frac{n}{2}}^{k}) - \omega_n^k A_2(\omega_{\frac{n}{2}}^{k})$$

两个式子 $A_1()$ ,$A_2()$ 相同。

当计算 $[0,\frac{n}{2})$ 时,$[\frac{n}{2},n)$ 可以直接计算。

每次计算量减小一半,时间复杂度 $O(n\log n)$

快速傅里叶逆变换

快速傅里叶逆变换 $(\texttt{Inverse Fast Fourier Transform , IFFT})$

设 $C(x) = c_0 + c_1 x + c_2 x^2 + \cdots + c_{n - 1} x^{n-1}$

设 $b_i = \omega_n^i, y_i = C(b_i)$

$C(x)$ 经过 $n$ 个点,$(b_0, y_0) \dots (b_{n - 1}, y_{n - 1}) $


引理1

设

$$S(x) = 1 + x + x^2 + \cdots + x^{n-1}$$

因为

$$S(\omega_n^k) = \omega_n^0 + \omega_n^k + \omega_n^{2k} + \cdots + \omega_n^{(n-1)k}$$

又

$$\omega_n^k S(\omega_n^k) = \omega_n^k + \omega_n^{2k} + \cdots + \omega_n^{(n-1)k} + \omega_n^{nk}$$

所以

若 $k\not= 0$

则

$$S(\omega_n^k) = \frac{0}{1 - \omega_n^k} = 0$$

若 $k=0$

则

$$S(\omega_n^k) = n$$


引理2

$$nc_k = \sum_{i=0}^{n-1} y_i (w_n^{-k})^i$$

证明

$$\sum_{i=0}^{n-1} y_i (w_n^{-k})^i$$

$$=\sum_{i=0}^{n-1} (\sum_{j=0}^{n-1} c_j {(w_n^i)}^{j}) (w_n^{-k})^i$$

$$=\sum_{i=0}^{n-1} (\sum_{j=0}^{n-1} c_j {(w_n^j)}^{i}) (w_n^{-k})^i$$

$$=\sum_{i=0}^{n-1} (\sum_{j=0}^{n-1} c_j {(w_n^{j - k})}^{i})$$

$$=\sum_{j=0}^{n-1} c_j \sum_{i=0}^{n-1} (w_n^{j-k})^{i}$$

$$=\sum_{j=0}^{n-1} c_j S(w_n^{j-k})$$

$$=nc_k$$

证毕


设

$$D(x) = \sum_{i=0}^{n-1} y_i x^i$$

则

$$c_k = \frac{D(w_n^{-k})}{n}$$

所以再做一次快速傅里叶变换即可


$实现$

#include <bits/stdc++.h>
using namespace std;

const int N = 400010;
const double PI = acos(-1);
int n, m, tot;
int rev[N];

struct Complex
{
    double x, y;
    Complex operator+ (const Complex& t) const
    {
        return {x + t.x, y + t.y};
    }
    Complex operator- (const Complex& t) const
    {
        return {x - t.x, y - t.y};
    }
    Complex operator* (const Complex& t) const
    {
        return {x * t.x - y * t.y, x * t.y + y * t.x};
    }
} a[N], b[N];

void fft(Complex a[], int inv)
{
    for (int i = 0; i < tot; i ++ )
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
    for (int mid = 1; mid < tot; mid <<= 1)
    {
        Complex w1 = {cos(PI / mid), inv * sin(PI / mid)};
        for (int i = 0; i < tot; i += mid * 2)
        {
            Complex wk = {1, 0};
            for (int j = 0; j < mid; j ++ , wk = wk * w1)
            {
                Complex x = a[i + j], y = wk * a[i + j + mid];
                a[i + j] = x + y, a[i + j + mid] = x - y;
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i <= n; i ++ ) scanf("%lf", &a[i].x);
    for (int i = 0; i <= m; i ++ ) scanf("%lf", &b[i].x);

    int bit = 0;
    while ((1 << bit) <= n + m) bit ++ ;
    tot = 1 << bit;

    for (int i = 0; i < tot; i ++ )
        rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (bit - 1));

    fft(a, 1), fft(b, 1);
    for (int i = 0; i < tot; i ++ ) a[i] = a[i] * b[i];

    fft(a, -1);
    for (int i = 0; i <= n + m; i ++ )
        printf("%d ", (int)(a[i].x / tot + 0.5));
    return 0;
}

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息