[学习笔记]FFT——快速傅里叶变换
大力推荐博客:
傅里叶变换(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)得到多项式的点值表达。
例题:
思路:要用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——快速傅里叶变换的更多相关文章
- 【学习笔记】快速傅里叶变换(FFT)
[学习笔记]快速傅里叶变换 学习之前先看懂这个 浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理--gzy hhh开个玩笑. 讲一下\(FFT\) ...
- [学习笔记]NTT——快速数论变换
先要学会FFT[学习笔记]FFT——快速傅里叶变换 一.简介 FFT会爆精度.而且浮点数相乘常数比取模还大. 然后NTT横空出世了 虽然单位根是个好东西.但是,我们还有更好的东西 我们先选择一个模数, ...
- 「学习笔记」FFT 快速傅里叶变换
目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...
- FFT 快速傅里叶变换 学习笔记
FFT 快速傅里叶变换 前言 lmc,ikka,attack等众多大佬都没教会的我终于要自己填坑了. 又是机房里最后一个学fft的人 早背过圆周率50位填坑了 用处 多项式乘法 卷积 \(g(x)=a ...
- Django RF:学习笔记(8)——快速开始
Django RF:学习笔记(8)——快速开始 安装配置 1.使用Pip安装Django REST Framework: pip install djangorestframework 2.在Sett ...
- CQOI2018 九连环 打表找规律 fft快速傅里叶变换
题面: CQOI2018九连环 分析: 个人认为这道题没有什么价值,纯粹是为了考算法而考算法. 对于小数据我们可以直接爆搜打表,打表出来我们可以观察规律. f[1~10]: 1 2 5 10 21 4 ...
- 「算法笔记」快速傅里叶变换(FFT)
一.引入 首先,定义多项式的形式为 \(f(x)=\sum_{i=0}^n a_ix^i\),其中 \(a_i\) 为系数,\(n\) 为次数,这种表示方法称为"系数表示法",一个 ...
- [学习笔记]FWT——快速沃尔什变换
解决涉及子集配凑的卷积问题 一.介绍 1.基本用法 FWT快速沃尔什变换学习笔记 就是解决一类问题: $f[k]=\sum_{i\oplus j=k}a[i]*b[j]$ 基本思想和FFT类似. 首先 ...
- FFT —— 快速傅里叶变换
问题: 已知A[], B[], 求C[],使: 定义C是A,B的卷积,例如多项式乘法等. 朴素做法是按照定义枚举i和j,但这样时间复杂度是O(n2). 能不能使时间复杂度降下来呢? 点值表示法: 我们 ...
随机推荐
- 在Linux CentOS7系统中搭建LNMP
LNMP就是Linux+Nginx+MySQL+PHP,既然是在Linux CentOS7那么Linux就是已经安装好了.所以接下百度一下接下来的教程,整理测试如下: 教程是centos6.2的有点老 ...
- 怎样安装Appium
在浏览器地址栏输入 http://appium.io/ 打开Appium官网: 安装包下载完成后, 一路默认安装, 什么都不用点击, 等待大约10分钟: 安装完成后, 会在桌面生成快捷图标: 启动: ...
- darknet 识别获取结果
在examples/darknet.c文件中若使用detect命令可以看到调用了test_detector. ... else if (0 == strcmp(argv[1], "detec ...
- 【转】巫师3:狂猎(The Witcher 3: Wild Hunt )的游戏事件工作流
转自腾讯游戏开发者平台(GAD) CDPROJEKT RED的主程序.Piotr Tomsinski 在GDC2016的最后一天18号,CDPROJEKT RED的主程Piotr Tomsinski, ...
- Java开发中用的比较多的数据结构
java 中几种常用数据结构 2016年07月11日 09:11:27 阅读数:83211 标签: 数据结构java 更多 个人分类: 自行学习 JAVA中常用的数据结构(java.util. 中 ...
- ServiceStack.Ormlit 使用Insert的时候自增列不会被赋值
Insert签名是这样的,将第2个参数设置为true就会返回刚插入的自增列ID了,然后可以手工赋值到对象上面去 public static long Insert<T>(this IDbC ...
- POJ 3487 The Stable Marriage Problem(稳定婚姻问题 模版题)
Description The stable marriage problem consists of matching members of two different sets according ...
- 第三课——MFC编程
一.MFC概述 1. MFC简述 MFC不仅仅是一套基础类库,更是一种编程方式. 2. MFC由来 1987年微软公司推出了第一代Windows产品,并为应用程序设计者提供了Win16(16位Wind ...
- c# dll问题
问题描述: dll完全拷贝另一个程序,可是报缺少引用程序集之类的错误. 解决办法: 有可能是.net版本造成的错误.一般常见在3.5升到4之后,存在很多容差.
- 评价cnblogs的用户体验
用户体验: 1.是否提供良好的体验给用户(同时提供价值)? cnbolgs为广大的用户提供了一个学习工作交流的平台,方便大家对各种问题提出自己的看法,并且可以实现不同用户的即时评论,互动交流. ...