FFT(快速傅里叶变换)摘要
怎么说呢。。。这次的代码码风有点。。。
从这篇博客开始,我终于会用LATEX了!撒花
注:以下涉及多项式的n均表示多项式的次数
FFT用途
很简单,多项式相乘。
FFT原理
如果暴力乘,复杂度是$O(n^{2})$的,显然不行
所以考虑点值法。点值表示下的多项式相乘是$O(n)$的。这就是DFT(离散傅里叶变换)。
但很显然,随机带入n个值的复杂度是$O(n^{2})$的。
因此我们考虑以下推导。
设$$F(n)=a_{0}+a_{1}x+a_{2}x^{2}+...+a_{n}x^{n}$$那么我们一定可以将其拆成两个函数,即$$G(n)=a_{0}+a_{2}x+a_{4}x^{2}+a_{6}x^{3}+...$$$$H(n)=a_{1}+a_{3}x+a_{5}x^{2}+a_{7}x^{3}+...$$所以$$F(n)=G(x^{2})+xH(x^{2})$$从这个式子我们可以看出,如果我们取这么一个点值集合,使得其中一半的值为所有值的平方,比如${-1,1}$,那么我们就可以使计算规模减半(对于$G(n)$和$H(n)$,我们只需带入其中一半的值即可)。看来这是个很完美的策略。
很不幸,如果我们只在实数中这么思考,那复杂度仍为$O(n^{2})$,可以说没什么用。但这给了我们一个启发,如果每次对函数进行拆分递归并使点值集合规模减半,我们就可以在$O(nlogn)$的复杂度内求出一个多项式的点值表示。毫无疑问,问题的关键是这个点值集合。那是否存在这样一个点值集合呢?实数域当然没有,但复数域有,也就是单位根!(用复数做自变量真的让我很长时间无法接受)至于单位根么,出门右转问百度
当然,既然用了FFT,自然要把多项式项数补成2的幂,具体做法为高次项补零。
递归式FFT的很好打,但常数大,且容易爆栈。因此我们考虑迭代。
易发现,每次拆分多项式的时候,我们总是按下标的奇偶性来拆分。这就导致了一个结果:原多项式经过递归拆分后的新多项式下标是原多项式下标的二进制反转。
- void unit() {
- lg[]=;
- fui(i,,mxle,)lg[i]=lg[i/]+;
- bz[]=;
- fui(i,,mxlg,)bz[i]=bz[i-]<<;
- n=max(n,m);
- n=bz[lg[n]+]-;
- root[].x=;
- root[].x=cos((*PI)/(n+));
- root[].y=sin((*PI)/(n+));
- fui(i,,n,)root[i]=root[i-]*root[]; //求单位根
- frot[]=root[];
- fui(i,,n,)frot[i]=root[n-i+]; //求逆单位根(后面用
- for(int i=; i<=n; i++)fz[i]=(fz[i>>]>>)|((i&)<<(lg[n+]-)) ; //二进制反转
- }
预处理
然后就是FFT了
- void pre_FFT() {
- fui(i,,n,)if(i<fz[i]) {
- swp=fft[i];
- fft[i]=fft[fz[i]];
- fft[fz[i]]=swp;
- }
- }
- void FFT() {
- pre_FFT(); //对原多项式进行二进制反转
- int zl=(n+)>>; //单位根增量
- for(int i=; i<=n; i=(i<<)|,zl>>=) { //每次处理的多项式长度
- for(int j=; j<n; j+=i+) { //每次处理的多项式左端点
- int mid=(j+j+i)>>;
- for(int k1=j,k2=mid+,nw=; k1<=mid; nw+=zl,k1++,k2++) { //对该多项式的点值进行迭代处理
- x1=fft[k2]*root[nw]+fft[k1],x2=fft[k2]*root[nw+((n+)>>)]+fft[k1];
- fft[k1]=x1;
- fft[k2]=x2;
- }
- }
- }
- }
FFT
记住,千万不要用stl里的复数,会被坑死的,强烈建议手打
IFFT(快速傅里叶逆变换)
FFT求出了点值,然后相乘,求出了结果多项式的点值表示。但这不是结果,我们还要将其“翻译”回系数表示。所以就有了IFFT,可以叫其插值。
插值么,具体做法就是对结果求一遍类似FFT的东西,只不过把单位根改成{${{\omega}^{0},{\omega}^{-1},{\omega}^{-2},{\omega}^{-3},...,{\omega}^{-n}}$},即上面预处理的时候求过的那个所谓逆单位根带入即可。证明么,我不会,反正作为一个OIer记结论就好了。具体证明参见自为风月马前卒大佬的博客。
- void IFFT() {
- pre_FFT();
- int zl=(n+)>>;
- for(int i=; i<=n; i=(i<<)|,zl>>=) {
- for(int j=; j<n; j+=i+) {
- int mid=(j+j+i)>>;
- for(int k1=j,k2=mid+,nw=; k1<=mid; nw+=zl,k1++,k2++) {
- x1=fft[k2]*frot[nw]+fft[k1],x2=fft[k2]*frot[nw+((n+)>>)]+fft[k1]; //改成逆单位根带入
- fft[k1]=x1;
- fft[k2]=x2;
- }
- }
- }
- }
IFFT
模板题
- #include<bits/stdc++.h>
- using namespace std;
- #define INF 0x7fffffff
- #define ME 0x7f
- #define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
- #define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
- #define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
- #define fel(i,a) for(register int i=h[a];i;i=b[i].ne)
- #define ll long long
- #define ld double
- #define MEM(a,b) memset(a,b,sizeof(a))
- #define maxn 1000010
- #define mxlg 21
- #define mxle 4194310
- const ld PI=3.1415926535897932384626;
- int n,m,mn;
- ld a0[mxle],b0[mxle];
- ld ans[mxle];
- int lg[mxle],bz[mxlg+];
- int fz[mxle];
- struct Complex{
- ld x,y;
- Complex(){}
- Complex(ld __x,ld __y){x=__x;y=__y;}
- Complex operator *(Complex xx)const{return (Complex){x*xx.x-y*xx.y,x*xx.y+y*xx.x};}
- Complex operator *(ld xx)const{return (Complex){x*xx,y*xx};}
- Complex operator +(Complex xx)const{return (Complex){x+xx.x,y+xx.y};}
- }root[mxle],frot[mxle],a[mxle],b[mxle],fft[mxle],c[mxle],swp,x1,x2;
- template<class T>
- inline T read(T &n){
- n=;int t=;double x=;char ch;
- for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());(ch=='-')?t=-:n=ch-'';
- for(ch=getchar();isdigit(ch);ch=getchar()) n=n*+ch-'';
- if(ch=='.') for(ch=getchar();isdigit(ch);ch=getchar()) n+=(ch-'')/x,x*=;
- return (n*=t);
- }
- template<class T>
- T write(T n){
- if(n<) putchar('-'),n=-n;
- if(n>=) write(n/);putchar(n%+'');return n;
- }
- template<class T>
- T writeln(T n){write(n);putchar('\n');return n;}
- int inc(int &x){int in=(n+)>>;for(;x∈in>>=)x^=in;x|=in;return x;}
- void unit(){
- lg[]=;fui(i,,mxle,)lg[i]=lg[i/]+;
- bz[]=;fui(i,,mxlg,)bz[i]=bz[i-]<<;
- n=max(n,m);n=bz[lg[n]+]-;
- root[].x=;
- root[].x=cos((*PI)/(n+));root[].y=sin((*PI)/(n+));
- fui(i,,n,)root[i]=root[i-]*root[];
- frot[]=root[];
- fui(i,,n,)frot[i]=root[n-i+];
- fui(i,,n,)a[i]=root[]*a0[i];
- fui(i,,n,)b[i]=root[]*b0[i];
- for(int i=;i<=n;i++)fz[i]=(fz[i>>]>>)|((i&)<<(lg[n+]-)) ;
- }
- void pre_FFT(){
- fui(i,,n,)if(i<fz[i]){
- swp=fft[i];fft[i]=fft[fz[i]];fft[fz[i]]=swp;
- }
- }
- void FFT(){
- pre_FFT();int zl=(n+)>>;
- for(int i=;i<=n;i=(i<<)|,zl>>=){
- for(int j=;j<n;j+=i+){
- int mid=(j+j+i)>>;
- for(int k1=j,k2=mid+,nw=;k1<=mid;nw+=zl,k1++,k2++){
- x1=fft[k2]*root[nw]+fft[k1],x2=fft[k2]*root[nw+((n+)>>)]+fft[k1];
- fft[k1]=x1;fft[k2]=x2;
- }
- }
- }
- // pre_FFT();
- }
- void IFFT(){
- pre_FFT();int zl=(n+)>>;
- for(int i=;i<=n;i=(i<<)|,zl>>=){
- for(int j=;j<n;j+=i+){
- int mid=(j+j+i)>>;
- for(int k1=j,k2=mid+,nw=;k1<=mid;nw+=zl,k1++,k2++){
- x1=fft[k2]*frot[nw]+fft[k1],x2=fft[k2]*frot[nw+((n+)>>)]+fft[k1];
- fft[k1]=x1;fft[k2]=x2;
- }
- }
- }
- }
- int main(){
- read(n);read(m);mn=m+n;
- fui(i,,n,)read(a0[i]);fui(i,,m,)read(b0[i]);unit();
- fui(i,,n,)fft[i]=a[i];FFT();fui(i,,n,)a[i]=fft[i];
- fui(i,,n,)fft[i]=b[i];FFT();fui(i,,n,)b[i]=fft[i];
- fui(i,,n,)c[i]=a[i]*b[i];
- fui(i,,n,)fft[i]=c[i];IFFT();fui(i,,n,)c[i]=fft[i];
- fui(i,,mn,)ans[i]=(c[i].x+0.5)/((n+));
- fui(i,,mn,)printf("%.0f ",ans[i]);
- return ;
- }
AC代码
常数太大,导致最后两个点2s多。。。
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太玄幻了,不过我要先膜拜HQM,实在太强了 1.多项式 1)多项式的定义 在数学中,由若干个单项式相加组成的代数式叫做多项式.多项式中的每个单项式叫做多项式的项,这些单项式中的最高项次数,就是这 ...
- [学习笔记]FFT——快速傅里叶变换
大力推荐博客: 傅里叶变换(FFT)学习笔记 一.多项式乘法: 我们要明白的是: FFT利用分治,处理多项式乘法,达到O(nlogn)的复杂度.(虽然常数大) FFT=DFT+IDFT DFT: 本质 ...
- FFT(快速傅里叶变换)
学习了FFT用来求多项式的乘法,看了算导上的介绍,上面讲的非常明白,概括一下FFT的原理就是,我们在计算多项式的乘法时,如果暴力模拟的话是n^2 复杂度的,就像小学学的竖式乘法一样,比如一个n位数乘上 ...
随机推荐
- 组件 --BreadCrumb--面包屑
面包屑组件多用于导航栏,对于大型网站,做面包屑导航栏 .breadcrumb .breadcrumb-item .active:表示现在正处在该页面 效果截图: 代码: <nav> < ...
- Android 图表
今天在群里有人提问Android的图表,在网上搜索了一下,在贴吧中看到的回答,说是Trinea整理的开源项目,不知道是不是博客上的Trinea. 将内容记录如下,以备需要时查看. GraphView ...
- BZOJ3881 Coci2015Divljak(AC自动机+树上差分+树状数组)
建出AC自动机及其fail树,每次给新加入的串在AC自动机上经过的点染色,问题即转化为子树颜色数.显然可以用dfs序转成序列问题树状数组套权值线段树解决,显然过不掉.事实上直接树上差分,按dfs序排序 ...
- 【BZOJ4260】Codechef REBXOR (Trie树)
[BZOJ4260]Codechef REBXOR (Trie树) 题面 BZOJ 题解 两眼题.第一眼不会做,第二眼好简单... 前缀异或和一下,拿\(Trie\)树维护求一个在这个端点以左的最大值 ...
- 用JS制作博客页面背景随滚动渐变的效果
今天颓了一会,用JavaScript给我的博客园博客写了一个页面背景随滚动而渐变的效果,做完之后自我感觉良好-- 下面就以我的博客园博客为例,介绍一下如何制作这个效果! 准备 [x] 申请博客园的JS ...
- BZOJ 百题纪念!
一百题辣! 现在NOI知识点中最基础的那部分已经学完了--这几天发现自己会写SA啊树剖啊可持久化Trie啊之类模板题--还挺开心的-- 逛了两天学长博客之后--BZOJ100题辣--也挺开心的-- 现 ...
- 【字符串算法2】浅谈Manacher算法
[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述 字符串算法2:Manacher算法 问题:给出字符串S(限制见后)求出最 ...
- 上传文件到aws的s3存储
只要有aws-cli客户端就可以上传文件到aws的S3存储.可以在任意机器上.这里以centos为例. 1.安装python.pip. # yum install -y python python-p ...
- js完整教程一 : 基本概念和数组操作
文章提纲 JS相关常识 JS基本概念 实践 总结 JS相关常识 js是一种可以与HTML标记语言混合使用的脚本语言,其编写的程序可以直接在浏览器中解释执行. 一.组成 js是一种专门为网页交互设计的脚 ...
- os.chmod()--更改目录授权权限
用法:os.chmod() 方法用于更改文件或目录的权限. 语法:os.chmod(path, mode) 参数:只需要2个参数,一个是路径,一个是说明路径的模式. path -- 文件名路径或目录路 ...