【学习】快速傅里叶变化(FFT)
来源:互联网 发布:迭代器java 线程安全 编辑:程序博客网 时间:2024/06/04 20:12
知识点
声明:以下内容来自于goodqt的课件
复数
形如
在复数域 C 中定义如下运算:
可以证明,当 z ∈ R 时,这里的定义和之前我们在实数域中给出的定义完全等价。
可以证明,在复数域中,下式依然成立:
令
对任意复数
其中
把复数
注意到
所以在复平面中复数加减按平行四边形法则,复数乘除为模乘除、辐角加减。
单位根
考虑方程
有
几何意义非常明显,它们被称为 n 次单位根。
多项式
给出 n − 1 次多项式有多种方法,最常见的是给出每一项前面的系数,即:
还有其他的方法,即给出 n 个两两不同的位置处 f(x) 的函数值。
显然如果要做乘法,第一个的复杂度需要
快速傅立叶变换是用
快速傅立叶变换
我们首先要把
然后我们取“
分别拆出奇数项和偶数项,直接递归即可。时间复杂度
快速傅立叶逆变换
常数
虽然复杂度没啥问题,但是递归版的快速傅立叶变换常数还是非常大的,所以可能需要优化常数。
考虑分治的过程,不停的拆分奇数项和偶数项,所以我们直接把标号全部倒置,从低位开始不停的合并
代码详解
递归版
#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <cmath>using namespace std;const double pi = acos(-1);//定义复数类型struct Com{ double x, y; Com(double X = 0, double Y = 0){x = X, y = Y;} Com operator + (const Com &t) const{return Com(x+t.x, y+t.y);} Com operator - (const Com &t) const{return Com(x-t.x, y-t.y);} Com operator * (const Com &t) const{return Com(x*t.x - y*t.y, y*t.x + x*t.y);}};//A一开始是系数数组,在调用完函数后,会变成记录答案的数组,答案记录在实部void fft(Com *A, int n, int f){ //仅有一个数的时候直接返回,这时的系数恰好是答案 if(n == 1) return; int m = n/2; Com C1[m], C2[m]; //分奇偶划分,递归 for(int i = 0; i < m; i ++) C1[i] = A[i*2], C2[i] = A[i*2+1]; fft(C1, m, f), fft(C2, m, f); //k等与0的时候的单位元,k等于1的时候的数值 Com t1(1, 0), t2(cos(pi/m), f*sin(pi/m)); //当i大于m时,$e^{i\pi}=-1$,所以后面的项顺便算出来 for(int i = 0; i < m; i ++){ A[i] = C1[i] + t1 * C2[i]; A[i+m] = C1[i] - t1 * C2[i]; //让k增大 t1 = t1 * t2; }}Com a[300010], b[300010];int main(){ int n, m; scanf("%d%d", &n, &m); for(int i = 0; i <= n; i ++) scanf("%lf", &a[i].x); for(int i = 0; i <= m; i ++) scanf("%lf", &b[i].x); //为了容纳答案的长度 m += n; //最多有n+m+1项,"n <= m" '=' 不能省略 for(n = 1; n <= m; n <<= 1); fft(a, n, 1); fft(b, n, 1); for(int i = 0; i < n; i ++) a[i] = a[i] * b[i]; fft(a, n, -1); //有浮点误差,+0.5消除 for(int i = 0; i <= m; i ++) printf("%d ", int((a[i].x/n+0.5))); return 0;}
非递归版
//与上边一样的就不打了#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <cmath>using namespace std;const int maxn = 300010;const double pi = acos(-1);int Bit, rx[maxn];struct Com{ double x, y; Com(double X = 0, double Y = 0){x = X, y = Y;} Com(const Com &t){x = t.x, y = t.y;} Com operator + (const Com &t) const{return Com(x+t.x, y+t.y);} Com operator - (const Com &t) const{return Com(x-t.x, y-t.y);} Com operator * (const Com &t) const{return Com(x*t.x - y*t.y, y*t.x + x*t.y);}};void fft(Com *A, int n, int f){ //预处理二进制翻转之后的值是多少,只会执行一次 //原理:100111 进行反转,我们得到 10011 的翻转 110010,右移一位,空出最高位,把之前的那个补上即可 if(rx[n-1] != n-1) for(int i = 0; i < n; i ++) rx[i] = (rx[i>>1]>>1) | ((i&1) << (Bit-1)); //进行位数交换,只在有特定条件的时候进行 for(int i = 0; i < n; i ++) if(i < rx[i]) swap(A[i], A[rx[i]]); //长度 -> 开头 -> 每一项 for(int i = 1; i < n; i <<= 1){ const Com Base(cos(pi/i), f*sin(pi/i)); for(int j = 0; j < n; j += i << 1){ Com mi(1, 0); for(int k = 0; k < i; k ++, mi = mi * Base){ Com x = A[j+k], y = A[i+j+k] * mi; //这一句等价于:当i大于m时,$e^{i\pi}=-1$,所以后面的项顺便算出来 A[j+k] = x+y, A[i+j+k] = x-y; } } }}Com a[maxn], b[maxn];int main(){ int n, m; scanf("%d%d", &n, &m); for(int i = 0; i <= n; i ++) scanf("%lf", &a[i].x); for(int i = 0; i <= m; i ++) scanf("%lf", &b[i].x); m += n; for(n = 1; n <= m; n <<= 1, Bit ++); fft(a, n, 1); fft(b, n, 1); for(int i = 0; i <= n; i ++) a[i] = a[i] * b[i]; fft(a, n, -1); for(int i = 0; i <= m; i ++) printf("%d ", int((a[i].x/n+0.5))); puts(""); return 0;}
练习
1. BZOJ 2179 FFT快速傅立叶
2. BZOJ 2194 快速傅立叶之二
3. BZOJ 3527 [Zjoi2014]力
0 0
- 【学习】快速傅里叶变化(FFT)
- 模板,FFT 快速傅里叶变化
- DiscreteFourierTransform API 及FFT(快速傅里叶变化)总结
- DiscreteFourierTransform API 及FFT(快速傅里叶变化)总结
- FFT快速傅里叶模板
- 快速傅里叶(FFT)
- 快速傅里叶(FFT)题表
- FFT快速傅里叶
- FFT 变化
- 快速傅里叶变换 FFT 学习小记
- [FFT] 快速傅里叶变换学习笔记
- FFT,快速傅里叶变换学习笔记
- [学习笔记]快速傅里叶变换 FFT
- bzoj 2179 FFT快速傅里叶
- FFT(快速傅里叶) c语言版
- FFT(快速傅里叶变换)算法学习笔记
- FFT—快速傅里叶变换学习小记
- M.Determine_die_pitch_fft_correlation.hdev相关学习(fft变化)
- Java中的深浅拷贝
- 创建数据库的表空间
- 工作中遇到的问题
- 名企笔试:蘑菇街2017校招笔试题(修理桌子问题)2017-03-09 算法爱好者
- C++简单工厂模式
- 【学习】快速傅里叶变化(FFT)
- 性能优化(CSS优化)
- LeetCode 561. Array Partition I
- Problem J: Color Circle----dfs
- linux 下vim编辑器使用方法及技巧
- 【BZOJ】1922 [Sdoi2010]大陆争霸 带限制最短路
- 官网方法提升AndroidStudio编译速度
- linuxc操作数据库
- 【数据结构基础】数的层次遍历