大力推荐博客:

傅里叶变换(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. CentOS 7.2使用tomcat部署jenkins2.130

    一.jenkins介绍 Jenkins是一个功能强大的应用程序,允许持续集成和持续交付项目,无论用的是什么平台.这是一个免费的源代码,可以处理任何类型的构建或持续集成.集成Jenkins可以用于一些测 ...

  2. Java中二进制数与整型之间的转换

    import java.io.*; public class Test{ /** * 二进制与整型之间的转换 * @param args * @throws IOException */ public ...

  3. Solium代码测试框架

    Solium, 在solid中,Linter用于标识和修复样式&安全问题 //调用测试 solium -d contracts --fix 源代码名称:Solium 源代码网址:http:// ...

  4. Python3 Tkinter-Scale

    1.创建 from tkinter import * root=Tk() Scale(root).pack() root.mainloop() 2.参数 from tkinter import * r ...

  5. UVA 11922 Permutation Transformer(平衡二叉树)

    Description Write a program to transform the permutation 1, 2, 3,..., n according to m instructions. ...

  6. POJ 1113 Wall(计算几何の凸包)

    Description Once upon a time there was a greedy King who ordered his chief Architect to build a wall ...

  7. Memory及其controller芯片整体测试方案(上篇)

    如果你最近想买手机,没准儿你一看价格会被吓到手机什么时候偷偷涨价啦! 其实对于手机涨价,手机制造商也是有苦难言,其中一个显著的原因是存储器芯片价格的上涨↗↗↗ >>> 存储器memo ...

  8. 初学c#(又要打代码了好难)

    因为我原来从没有学过C#,所以要重新看一个语言的基本语法,仔细阅读了老师的作业要求,发现第一个10分的作业如果要用c语言写我是可以完成的,于是定个小目标就是在周日前完成作业的第一步.今天我在菜鸟教程的 ...

  9. DAY2敏捷冲刺

    站立式会议 工作安排 (1)服务器配置 (2)数据库连接 (3)页面创意 燃尽图 代码提交记录 感想 林一心:centos配置服务器真的算是一个不小的坑,目前数据库配置清楚,脚本部署好明天测试交互,还 ...

  10. 分页查询es时,返回的数据不是自己所期望的问题

    在进行es分页查询时,一般都是用sql语句转成es查询字符串,在项目中遇到过不少次返回的数据不是自己所期望的那样时,多半原因是自己的sql拼接的有问题. 解决办法:务必要保证自己的sql语句拼接正确.