怎么说呢。。。这次的代码码风有点。。。

从这篇博客开始,我终于会用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

模板题

洛谷P3803 【模板】多项式乘法(FFT)

 #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;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(快速傅里叶变换)摘要的更多相关文章

  1. FFT 快速傅里叶变换 学习笔记

    FFT 快速傅里叶变换 前言 lmc,ikka,attack等众多大佬都没教会的我终于要自己填坑了. 又是机房里最后一个学fft的人 早背过圆周率50位填坑了 用处 多项式乘法 卷积 \(g(x)=a ...

  2. CQOI2018 九连环 打表找规律 fft快速傅里叶变换

    题面: CQOI2018九连环 分析: 个人认为这道题没有什么价值,纯粹是为了考算法而考算法. 对于小数据我们可以直接爆搜打表,打表出来我们可以观察规律. f[1~10]: 1 2 5 10 21 4 ...

  3. 「学习笔记」FFT 快速傅里叶变换

    目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...

  4. FFT —— 快速傅里叶变换

    问题: 已知A[], B[], 求C[],使: 定义C是A,B的卷积,例如多项式乘法等. 朴素做法是按照定义枚举i和j,但这样时间复杂度是O(n2). 能不能使时间复杂度降下来呢? 点值表示法: 我们 ...

  5. [C++] 频谱图中 FFT快速傅里叶变换C++实现

    在项目中,需要画波形频谱图,因此进行查找,不是很懂相关知识,下列代码主要是针对这篇文章. http://blog.csdn.net/xcgspring/article/details/4749075 ...

  6. matlab中fft快速傅里叶变换

    视频来源:https://www.bilibili.com/video/av51932171?t=628. 博文来源:https://ww2.mathworks.cn/help/matlab/ref/ ...

  7. FFT快速傅里叶变换算法

    1.FFT算法概要: FFT(Fast Fourier Transformation)是离散傅氏变换(DFT)的快速算法.即为快速傅氏变换.它是根据离散傅氏变换的奇.偶.虚.实等特性,对离散傅立叶变换 ...

  8. FFT快速傅里叶变换

    FFT太玄幻了,不过我要先膜拜HQM,实在太强了 1.多项式 1)多项式的定义 在数学中,由若干个单项式相加组成的代数式叫做多项式.多项式中的每个单项式叫做多项式的项,这些单项式中的最高项次数,就是这 ...

  9. [学习笔记]FFT——快速傅里叶变换

    大力推荐博客: 傅里叶变换(FFT)学习笔记 一.多项式乘法: 我们要明白的是: FFT利用分治,处理多项式乘法,达到O(nlogn)的复杂度.(虽然常数大) FFT=DFT+IDFT DFT: 本质 ...

  10. FFT(快速傅里叶变换)

    学习了FFT用来求多项式的乘法,看了算导上的介绍,上面讲的非常明白,概括一下FFT的原理就是,我们在计算多项式的乘法时,如果暴力模拟的话是n^2 复杂度的,就像小学学的竖式乘法一样,比如一个n位数乘上 ...

随机推荐

  1. A brief introduction of myself

    来到博客园的第二天.晚上天色墨蓝,余热未退,北京这里的秋风干松爽利,和小组的伙伴们吃了一顿饱饱的香锅,按照咱们国人的传统,所有的事情在饭桌上都解决了,包括队员之间相互的认识和短期任务的分配以及后期的团 ...

  2. <转>HTML、CSS、font-family:中文字体的英文名称

    宋体 SimSun 黑体 SimHei 微软雅黑 Microsoft YaHei 微软正黑体 Microsoft JhengHei 新宋体 NSimSun 新细明体 PMingLiU 细明体 Ming ...

  3. MarkDown to PDF

    前面随便说说 之前在 windows 上一直习惯用 cmdmarkdown 把要写的东西写下来,再通过 typora 转成 pdf:现在很多时候在用 Ubuntu,需要把写完的作业转成 pdf 交上去 ...

  4. [开源中国]Windows 10 全球市场份额正式超越 Windows 7

    Windows 10 全球市场份额正式超越 Windows 7 全球知名科技数据调查公司 Netmarketshare 昨天发布了2018年12月份最新的桌面操作系统份额报告.对于微软来说,这是历史一 ...

  5. 阿里云视频直播PHP-SDK接入教程

    阿里云视频直播PHP-SDK接入教程 阿里云 视频直播 配置 及 PHP-SDK 接入教程        准备工作        域名管理        配置鉴权        地址生成器及DEMO演 ...

  6. javascript extend

    interface Date{ addHours(h:number); addMinutes(m:number); format(str):string } interface String{ tri ...

  7. Delphi中Form的position属性与代码自定义窗体位置

    通过Form的Position属性可设置窗体的初始位置,如选择DesktopCenter为桌面中心,ScreenCenter为屏幕中心,等等. 这个属性在很多时候简化了程序代码. 但是,如果设置了po ...

  8. 登录窗口不是系统主窗口 但又需要最先显示 用delphi怎么编写代

    主窗体FormShow事件(主窗体为Form1为例,Form2为登陆窗体)   procedure TForm1.FormShow(Sender: TObject); begin if Form2.S ...

  9. Shell命令——文件目录

    Linux只有一个文件系统树,不同的硬件设备可以挂载在不同目录下. 文件或目录有两种表示方式:  - 绝对路径:从根目录”/”开始  - 相对路径:从工作目录开始,使用”..”指向父目录,”.”指向当 ...

  10. MT【102】一个常见的因式分解公式

    解答: $x^3+y^3+1-3xy=(x+y+1)(x^2+y^2+1+xy-x-y)=$ $(x+y+1)(x^2+y^2+1+xy-x-y)=$ $\frac{1}{2}(x+y+1)[(x-y ...