FFT快速傅里叶变换
FFT太玄幻了,不过我要先膜拜HQM,实在太强了
1.多项式
1)多项式的定义
在数学中,由若干个单项式相加组成的代数式叫做多项式。多项式中的每个单项式叫做多项式的项,这些单项式中的最高项次数,就是这个多项式的次数。其中多项式中不含字母的项叫做常数项。
2)多项式的表达
我们可以用一些不同的表达方式来表示一个多项式
\]
系数表达:
可以用一个n+1维的向量来表示
\]
点值表达:
我们要选取任意\(n+1\)个点值\(x_0,...,x_n\)求出它的\(f(x_i)\),得到$${(x_i,f(x_i)):0<=i<=n,i∈Z} $$
我们可以把\(n\)次多项式\(A(x)\)看作一个函数,那么它可以用平面直角坐标系上的n个点\((x_1,y_1),(x_2,y_2),...,(x_n,y_n)\)来确定。这里我们可以直观理解一下:两个点我们可以轻易确定一条直线,同时我们依然可以由三个点确定一条抛物线,这个是可以证明的,MYH巨佬给我们讲过,但是我太菜了,实在没有听懂。回到上面,我们把这n个点代入\(A(x)\),我们就可以得到一个\(n\)元一次方程组,然后通过高斯消元就可以确定这个多项式。
系数表达法的多项式乘法时间复杂度显然是\(O(n^2)\)的,但是点值表达法的多项式的乘法的时间复杂度却是\(O(n\))的(两个多项式的每一个点的横坐标都相等)。那我们就会希望可以利用点值表达法的这一优势来快速地进行多项式乘法。但是我们平时使用的都是系数表示法,想要利用这个优势,就要快速地将系数表达法转化为点值表达法。这里我们就要用FFT了。
2.复数
1)复数的定义
我们把形如\(a+bi\)(a,b均为实数)的数称为复数,其中\(a\)称为实部,\(b\)称为虚部,i称为虚数单位,也就是说\(i^2=-1\),或者说\(i=\sqrt{-1}\)。当虚部等于零时,这个复数可以视为实数;当z的虚部不等于零时,实部等于零时,常称z为纯虚数。复数域是实数域的代数闭包,也即任何复系数多项式在复数域中总有根。 复数是由意大利米兰学者卡当在十六世纪首次引入,经过达朗贝尔、棣莫弗、欧拉、高斯等人的工作,此概念逐渐为数学家所接受。
2)复数的四则运算
复数的加法
设\(z_1=a+bi,z_2=c+di\),那么:$$z_1\pm z_2=(a+c)+(b+d)i$$
复数的乘法
设\(z_1=a+bi,z_2=c+di\),那么:$$z_1\times z_2=(ac-bd)+(ad+bc)i$$
一个奇奇怪怪的东西
\]
单位复数根
定义
如果\(\omega^n=1\),那么我们称\(\omega\)为\(n\)次单位根。
单位根的值
事实上,在这里:$$\omega_k=e^\frac{2\pi ik}{n}=cos\frac{2\pi k}{n}+isin\frac{2\pi k}{n}$$
这个可以用欧拉恒等式\((e^{i\pi}+1=0)\)来轻易证明。
消去引理
\]
证明:
\]
折半引理
如果n是大于0的一个偶数,那么前\frac{n}{2}个\(n\)次单位根的平方的集合等于后\frac{n}{2}个n次单位根的平方的集合。
证明:对于任意的k<\frac{n}{2},有:
\]
因此,$$(\omega_n{k+\frac{n}{2}})2=(\omega_nk)2$$
复数类
struct cmplx{
double real,imag;
inline cmplx(double x=0,double y=0){real=x,imag=y;}
inline cmplx operator +(const int b){return cmplx(real+b,imag);}
inline cmplx operator -(const int b){return cmplx(real-b,imag);}
inline cmplx operator *(const int b){return cmplx(real*b,imag*b);}
inline cmplx operator /(const int b){return cmplx(real/b,imag/b);}
inline cmplx operator+=(const int b){*this=*this+b;return *this;}
inline cmplx operator/=(const int b){*this=*this/b;return *this;}
inline cmplx operator +(const cmplx b){return cmplx(real+b.real,imag+b.imag);}
inline cmplx operator -(const cmplx b){return cmplx(real-b.real,imag-b.imag);}
inline cmplx operator *(const cmplx b){return cmplx(real*b.real-imag*b.imag,real*b.imag+imag*b.real);}
inline cmplx operator+=(const cmplx b){*this=*this+b;return *this;}
inline cmplx operator*=(const cmplx b){*this=*this*b;return *this;}
};
FFT(fast Fourier transform)
我们正式开始我们的FFT之旅。
FFT就是用来快速求傅里叶变换
证明
终于切入正题了。
DFT就是将一个多项式的系数表达法转化为点值表达法。
具体操作:
对于n-1(2|n)次多项式A(x),我们有:
\]
令$$A{[1]}=a_0+a_2x2+...+a_{n-2}x^{\frac{n}{2}-1}$$
\]
则$$A(x)=A{[1]}(x2)+xA{[2]}(x2)$$
把\(\omega_n^k(k<\frac{n}{2})\)代入可得到:$$A(\omega_nk)=A{[1]}((\omega_nk)2)+\omega_nkA{[2]}((\omega_nk)2)$$
由折半引理我们会发现\((\omega_n^k)^2=(\omega_n^{k+\frac{n}{2}})^2\)
易证\(\omega_n^k=-\omega_n^{k+\frac{n}{2}}\)
所以$$A(\omega_n{k+\frac{n}{2}})=A{[1]}((\omega_nk)2)-\omega_nkA{[2]}((\omega_nk)2)$$
我们会发现这两个式子只有常数项不同
那么当我们在求第一个式子的时候,我们可以直接在O(1)的时间内同时得到第二个式子的值
然后就这样递归下去,就可以得到A(x)的点值表达式了
这里先埋个坑吧,证明真的不想肝啊【绝望脸】
直接上代码吧
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const double pi=acos(-1);
struct cmplx{
double real,imag;
inline cmplx(double x=0,double y=0){real=x,imag=y;}
inline cmplx operator +(const int b){return cmplx(real+b,imag);}
inline cmplx operator -(const int b){return cmplx(real-b,imag);}
inline cmplx operator *(const int b){return cmplx(real*b,imag*b);}
inline cmplx operator /(const int b){return cmplx(real/b,imag/b);}
inline cmplx operator+=(const int b){*this=*this+b;return *this;}
inline cmplx operator/=(const int b){*this=*this/b;return *this;}
inline cmplx operator +(const cmplx b){return cmplx(real+b.real,imag+b.imag);}
inline cmplx operator -(const cmplx b){return cmplx(real-b.real,imag-b.imag);}
inline cmplx operator *(const cmplx b){return cmplx(real*b.real-imag*b.imag,real*b.imag+imag*b.real);}
inline cmplx operator+=(const cmplx b){*this=*this+b;return *this;}
inline cmplx operator*=(const cmplx b){*this=*this*b;return *this;}
};
cmplx a[10000001],b[10000001];
int n,m,limit,res[10000001];
void fft(cmplx *a,int t){
for(int i=0;i<n;i++){
if(i<res[i])swap(a[i],a[res[i]]);
}
for(int i=1;i<n;i<<=1){
cmplx wn(cos(pi/i),t*sin(pi/i));
for(int p=i<<1,j=0;j<n;j+=p){
cmplx w(1,0);
for(int k=0;k<i;k++,w*=wn){
cmplx x=a[j+k],y=w*a[j+k+i];
a[j+k]=x+y;a[j+k+i]=x-y;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++){
scanf("%lf",&a[i].real);
}
for(int i=0;i<=m;i++){
scanf("%lf",&b[i].real);
}
m+=n;
for(n=1;n<=m;n<<=1)limit++;
for(int i=0;i<n;i++){
res[i]=((res[i>>1]>>1)|(i&1)<<(limit-1));
}
fft(a,1);
fft(b,1);
for(int i=0;i<=n;i++)a[i]*=b[i];
fft(a,-1);
for(int i=0;i<=m;i++){
printf("%d ",int(a[i].real/n+0.5));
}
return 0;
}
FFT快速傅里叶变换的更多相关文章
- FFT 快速傅里叶变换 学习笔记
FFT 快速傅里叶变换 前言 lmc,ikka,attack等众多大佬都没教会的我终于要自己填坑了. 又是机房里最后一个学fft的人 早背过圆周率50位填坑了 用处 多项式乘法 卷积 \(g(x)=a ...
- CQOI2018 九连环 打表找规律 fft快速傅里叶变换
题面: CQOI2018九连环 分析: 个人认为这道题没有什么价值,纯粹是为了考算法而考算法. 对于小数据我们可以直接爆搜打表,打表出来我们可以观察规律. f[1~10]: 1 2 5 10 21 4 ...
- 「学习笔记」FFT 快速傅里叶变换
目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...
- FFT —— 快速傅里叶变换
问题: 已知A[], B[], 求C[],使: 定义C是A,B的卷积,例如多项式乘法等. 朴素做法是按照定义枚举i和j,但这样时间复杂度是O(n2). 能不能使时间复杂度降下来呢? 点值表示法: 我们 ...
- [C++] 频谱图中 FFT快速傅里叶变换C++实现
在项目中,需要画波形频谱图,因此进行查找,不是很懂相关知识,下列代码主要是针对这篇文章. http://blog.csdn.net/xcgspring/article/details/4749075 ...
- matlab中fft快速傅里叶变换
视频来源:https://www.bilibili.com/video/av51932171?t=628. 博文来源:https://ww2.mathworks.cn/help/matlab/ref/ ...
- FFT快速傅里叶变换算法
1.FFT算法概要: FFT(Fast Fourier Transformation)是离散傅氏变换(DFT)的快速算法.即为快速傅氏变换.它是根据离散傅氏变换的奇.偶.虚.实等特性,对离散傅立叶变换 ...
- [学习笔记]FFT——快速傅里叶变换
大力推荐博客: 傅里叶变换(FFT)学习笔记 一.多项式乘法: 我们要明白的是: FFT利用分治,处理多项式乘法,达到O(nlogn)的复杂度.(虽然常数大) FFT=DFT+IDFT DFT: 本质 ...
- FFT(快速傅里叶变换)
学习了FFT用来求多项式的乘法,看了算导上的介绍,上面讲的非常明白,概括一下FFT的原理就是,我们在计算多项式的乘法时,如果暴力模拟的话是n^2 复杂度的,就像小学学的竖式乘法一样,比如一个n位数乘上 ...
随机推荐
- 洛谷 P1233 木棍加工
题目描述 一堆木头棍子共有n根,每根棍子的长度和宽度都是已知的.棍子可以被一台机器一个接一个地加工.机器处理一根棍子之前需要准备时间.准备时间是这样定义的: 第一根棍子的准备时间为1分钟: 如果刚处理 ...
- http2及server push
本文主要研究下java9+springboot2+undertow2启用http2及server push maven <parent> <groupId>org.spri ...
- 折半枚举(双向搜索)poj27854 Values whose Sum is 0
4 Values whose Sum is 0 Time Limit: 15000MS Memory Limit: 228000K Total Submissions: 23757 Accep ...
- hdu5922Minimum’s Revenge
Minimum's Revenge Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- PowerDesigner连接Oracle数据库(32位)反向生成物理数据模型
PowerDesigner可以连接Oracle数据库进行反向生成物理数据模型,本文演示操作过程. 环境说明: 1)Windows8.1,Oracle11R2 32位. 2)PowerDesigner1 ...
- python2 'str' object has no attribute 'decode'
'.decode('hex') 上述代码,报错: 'str' object has no attribute 'decode' 查找原因: https://stackoverflow.com/ques ...
- 对socket的理解
要想理解socket,就得先熟悉TCP/IP协议族,TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如 ...
- redis 数据类型Hash
redis的Hash数据类型: hash数据类型 Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象. 创建map: hmset map c & ...
- Clickhouse DDL&DML
(1)添加列: alter table [db.]table_name add column column_name [type] [default_expr] [after name_after] ...
- UIPageViewController 翻页、新手引导--UIScrollView:pagingEnabled
UIPageViewController 翻页.新手引导--UIScrollView:pagingEnabled