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位数乘上 ...
随机推荐
- ACM_排序
除了sort,你还会什么 Time Limit: 1000/500ms (Java/Others) Problem Description: 给出若干人的年龄(1~100之间的整数),把它们按照从小到 ...
- WCF学习笔记(2)-WCF的通讯过程
一.WCF中的ABC 场景:公司让你送一份合同文件,送文件的过程你可以选择的交通方式有打的,地铁或公交. 到了对方公司后,你要找到某负责人,并且要一份收到合同文件的回执和相应文件 要完成这项工作任务主 ...
- 第八届蓝桥杯省赛C/C++ A组第8题 包子凑数
参考了http://blog.csdn.net/y1196645376/article/details/69718192 思路: 数论+完全背包. 实现: #include <iostream& ...
- vscode使用教程(web开发)
1.安装 进入官网下载https://code.visualstudio.com/ 一直下一步就好了,中间可以选择把软件安装在哪个目录. 2.常用插件安装 a. 进入扩展视图界面安装/卸载 a1.快捷 ...
- Android 滚动RecyclerView加载图片时的流畅度优化
实现:使用onScrollStateChanged回调检测滚动状态,并在RecyclerViewAdapter内部设置类似isScrolling的状态值来控制网络图片的加载. 下面是代码举例: // ...
- Indy 编译提示版本不一致问题的解决
1,起因 某delphi程序A使用了Indy9.0.18组件.机器中原本自带老版本的Indy组件9.0.12,后升级到9.0.18,使用一直正常. 某次操作将程序A重新build all了一下,结果提 ...
- Angular——配置模块与运行模块
配置模块 通过config方法实现对模块的配置,AngularJS中的服务大部分都对应一个“provider”,用来执行与对应服务相同的功能或对其进行配置.比如$log.$http.$location ...
- Linux服务器文件权限被改
阿里云买的ubuntu服务器遭受了不明攻击,导致站点访问不了,折腾了很久,才发现是文件的权限被修改了.然后就是一点点的修改,很是麻烦.服务器的安全要重视呢! 1.修改权限 chmod 755 * -R ...
- golang协程——通道channel阻塞
新的一年开始了,不管今天以前发生了什么,向前看,就够了. 说到channel,就一定要说一说线程了.任何实际项目,无论大小,并发是必然存在的.并发的存在,就涉及到线程通信.在当下的开发语言中,线程通讯 ...
- BeanFactory和ApplicationContext
BeanFactory是一个类的通用工厂,可以创建并管理各种类的对象 Bean工厂是Spring框架最核心的接口,它提供了高级Ioc的配置机制.BeanFeactory使管理不同类的Java对象成为可 ...