0%

卷积&快速傅里叶变换

多项式的系数表示和点值表示,在计算多项式的数乘、加法、乘法时各有优势,利用快速傅里叶变换(FFT),在O(nlogn)的时间复杂度内进行两中表示方法的转化,可以有效降低程序的复杂度。以卷积为例,如下图所示1557145462556

利用下面这篇博客:快速傅里叶变换(FFT)详解,学习了FFT的写法,代码中把实系数都转化成复数,统一进行复数上的运算。IFFT(逆运算)和FFT可以放一起,用复数的虚部(+/- i)就可以区分两种不同的运算,至于原理,属于数学属于数学证明了(关键我不会)。rev数组的作用没有看懂,下次要来这里补上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include<bits/stdc++.h>
using namespace std;
const double pi = acos(-1);
struct cn
{
double x, y;
cn(double x = 0, double y = 0) :x(x), y(y) {}
}a[300005], b[300005], c[300005];
cn operator + (const cn &a, const cn &b) { return cn(a.x + b.x, a.y + b.y); }
cn operator - (const cn &a, const cn &b) { return cn(a.x - b.x, a.y - b.y); }
cn operator * (const cn &a, const cn &b) { return cn(a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x); }
void fft(cn a[], int n, int l, int f)
{
int* rev=new int[n + 5];
rev[0] = 0;
for (int i = 1; i<n; i++) {
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << l - 1);
if (i<rev[i]) swap(a[i], a[rev[i]]);
}
for (int i = 1; i<n; i <<= 1) {
cn wi(cos(pi / i), f*sin(pi / i));
for (int j = 0; j<n; j += i * 2) {
cn w(1, 0);
for (int k = 0; k<i; k++) {
cn x = a[j + k], y = w * a[j + k + i];
a[j + k] = x + y;
a[j + k + i] = x - y;
w = w * wi;
}
}
}
if (f == -1)
for (int i = 0; i<n; i++) {
a[i].x /= n; a[i].y /= n;
}
}
int main()
{
int n, m;
scanf_s("%d%d", &n, &m); n++; m++;
for (int i = 0; i<n; i++) scanf_s("%lf", &a[i].x);
for (int i = 0; i<m; i++) scanf_s("%lf", &b[i].x);
int l = 0, N = 1;
while (N<n + m - 1) N <<= 1, l++;
fft(a, N, l, 1);
fft(b, N, l, 1);
for (int i = 0; i<N; i++) c[i] = a[i] * b[i];
fft(c, N, l, -1);
for (int i = 0; i<n + m - 1; i++) printf("%d ", (int)(c[i].x + 0.5));
system("pause");
return 0;
}