算法学习笔记:FFT

算法学习笔记

FFT(FastFourierTransformation) F F T ( F a s t F o u r i e r T r a n s f o r m a t i o n ) ,即为快速傅氏变换,是离散傅氏变换的快速算法,它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。
说实话我根本就没看懂,那我就草草地说说。
~~我默认~~大家都知道多项式乘法 O(n2) O ( n 2 ) 的方法, FFT F F T 就是把它变成 O(nlogn) O ( n l o g n )

首先, FFT F F T 要先将系数表示法转化为点值表示法。

系数表示法:已知 n n ai f(x)=aixi f ( x ) = ∑ a i x i
点值表示法:已知 n n xi yi y i yi=f(xi) y i = f ( x i )
其中, f(x) f ( x ) n n n1次多项式
可得, f(x) f ( x ) 唯一确定,且两种表示法可相互转化。

接着, FFT F F T 要用数学方法转化两种表示法。

  1. exi=cosx+isinx e x i = c o s x + i s i n x
  2. ωn=e2πin ω n = e 2 π i n ,则 ωnn=(e2πin)n=e2πi=1 ω n n = ( e 2 π i n ) n = e 2 π i = 1 (ωkn)n=(ωnn)k=1k=1 ( ω n k ) n = ( ω n n ) k = 1 k = 1
  3. (ωk+n2n)2=ω2k+nn=ω2kn=(ωkn)2 ( ω n k + n 2 ) 2 = ω n 2 k + n = ω n 2 k = ( ω n k ) 2

然后, FFT F F T 要先 DFT D F T 然后再逆 DFT D F T

离散傅里叶变换( DiscreteFourierTransform D i s c r e t e F o u r i e r T r a n s f o r m ,缩写为 DFT D F T ),是傅里叶变换在时域和频域上都呈离散的形式,将信号的时域采样变换为其 DTFT D T F T 的频域采样。在实际应用中通常采用快速傅里叶变换计算 DFT D F T
说实话我还是没看懂,那我还是草草地说说。
假设 f(x)=a0+a1x+a2x2++an1xn1 f ( x ) = a 0 + a 1 x + a 2 x 2 + … + a n − 1 x n − 1
构造 f0(x)=a0+a2x+a4x2++an2xn21 f 0 ( x ) = a 0 + a 2 x + a 4 x 2 + … + a n − 2 x n 2 − 1
构造 f1(x)=a1+a3x+a5x2++an1xn21 f 1 ( x ) = a 1 + a 3 x + a 5 x 2 + … + a n − 1 x n 2 − 1
那么 f(x)=f0(x2)+xf1(x2) f ( x ) = f 0 ( x 2 ) + x f 1 ( x 2 )
假设 xk=ωkn x k = ω n k ,那么 xk+n2=xk x k + n 2 = − x k
这样,我们就可以用 f0(x) f 0 ( x ) f1(x) f 1 ( x ) 的点值表示法求出 f(x) f ( x ) 的点值表示法
时间 O(nlogn) O ( n l o g n )
至于逆 DFT D F T ,水证一下。
对于 ωipn ∑ ω i p n
p=0 p = 0 时,原式 =1 = 1
p0 p ≠ 0 时,原式 =1n1qn1q=0 = 1 n 1 − q n 1 − q = 0
所以,原式 =[p=0] = [ p = 0 ]
用最原始的式子:

ci=jkajbk[i+j+k=0]=1njkajbklωilωjlωkl=1nlωil(jajωjl)(kbkωkl)(17)(18)(19)(20) (17) (18) c i = ∑ j ∑ k a j b k [ − i + j + k = 0 ] (19) = 1 n ∑ j ∑ k a j b k ∑ l ω − i l ω j l ω k l (20) = 1 n ∑ l ω − i l ( ∑ j a j ω j l ) ( ∑ k b k ω k l )

也就是说先将 a a b各做一次 DFT D F T ,再将 a a b相乘得到 d d ,最后将d做一次逆 DFT D F T

分治过程中,需要将系数交错排列。
零层: 0,1,2,3,4,5,6,7 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7
一层: 0,2,4,6,1,3,5,7 0 , 2 , 4 , 6 , 1 , 3 , 5 , 7
两层: 0,4,2,6,1,5,3,7 0 , 4 , 2 , 6 , 1 , 5 , 3 , 7
这被叫做蝴蝶变换。

我们为了从下到上计算,必须预处理出这个序列。
将序列转化为二进制数,可以发现神奇的玩意儿。
两层: 000,100,010,110,001,101,011,111 000 , 100 , 010 , 110 , 001 , 101 , 011 , 111
零层: 000,001,010,011,100,101,110,111 000 , 001 , 010 , 011 , 100 , 101 , 110 , 111
正好倒序,也就是说,可以直接得出。

算法学习结束。
写笔记好累的,好奇大佬们都怎么写的。


luogu1919 题 目 传 送 门 l u o g u 1919
bzoj2179 题 目 传 送 门 b z o j 2179


题目

2179:FFT 2179 : F F T 快 速 傅 立 叶

TimeLimit:10Sec T i m e L i m i t : 10 S e c
MemoryLimit:259MB M e m o r y L i m i t : 259 M B

Description D e s c r i p t i o n

给出两个 n n 10进制整数 x x y,你需要计算 xy x ⋅ y

Input I n p u t

第一行一个正整数 n n 。 第二行描述一个位数为n的正整数 x x 。 第三行描述一个位数为n的正整数 y y

Output

输出一行,即 xy x ⋅ y 的结果。

SampleInput S a m p l e I n p u t

1
3
4

SampleOutput S a m p l e O u t p u t

12

数据范围:

n60000 n ≤ 60000


题解

这是 FFT F F T 的入门题目,只要将大数 an1an2a1a0¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ a n − 1 a n − 2 … a 1 a 0 ¯ 转化为 f(x)=an1xn1+an2xn2++a1x+a0 f ( x ) = a n − 1 x n − 1 + a n − 2 x n − 2 + … + a 1 x + a 0 ,然后将两个多项式相乘,并且将 x=10 x = 10 代入即可。
时间 O(nlogn) O ( n l o g n ) ,空间 O(n) O ( n )


总结

当我们需要求解 ci=ajbij c i = ∑ a j b i − j 这样的式子的时候,可以利用 FFT F F T 小优化。当然,如果不用能过,那在好不过了。


标程

#include <bits/stdc++.h>
using namespace std;
const double PI = acos(-1);
const int N = 1 << 17;
struct Complex{
    double r, i;
    Complex(double _r = 0, double _i = 0) {r = _r; i = _i;}
    Complex operator +(Complex c) {return Complex(r + c.r, i + c.i);}
    Complex operator -(Complex c) {return Complex(r - c.r, i - c.i);}
    Complex operator *(Complex c) {return Complex(r * c.r - i * c.i, r * c.i + i * c.r);}
    Complex operator /(int c) {return Complex(r / c, i / c);}
}a[N], b[N];
int n, m, l, c[N], d[N];
char s[N];
void fft(Complex *a, int p)
{
    for (int i = 0; i < n; i++) if (i < d[i]) swap(a[i], a[d[i]]);
    for (int i = 1; i < n; i <<= 1)
    {
        Complex u = Complex(cos(PI / i), p * sin(PI / i));
        for (int j = 0; j < n; j += (i << 1))
        {
            Complex v = Complex(1, 0);
            for (int k = 0; k < i; k++, v = v * u)
            {
                Complex x = a[j + k], y = v * a[j + k + i];
                a[j + k] = x + y; a[j + k + i] = x - y;
            }
        }
    }
    if (p == -1) for (int i = 0; i < n; i++) a[i] = a[i] / n;
}
int main()
{
    scanf("%d", &n); n--;
    scanf("%s", s); for (int i = 0; i <= n; i++) a[i] = Complex(s[n - i] - '0', 0);
    scanf("%s", s); for (int i = 0; i <= n; i++) b[i] = Complex(s[n - i] - '0', 0);
    m = n << 1; for (n = 1; n <= m; n <<= 1) l++;
    for (int i = 0; i < n; i++) d[i]=((d[i >> 1] >> 1) | (i & 1) << (l - 1));
    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 <= m; i++) c[i] = (int)(a[i].r + 0.5);
    for (int i = 0; i <= m; i++)
        if (c[i] >= 10)
        {
            c[i + 1] += c[i] / 10;
            c[i] %= 10;
            if (i == m) m++;
        }
    for (int p = 0, i = m; i >= 0; i--) if (p || c[i]) {p = 1; printf("%d", c[i]);}
    return 0;
}

友情链接

算法学习笔记,我实在不会(想)写好,于是就。。。。
友链一份 FFT 算 法 学 习 笔 记 : F F T
xxcc x x c c 大神算法学习笔记,上文大多摘自上文。
lvzelong2014 l v z e l o n g 2014 大神因为数学远远好于常人,所以不屑于写。)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值