大力推荐博客:

傅里叶变换(FFT)学习笔记

一、多项式乘法:

我们要明白的是:

FFT利用分治,处理多项式乘法,达到O(nlogn)的复杂度。(虽然常数大)

FFT=DFT+IDFT

DFT:

本质是把多项式的系数表达转化为点值表达。因为点值表达,y可以直接相乘。点值表达下相乘的复杂度是O(n)的。

我们分别对两个多项式求x为$\omega_n^i$时的y值。

然后可以O(n)求出乘积多项式x为$\omega_n^i$时的y值。

求法:

把F(x)奇偶分类。

$FL(x)=a_0+a_2x+...+a_{n-2}x^{n/2-1}$

$FR(x)=a_1+a_3x+...+a_{n-1}x^{n/2-1}$

$F(x)=FL(x^2)+xFR(x^2)$

带入那些神奇的单位根之后,
发现有:

$0<=k<n/2$

$F(\omega_n^k)=Fl(\omega_{n/2}^k)+\omega_{n}^kFR(\omega_{n/2}^k)$

$F(\omega_n^{k+n/2})=Fl(\omega_{n/2}^k)-\omega_{n}^kFR(\omega_{n/2}^k)$

我们只要知道Fl、FR多项式在那n/2个位置的点值,那么就可以知道F那n个位置的点值了。

分治就可以处理出来。

IDFT:

经过一系列矩阵的运算之后,,,,

可以得到:

$b_k=[(ω_n^{-k})^0y_0+(ω_n^{-k})^1y_1+(ω_n^{-k})^2y_2+...+(ω_n^k)^{n-1}y_{n-1}]/n$

可以把y当做系数,

只要知道,当x是一系列w的时候,值是多少。

那么就求出来了$b_k$

FFT再写一遍。

注意这里带入的是$ω_n^{-k}$

开始的$tmp$有所不同

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;x=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=1e6+;
const double Pi=acos(-);
struct node{
double x,y;
node(){}
node(double xx,double yy){x=xx,y=yy;}
node operator +(const node &b){
return node(x+b.x,y+b.y);
}
node operator -(const node &b){
return node(x-b.x,y-b.y);
}
node operator *(const node &b){
return node(x*b.x-y*b.y,x*b.y+y*b.x);
}
}a[*N],b[*N];
int n,m;
int r[*N];
void FFT(node *f,short op){
for(reg i=;i<n;++i){
if(i<r[i]){
node tmp=f[i];
f[i]=f[r[i]];
f[r[i]]=tmp;
}
}
for(reg p=;p<=n;p<<=){
int len=p/;
node tmp(cos(Pi/len),op*sin(Pi/len));
for(reg k=;k<n;k+=p){
node buf(,);
for(reg l=k;l<k+len;++l){
node tt=buf*f[l+len];
f[l+len]=f[l]-tt;
f[l]=f[l]+tt;
buf=buf*tmp;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(reg i=;i<=n;++i) scanf("%lf",&a[i].x);
for(reg i=;i<=m;++i) scanf("%lf",&b[i].x);
for(m=n+m,n=;n<=m;n<<=);
for(reg i=;i<n;++i){
r[i]=r[i>>]>>|((i&)?n>>:);
}
FFT(a,);FFT(b,);
for(reg i=;i<n;++i) b[i]=a[i]*b[i];
FFT(b,-);
for(reg i=;i<=m;++i) printf("%.0lf ",fabs(b[i].x)/n);
return ;
} }
int main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2018/11/21 8:05:13
*/

多项式乘法

关键点就是在于,用了单位根这个东西,可以避免平方、避免爆long long 以及精度损失的情况下,再利用乘法分配律,可以O(nlogn)得到多项式的点值表达。

例题:

P3338 [ZJOI2014]力

思路:要用FFT,必然要化成多项式卷积的形式

即形如:$h[j]=\sum_{i=0}^j f[i]*g[j-i]$

这样的话,我们把f,g分别作为两个多项式的系数,那么,发现,h[j]的值,恰好是f,g两个多项式乘积得到的多项式的第j+1项的系数。(考虑次数j是怎么来的)

就可以FFT优化这个n^2的算式了。

对于这个题:

令$f[i]=q[i]$,$g[i]=\frac{1}{i*i}$

特别的;有$g[0]=0$

则有$E[j]=\sum_{i=0}^jf[i]*g[j-i]-\sum_{i=j}^nf[i]*g[i-j]$

我们可以分开算,

后面的减法部分类似一个后缀,把$f$数组$reverse$一下,就变成了前缀了。$g$数组不用,因为距离要保持这样。

于是;

$E[j]=\sum_{i=0}^jf[i]*g[j-i]-\sum_{i=0}^{n-j}f'[i]*g[n-j-i]$

两次$FFT$即可

值得注意的是:

1.g数组赋值的时候,i*i可能会爆int,导致精度误差。所以,写成1/i/i比1/(i*i)要好得多。(30pts->100pts)

2.乘积多项式一定要n+n项都算出来,因为最后的插值和每一项的点值都有关系。即使我们只关心前n项。

代码:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;x=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=+;
const double Pi=acos(-);
struct node{
double x,y;
node(){}
node(double xx,double yy){
x=xx;y=yy;
}
}f[*N],g[*N],h[*N];
double q[*N];
int r[*N];
int n,m;
node operator+(const node &a,const node &b){
return node(a.x+b.x,a.y+b.y);
}
node operator-(const node &a,const node &b){
return node(a.x-b.x,a.y-b.y);
}
node operator*(const node &a,const node &b){
return node(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
void FFT(node *f,short op){
for(reg i=;i<n;++i){
if(i<r[i]){
node tmp=f[i];
f[i]=f[r[i]];
f[r[i]]=tmp;
}
}
for(reg p=;p<=n;p<<=){
int len=p/;
node tmp=node(cos(Pi/len),op*sin(Pi/len));
for(reg k=;k<n;k+=p){
node buf=node(,);
for(reg l=k;l<k+len;++l){
node tt=buf*f[l+len];
f[l+len]=f[l]-tt;
f[l]=f[l]+tt;
buf=buf*tmp;
}
}
}
}
int main(){
scanf("%d",&m);
for(reg i=;i<=m;++i){
scanf("%lf",&q[i]);
if(i)g[i]=node((double)/(double)i/(double)i,);
}
for(n=;n<=*m;n<<=);
//cout<<" nn "<<n<<endl;
for(reg i=;i<n;++i){
f[i]=node(q[i],);
//cout<<f[i].x<<" ";
} //g[0]=node(0,0);
for(reg i=;i<n;++i){
r[i]=(r[i>>]>>)|((i&)?(n>>):);
} FFT(f,);
FFT(g,);
for(reg i=;i<n;++i) f[i]=g[i]*f[i];
FFT(f,-); reverse(q+,q+n);
for(reg i=;i<n;++i){
h[i]=node(q[i],);
}
FFT(h,);
for(reg i=;i<n;++i) h[i]=h[i]*g[i];
FFT(h,-); for(reg i=;i<=m;++i){
node ans=f[i]-h[n-i];
printf("%lf\n",ans.x/n);
}
return ;
} }
int main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2018/11/21 10:17:15
*/

FFT优化高精乘法:

把数字看成系数,把10^k看做是x^k,那么就可以得到多项式。

这两个多项式相乘,得到的多项式,各个系数通过进位变成个位数之后,直接输出系数即可。

值得注意的是:

浮点数四舍五入赋值:

$a=floor(b+0.5);$

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
char ch;x=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=+;
const double Pi=acos(-);
struct node{
double x,y;
node(){}
node(double xx,double yy){
x=xx;y=yy;
}
}a[*N],b[*N];
char p[N],q[N];
int c[*N];
int n,m;
int r[*N];
node operator+(node a,node b){
return node(a.x+b.x,a.y+b.y);
}
node operator-(node a,node b){
return node(a.x-b.x,a.y-b.y);
}
node operator*(node a,node b){
return node(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
void FFT(node *f,short op){
for(reg i=;i<n;++i){
if(i<r[i]){
node tmp=f[i];
f[i]=f[r[i]];
f[r[i]]=tmp;
}
}
for(reg p=;p<=n;p<<=){
int len=p/;
node tmp=node(cos(Pi/len),op*sin(Pi/len));
for(reg k=;k<n;k+=p){
node buf=node(,);
for(reg l=k;l<k+len;++l){
node tt=buf*f[l+len];
f[l+len]=f[l]-tt;
f[l]=f[l]+tt;
buf=buf*tmp;
}
}
}
}
int main(){
scanf("%d",&m);
scanf("%s",p);scanf("%s",q);
for(reg i=;i<m;++i){
a[m-i-].x=p[i]-'';
b[m-i-].x=q[i]-'';
}
for(m=m+m,n=;n<m;n<<=);
for(reg i=;i<n;++i){
r[i]=r[i>>]>>|((i&)?n>>:);
}
FFT(a,);FFT(b,); for(reg i=;i<n;++i) b[i]=a[i]*b[i];
FFT(b,-);
for(reg i=;i<n;++i){
c[i]=floor(b[i].x/n+0.5);
} int x=;
for(reg i=;i<n;++i){
c[i]+=x;
x=(int)c[i]/;
c[i]%=;
}
while(c[n-]==&&n>=) --n;
for(reg i=n-;i>=;--i){
printf("%d",c[i]);
}
return ;
} }
int main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2018/11/21 16:30:14
*/

FFT高精

[学习笔记]FFT——快速傅里叶变换的更多相关文章

  1. 【学习笔记】快速傅里叶变换(FFT)

    [学习笔记]快速傅里叶变换 学习之前先看懂这个 浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理--gzy hhh开个玩笑. 讲一下\(FFT\) ...

  2. [学习笔记]NTT——快速数论变换

    先要学会FFT[学习笔记]FFT——快速傅里叶变换 一.简介 FFT会爆精度.而且浮点数相乘常数比取模还大. 然后NTT横空出世了 虽然单位根是个好东西.但是,我们还有更好的东西 我们先选择一个模数, ...

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

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

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

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

  5. Django RF:学习笔记(8)——快速开始

    Django RF:学习笔记(8)——快速开始 安装配置 1.使用Pip安装Django REST Framework: pip install djangorestframework 2.在Sett ...

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

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

  7. 「算法笔记」快速傅里叶变换(FFT)

    一.引入 首先,定义多项式的形式为 \(f(x)=\sum_{i=0}^n a_ix^i\),其中 \(a_i\) 为系数,\(n\) 为次数,这种表示方法称为"系数表示法",一个 ...

  8. [学习笔记]FWT——快速沃尔什变换

    解决涉及子集配凑的卷积问题 一.介绍 1.基本用法 FWT快速沃尔什变换学习笔记 就是解决一类问题: $f[k]=\sum_{i\oplus j=k}a[i]*b[j]$ 基本思想和FFT类似. 首先 ...

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

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

随机推荐

  1. apache+php开发环境搭建步骤

    apache 卸载apache服务命令:sc delete apache 1.在D盘下面新建文件夹php7 2.解压apache到php7文件夹下面 3.修改配置文件 4.安装apache服务C:\w ...

  2. hdu1874畅通工程续(floyd)

    畅通工程续 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  3. UE4蓝图小记

    http://www.element3ds.com/forum.php?mod=viewthread&tid=76930&page=1&authorid=104414 http ...

  4. 关于java获取网页内容

    最近项目需求,做一些新闻站点的爬取工作.1.简单的jsoup爬取,静态页面形式: String url="a.atimo.cn";//静态页面链接地址Document doc = ...

  5. ubuntu networking 与 network-manager

    刚遇到的坑,因为操作不当导致网络中断,于是手动配置了/etc/network/interfaces , 修复了系统之后发现ubuntu-desktop中的有线链接不见了,百度了一下说是networki ...

  6. 1.linux环境配置

    首先说一下,这里是虚拟机环境. 1.用vbox安装centos6.8-mini 注意不要使用复制的方式安装,复制的虚拟机网络不通 安装如下: 主机 ip 角色 内存 hadoop1 192.168.0 ...

  7. iostat lsof

    转至:http://www.51testing.com/html/48/202848-242043.html 命令总结: 1. top/vmstat 发现 wa%过高,vmstat b >1: ...

  8. Linux 应用笔记

    Linux 应用笔记 Linux 应用笔记 小书匠 Raspberry Pi 常用命令 CentOs Raspberry Ubuntu python 实用教程 Vim 权限问题 内存分配 shell ...

  9. Python3.5在Windows7环境下Scrapy库的安装

    Python3.5在Windows7环境下Scrapy库的安装 忙活了一下午,总算是把Scrapy库给装完了,记下来给需要帮助的人 首先安装的环境:Windows7 64位 Python的版本是:3. ...

  10. 软件工程 part4 评价3作品

    作品1 抢答器 地址: https://modao.cc/app/ylGTXobcMU7ePNi6tY53gG4iraLl0md评价: 挺好玩,但是字体大小是个缺陷,简单大方. 作品2:连连看 软件工 ...