快速傅里叶变换
发表于:2023-10-12 | 分类: 数论
字数统计: 971 | 阅读时长: 4分钟 | 阅读量:

学了数字信号处理 里的FFT于是上洛谷看了许久的cpp

洛谷:多项式乘法

思路:

对于朴素的 O(n2)O(n^2) 的乘法求多项式显然过不了 1e61e6 的高时间复杂度, 此时引入离散傅里叶变换(DFT)此时引入离散傅里叶变换(DFT)逆离散傅里叶变换逆离散傅里叶变换
我们可以通过简单模拟知道多项式的乘法就是其系数的卷积运算,然后时间域的卷积等于其频域的乘法,即 f[t]g[t]=F(jw).G(jw)(这里区分.分别为普通乘法和卷积运算)f[t] * g[t] = F(jw) .* G(jw) (这里区分 .* 和 * 分别为普通乘法和卷积运算)。 这里的等式是连续函数的,离散条件下该等式也成立。
这里我们过程就是
AB的多项式系数(DFT)AB的频域形式相乘(IDFT)ANSWER的多项式系数A和B的多项式系数→(DFT)→A和B的频域形式相乘→(IDFT)→ANSWER的多项式系数

这个过程中,我们 DFTDFT 采用 FFTFFT 进行正转换和逆转换,其中的复杂度为 O(nlogn)O(nlogn), 频域相乘的复杂度为 O(n)O(n)

实现

FFTFFT 实际上就是将一整个序列平均分成两个序列进行 DFTDFT,发现这俩个部分存在重复的值,便可以不断实现 O(logn)O(logn) 的划分,最终实现递归。

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
const double pi = acos(-1.0);
int n, m, r[N], lim = 1, l;
struct com{ //结构体实现虚数坐标轴
double x, y;
com(double xx = 0, double yy = 0){
x = xx; y = yy;
}
}a[N], b[N];
// 虚数加减乘法的实现
com operator + (com a, com b) {return com(a.x + b.x, a.y + b.y);}
com operator - (com a, com b) {return com(a.x - b.x, a.y - b.y);}
com operator * (com a, com b) {return com(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);}

void fft(com *x, int type){

// 由蝶形运算的下标位置对应关系
for(int i = 0; i < lim; ++ i){
if(r[i] > i) swap(x[i], x[r[i]]);
}

// 枚举每次折中下标
for(int mid = 1; mid < lim; mid <<= 1){
com k(cos(pi / mid), type * sin(pi / mid)) ;
// 幂次方
for(int R = mid << 1, j = 0; j < lim; j += R){
com w(1, 0);
// 幂次方的基底
for(int i = 0; i < mid; ++ i, w = w * k){
com aa = x[j + i], bb = w * x[j + i + mid];

// 反向递推
x[i + j] = aa + bb;
x[i + j + mid] = aa - bb;
}
}
}
}
int main(){
cin >> n >> m;
for(int i = 0; i <= n; ++ i) cin >> a[i].x;
for(int i = 0; i <= m; ++ i) cin >> b[i].x;

//由基2的FFT原理,我们构造一个最小2的幂次
while(lim <= n + m) lim <<= 1, l ++;


// 由蝶形运算法,我们求出每个下标序列二进制的倒序
// 该序列位置的时间域可以求出其二进制倒序位置的频域元素值,IDFT同理也可求出
for(int i = 0; i < lim; ++ i)
r[i] = (r[i >> 1] >> 1 | (i & 1) << (l - 1));

fft(a, 1); // 求出 A 多项式的DFT
fft(b, 1); // 求出 B 多项式的DFT

// 求出 A B 多项式在频域的相乘
for(int i = 0; i <= lim; ++ i) a[i] = a[i] * b[i];

// 求出 A 多项式的IDFT
fft(a, -1);

for(int i = 0; i <= n + m; ++ i) {
int xx = a[i].x / lim + 0.5;
cout << xx << ' ';
}
}
上一篇:
划水
下一篇:
牛客暑期多校训练营9