大力推荐博客:

傅里叶变换(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. php常用几个数组的区别

    本文主要介绍的php数组函数主要有:sort.rsort.asort.arsort.ksort.krsort 测试数据定义一个关联数组如下: $data=[ 'f'=>123, 'b'=> ...

  2. leetcode-单词探索

    单词搜索     给定一个二维网格和一个单词,找出该单词是否存在于网格中. 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格.同一个单元格内的字母 ...

  3. HTML/JSP中一些单书名号标签的用途<%-- --%><!-- --><%@ %><%! %><% %><%= %>

    注释 <%-- --%>是(JSP)隐式注释,不会在页面显示的注释 <!-- -->是(Html)显示注释,会在JSP页面显示 关于注释还有单行隐式注释//和多行隐式注释/* ...

  4. Trie 树——搜索关键词提示

    当你在搜索引擎中输入想要搜索的一部分内容时,搜索引擎就会自动弹出下拉框,里面是各种关键词提示,这个功能是怎么实现的呢?其实底层最基本的就是 Trie 树这种数据结构. 1. 什么是 "Tri ...

  5. UVa 1583 - Digit Generator 解题报告 - C语言

    1.题目大意 如果a加上a的各个数字之和得到b,则说a是b的生成元.给出n其中$1\le n\le 100000$,求其最小生成元,若没有解则输出0. 2.思路 使用打表的方法打出各个数字a对应的b, ...

  6. StreamSets小白踩过的一些坑

    由于公司业务上的需求,需要实时监控mysql数据库的数据的增长,并将数据同步到另一个平台,所以就问老大使用什么工具比较好,老大推荐使用StreamSets,还说在测试环境都已经部署好了StreamSe ...

  7. (转载)IE8+兼容经验小结

    本文分享下我在项目中积累的IE8+兼容性问题的解决方法.根据我的实践经验,如果你在写HTML/CSS时候是按照W3C推荐的方式写的,然后下面的几点都关注过,那么基本上很大一部分IE8+兼容性问题都OK ...

  8. 让我们一起来做最漂亮的Android界面吧!

    让我们一起来做最漂亮的Android界面吧! AndroidiOS产品设计 摘要:如何为Android设备量身定制以打造出最为完美的应用?这是让诸多开发者很是头疼的问题.不同于iOS,Android设 ...

  9. Android 开发错误集锦

    1. eclipse的Device中不显示手机 在eclipse中连接不上手机,出现adb server didn't ACK  fail to start daemon 错误. 出现这种原因是因为a ...

  10. iOS-tableView刷新指定行,组

    /一个section刷新 NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:]; [tableview reloadSections:inde ...