1312 字
7 分钟
从N方到NlogN的转变——FFT
2017-08-14

1.Why#

为什么我们信息学竞赛要用到FFT
因为我们要优化卷积啊
将n边为log是一个非常大的优化啊

What#

什么是FFT呢 在wiki上是这么说的

快速傅里叶变换(英语:Fast Fourier Transform, FFT),是计算序列的离散傅里叶变换(DFT)或其逆变换的一种算法。傅里叶分析将信号从原始域(通常是时间或空间)转换到频域的表示或者逆过来转换。FFT会通过把DFT矩阵分解为稀疏(大多为零)因子之积来快速计算此类变换。[1] 因此,它能够将计算DFT的复杂度从只用DFT定义计算需要的 O(n2)O(n^{2}),降低到 O(nlogn)O(n\log n),其中 n 为数据大小。

啥你说啥我听不懂(黑人问号?)

如果简单的说就是求两个多项式的乘积

How#

其实关于FFT的具体理论很复杂,我一篇博客肯定不能接受清楚 所以请有兴趣的人移步Google 或 baidu

如果简单来说FFT用的分制的方法
所以才能有log啊

将一个多项式按奇偶分开
但如果我们直接找值去计算时间复杂度还是O(n2)O(n^2)

所以出现了一个特殊的东西他叫做n次单位根

然后利用他的性质就可以搞了

先贴两个板子

UOJ 34 多项式乘法

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1 << 18 | 5;
namespace FFT
{
const double pi = acos(-1.0);
struct Complex
{
    double x, y;
    Complex operator+(const Complex &a)
    {
        return (Complex){x + a.x, y + a.y};
    }
    Complex operator-(const Complex &a)
    {
        return (Complex){x - a.x, y - a.y};
    }
    Complex operator*(const Complex &a)
    {
        return (Complex){x * a.x - y * a.y, x * a.y + y * a.x};
    }
    Complex operator*(const double &a)
    {
        return (Complex){x * a, y * a};
    }
    Complex Get()
    {
        return (Complex){x, -y};
    }
} c[maxn], c1[maxn], V = (Complex){0.5, 0} * (Complex){0, -0.5};
int rev[maxn], N, FU;
double Inv;
void FFt(Complex *a, int op)
{
    Complex w, wn, t;
    for (int i = 0; i < N; i++)
        if (i < rev[i])
            swap(a[i], a[rev[i]]);
    for (int k = 2; k <= N; k <<= 1)
    {
        wn = (Complex){cos(op * 2 * pi / k), sin(op * 2 * pi / k)};
        for (int j = 0; j < N; j += k)
        {
            w = (Complex){1, 0};
            for (int i = 0; i < (k >> 1); i++, w = w * wn)
            {
                t = a[i + j + (k >> 1)] * w;
                a[i + j + (k >> 1)] = a[i + j] - t;
                a[i + j] = a[i + j] + t;
            }
        }
    }
    if (op == -1)
        for (int i = 0; i < N; i++)
            a[i] = a[i] * Inv;
}
void FFt(int *a, int *b, int *res, int n)
{
    int j;
    N = 1;
    while (N < n)
        N <<= 1;
    FU = N - 1;
    Inv = 1. / N;
    for (int i = 0; i < N; i++)
        if (i & 1)
            rev[i] = (rev[i >> 1] >> 1) | (N >> 1);
        else
            rev[i] = (rev[i >> 1] >> 1);
    for (int i = 0; i < N; i++)
        c[i].x = a[i], c[i].y = b[i];
    FFt(c, 1);
    for (int i = 0; i < N; i++)
        j = (N - i) & FU, c1[i] = (c[i] + c[j].Get()) * (c[i] - c[j].Get()) * V;
    FFt(c1, -1);
    for (int i = 0; i < N; i++)
        res[i] = round(c1[i].x);
}
} // FFT

int main(int argc, char const *argv[])
{
    static int a[maxn],b[maxn];
    int n1, n2, m;
    scanf("%d%d", &n1, &n2);
    m = n1 + n2 + 1;
    for (int i = 0; i <= n1; i++)
        scanf("%d", &a[i]);
    for (int i = 0; i <= n2; i++)
        scanf("%d", &b[i]);
    FFT::FFt(a, b, a, m);
    for (int i = 0; i < m; i++)
        printf("%d ", a[i]);
    return 0;
}

COGS 1473. 很强的乘法问题

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
const double pi = acos(-1.);
struct Complex
{
    double x, y;
    Complex() { ; }
    Complex(double a, double b) : x(a), y(b) {}
    Complex operator+(const Complex &a) { return Complex(x + a.x, y + a.y); }
    Complex operator-(const Complex &a) { return Complex(x - a.x, y - a.y); }
    Complex operator*(const Complex &a) { return Complex(x * a.x - y * a.y, x * a.y + y * a.x); }
    Complex operator*(const double a) { return Complex(x * a, y * a); }
    Complex Get() { return Complex(x, -y); }
} A[150005 << 3], B[150005 << 3];
int rev[150005 << 3], N, FU;
double INV;
void FFt(Complex *a, int op)
{
    Complex w, wn, t;
    for (int i = 0; i < N; i++)
        if (i < rev[i])
            std::swap(a[i], a[rev[i]]);
    for (int k = 2; k <= N; k <<= 1)
    {
        wn = Complex(cos(op * 2 * pi / k), sin(op * 2 * pi / k));
        for (int j = 0; j < N; j += k)
        {
            w = Complex(1, 0);
            for (int i = 0; i < (k >> 1); i++, w = w * wn)
            {
                t = a[i + j + (k >> 1)] * w;
                a[i + j + (k >> 1)] = a[i + j] - t;
                a[i + j] = a[i + j] + t;
            }
        }
    }
    if (op == -1)
        for (int i = 0; i < N; i++)
            a[i] = a[i] * INV;
}
void FFt(const int *a, const int *b, int *res, int n)
{
    N = 1;
    while (N < n)
        N <<= 1;
    INV = 1. / N;
    for (int i = 0; i < N; i++)
        if (i & 1)
            rev[i] = (rev[i >> 1] >> 1) | (N >> 1);
        else
            rev[i] = (rev[i >> 1] >> 1);
    for (int i = 0; i < N; i++)
        A[i].x = a[i], B[i].x = b[i];
    FFt(A, 1), FFt(B, 1);
    for (int i = 0; i < N; i++)
        A[i] = A[i] * B[i];
    FFt(A, -1);
    for (int i = 0; i < N; i++)
        res[i] = round(A[i].x);
}
char s[150005 << 2];
struct BigNum
{
    int len;
    int a[1000005];
    void read()
    {
        scanf("%s", s);
        len = strlen(s);
        for (int i = len - 1, j = 0; i >= 0; i--, j++)
            a[j] = s[i] - '0';
    }
    BigNum operator*(const BigNum &b)
    {
        BigNum ans;
        ans.len = len + b.len - 1;
        FFt(a, b.a, ans.a, ans.len + 1);
        for (int i = 0; i <= ans.len + 2; i++)
        {
            if (ans.a[i] > 9)
            {
                ans.a[i + 1] += ans.a[i] / 10;
                ans.a[i] %= 10;
            }
        }
        while (ans.a[ans.len])
            ans.len++;
        return ans;
    }
    void print()
    {
        for (int i = len - 1; i >= 0; i--)
            printf("%d", a[i]);
        printf("\n");
    }
} a, b;
 
int main()
{
    freopen("bettermul.in","r",stdin);
    freopen("bettermul.out","w",stdout);
    a.read();
    b.read();
    (a * b).print();
    //while (1)
        ;
}
从N方到NlogN的转变——FFT
https://www.nekomio.com/posts/96/
作者
NekoMio
发布于
2017-08-14
许可协议
CC BY-NC-SA 4.0