算法学习笔记
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
项,
f(x)=∑aixi
f
(
x
)
=
∑
a
i
x
i
点值表示法:已知
n
n
项和
yi
y
i
,
yi=f(xi)
y
i
=
f
(
x
i
)
其中,
f(x)
f
(
x
)
为
n
n
项次多项式
可得,
f(x)
f
(
x
)
唯一确定,且两种表示法可相互转化。
接着, FFT F F T 要用数学方法转化两种表示法。
- exi=cosx+isinx e x i = c o s x + i s i n x
- 设 ω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
- (ω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+…+an−1xn−1
f
(
x
)
=
a
0
+
a
1
x
+
a
2
x
2
+
…
+
a
n
−
1
x
n
−
1
构造
f0(x)=a0+a2x+a4x2+…+an−2xn2−1
f
0
(
x
)
=
a
0
+
a
2
x
+
a
4
x
2
+
…
+
a
n
−
2
x
n
2
−
1
构造
f1(x)=a1+a3x+a5x2+…+an−1xn2−1
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
。
当
p≠0
p
≠
0
时,原式
=1n1−qn1−q=0
=
1
n
1
−
q
n
1
−
q
=
0
。
所以,原式
=[p=0]
=
[
p
=
0
]
。
用最原始的式子:
也就是说先将 a a ,各做一次 DFT D F T ,再将 a a 、相乘得到 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 位进制整数 x x 和,你需要计算 x⋅y x ⋅ y 。
Input I n p u t
第一行一个正整数 n n 。 第二行描述一个位数为的正整数 x x 。 第三行描述一个位数为的正整数 y y 。
输出一行,即 x⋅y 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
数据范围:
n≤60000 n ≤ 60000
题解
这是
FFT
F
F
T
的入门题目,只要将大数
an−1an−2…a1a0¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
a
n
−
1
a
n
−
2
…
a
1
a
0
¯
转化为
f(x)=an−1xn−1+an−2xn−2+…+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=∑ajbi−j
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
大神因为数学远远好于常人,所以不屑于写。)