[算法模板]FFT-快速傅里叶变换

感谢ZYW聚聚为我们讲解FFT~

FFT

思路

我懒,思路和证明部分直接贴链接:

rvalue

LSJ-FFT与NTT基础

代码

主要思想是利用了单位根特殊的性质(n次单位根后一半幂跟前一半幂取值相等)。只是因为式子中奇数次幂还要提出来个\(\omega_n^k\),这个东西只要取个反就好了(即对称性:\(\omega_n^k=-\omega_n^{k+\frac{n}{2}}\))。

FFT递归:

#include <cstdio>
#include <cmath>
using namespace std;
const int maxn=2e6+10;
const double pi=acos(-1.0);
struct comp{
double a,b;
};
comp operator +(comp a,comp b){return (comp){a.a+b.a,a.b+b.b};}
comp operator -(comp a,comp b){return (comp){a.a-b.a,a.b-b.b};}
comp operator *(comp a,comp b){return (comp){a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a};}
void fft(int l,comp *a,int f)
{
if(l==1) return;
comp a1[l>>1],a2[l>>1];
for(int i=0;i<l;i+=2)
{
a1[i>>1]=a[i];
a2[i>>1]=a[i+1];
}
fft(l>>1,a1,f); fft(l>>1,a2,f);
comp wn=(comp){cos(2*pi/l),f*sin(2*pi/l)},w=(comp){1,0};
for(int i=0;i<(l>>1);i++,w=w*wn)
{
a[i]=a1[i]+w*a2[i];
a[i+(l>>1)]=a1[i]-w*a2[i];
}
}
comp a[maxn],b[maxn];
int main ()
{
int n,m; scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) scanf("%lf",&a[i].a);
for(int i=0;i<=m;i++) scanf("%lf",&b[i].a);
int l=1; while(l<=n+m) l<<=1;
fft(l,a,1); fft(l,b,1);
for(int i=0;i<l;i++) a[i]=a[i]*b[i];
fft(l,a,-1);
for(int i=0;i<=n+m;i++) printf("%d ",(int)(a[i].a/l+0.5));
return 0;
}

因为其运行效率过低。我们一般使用迭代FFT。

FFT迭代:

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
const int maxn=4*1e6+10;
const double pi=acos(-1.0);
struct comp{
double a,b;
};
comp operator +(comp a,comp b){return (comp){a.a+b.a,a.b+b.b};}
comp operator -(comp a,comp b){return (comp){a.a-b.a,a.b-b.b};}
comp operator *(comp a,comp b){return (comp){a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a};}
int rev[maxn],rp;
void get_rev(int l)//l为位数,rev[i]代表i的二进制表示颠倒(二进制位有l位,不足补0)
{
for(int i=1;i<(1<<l);i++)
rev[i]=(rev[i>>1]>>1)|((1&i)<<l-1);
}
void fft(int len,comp *a,int f)
{
for(int i=1;i<len;i++)
if(rev[i]>i) swap(a[rev[i]],a[i]);
for(int l=2;l<=len;l<<=1)//区间长度
{
comp wn=(comp){cos(2*pi/l),f*sin(2*pi/l)};
for(int i=0;i+l<=len;i+=l)
{
comp w=(comp){1,0};
for(int k=i;k<i+(l>>1);k++,w=w*wn)
{
comp t=w*a[k+(l>>1)],tmp=a[k];
a[k]=tmp+t;
a[k+(l>>1)]=tmp-t;
}
}
}
}
//a[i]表示当x=单位根的i次方时y的值
comp a[maxn],b[maxn];
int main ()
{
int n,m; scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) scanf("%lf",&a[i].a);
for(int i=0;i<=m;i++) scanf("%lf",&b[i].a);
int l=1,cnt=0; while(l<=n+m) l<<=1,cnt++;
get_rev(cnt);
fft(l,a,1); fft(l,b,1);//l是多项式项数
for(int i=0;i<l;i++) a[i]=a[i]*b[i];
fft(l,a,-1);
for(int i=0;i<=n+m;i++) printf("%d ",(int)(a[i].a/l+0.5));
return 0;
}

NTT

啊我饿了我要吃NTT

直接粘一张钟神的PPT:

代码

预处理原根次幂:

for(int i=2;i<(1<<l);i<<=1) {//枚举单位根周期长度(即w_n的n)
int w0=Pow(3,(P-1)/i),w1=Pow(3,P-1-(P-1)/i);
wn[0][i>>1]=wn[1][i>>1]=1;//wn[f][i],i的最高位代表是几次单位根,其他位代表是第几个。这里求的是i的单位根,因为前一半i单位根等于i/2的单位根所以是存储在i/2的位置.(推式子的时候推过,长度为len时代入单位根周期为len/2)
for(int j=1;j<(i>>1);++j)//w_i单位根的j次方(因为折半了所以只用求一半)
wn[0][(i>>1)+j]=wn[0][(i>>1)+j-1]*(ll)w0%P,
wn[1][(i>>1)+j]=wn[1][(i>>1)+j-1]*(ll)w1%P;
}

[模板]分治FFT

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int mod=998244353;
const int maxn=3e5+10;
typedef long long ll;
ll a[maxn],b[maxn],f[maxn],g[maxn],wn[2][maxn];
int n,rev[maxn];
int ksm(int num,int t){
int res=1;
for(;t;t>>=1,num=1ll*num*num%mod){
if(t&1)res=1ll*res*num%mod;
}
return res;
}
void get_rev(int len){for(int i=1;i<(1<<len);i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));}
void get_wn(int len){
for(int i=2;i<=(1<<len);i<<=1){
ll w1=ksm(3,(mod-1)/i),w0=ksm(3,mod-1-(mod-1)/i);
wn[0][i>>1]=wn[1][i>>1]=1;
for(int j=1;j<(i>>1);j++){
wn[0][j+(i>>1)]=wn[0][j+(i>>1)-1]*w0%mod;
wn[1][j+(i>>1)]=wn[1][j+(i>>1)-1]*w1%mod;
}
}
}
void NTT(int len,ll *c,int f){
for(int i=0;i<len;i++)if(rev[i]>i)swap(c[i],c[rev[i]]);
for(int l=2;l<=len;l<<=1){
for(int i=0;i+l<=len;i+=l){
for(int k=i;k<i+(l>>1);k++){
ll tmp1=c[k],tmp2=wn[f][k+(l>>1)-i]*c[k+(l>>1)];
c[k]=(tmp1+tmp2)%mod;
c[k+(l>>1)]=(tmp1-tmp2+mod)%mod;
}
}
}
}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid);
int cnt=0,len=1;while(len<=(r-l-1))len<<=1,cnt++;
for(int i=0;i<len;i++)a[i]=b[i]=0;
for(int i=0;i<=mid-l;i++)a[i]=f[i+l];
for(int i=0;i<=r-l-1;i++)b[i]=g[i+1];
// memset(rev,0,sizeof(rev));
get_rev(cnt);
NTT(len,a,1);NTT(len,b,1);
for(int i=0;i<len;i++)a[i]=a[i]*b[i]%mod;
NTT(len,a,0);
ll inv=ksm(len,mod-2);
for(int i=0;i<len;i++)a[i]=a[i]*inv%mod;
for(int i=mid+1;i<=r;i++)f[i]+=a[i-l-1],f[i]%=mod;
cdq(mid+1,r);
}
int main(){
f[0]=1;
scanf("%d",&n);get_wn(18);
for(int i=1;i<n;i++)scanf("%lld",&g[i]);
cdq(0,n-1);
for(int i=0;i<n;i++)printf("%lld ",(f[i]%mod+mod)%mod);
return 0;
}

[算法模板]FFT-快速傅里叶变换的更多相关文章

  1. 模板 FFT 快速傅里叶变换

    FFT模板,原理不难,优质讲解很多,但证明很难看太不懂 这模板题在bzoj竟然是土豪题,服了 #include <cmath> #include <cstdio> #inclu ...

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

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

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

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

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

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

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

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

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

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

  7. 模板 - 数学 - 快速傅里叶变换/快速数论变换(FFT/NTT)

    先看看. 通常模数常见的有998244353,1004535809,469762049,这几个的原根都是3.所求的项数还不能超过2的23次方(因为998244353的分解). 感觉没啥用. #incl ...

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

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

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

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

随机推荐

  1. 日地拉格朗日L2点轨道的卫星运行

    看了嫦娥四号通信的中继卫星,不明白是怎么运行的,下面的演示解除了我的疑问. https://lt.cjdby.net/thread-2479933-1-2.html

  2. JAVA使用SnakeYAML解析与序列化YAML

    1.概述 本文,我们将学习如何使用SnakeYAML库将 YAML文档转换为Java对象,以及JAVA对象如何序列化为YAML文档. 2.项目设置 要在项目中使用SnakeYAML,需要添加Maven ...

  3. 清晰架构(Clean Architecture)的Go微服务: 程序结构

    我使用Go和gRPC创建了一个微服务,并试图找出最佳的程序结构,它可以用作我未来程序的模板. 我有Java背景,并发现自己在Java和Go之间挣扎,它们之间的编程理念完全不同.我写了一系列关于在项目工 ...

  4. java—将数据库读取的list转tree

    一.引言 有时候我们从数据库中读取出了一个表的数据,比如存储的是中国的省市县的ID.名称与父节点ID,读出来的数据并不是前台想要的,这个时候我们要想法处理一下都出来的list,将它变为一个树. 比如直 ...

  5. Linux系统入门简介<1>

    linux系统入门简介 我们为什么要学习Linux? 在介绍Linux的历史前,我想先针对大家如何对Linux的发音说一下.我发现我身边的朋友对Linux的发音大致有这么几种: "里那克斯& ...

  6. Leetcode题解 - 贪心算法部分简单题目代码+思路(860、944、1005、1029、1046、1217、1221)

    leetcode真的是一个学习阅读理解的好地方 860. 柠檬水找零 """ 因为用户支付的只会有5.10.20 对于10元的用户必须找一个5 对于20元的用户可以找(三 ...

  7. Another git process seems to be running in this repository

    今天在推送项目的时候git突然报如题的错误.查了一下是由于git被另外一个程序占用,产生原 原因在于Git在使用过程中遭遇了崩溃,部分被上锁资源没有被释放. 解决方案也很简单,在git中找到对应的in ...

  8. Linux服务器部署.Net Core笔记:六、安装MySQL

    接下来我们在 Centos7 系统下使用 yum 命令安装 MySQL,需要注意的是 CentOS 7 版本中 MySQL数据库已从默认的程序列表中移除,所以在安装前我们需要先去官网下载 Yum 资源 ...

  9. Node.js+Express+MongoDB数据库实现网页注册登入功能

    通过 Node.js + Express + MongoDB 实现网页注册账号 和 登入账号的功能 项目准备: 1: 事先准备好项目的页面 (首页页面 index.html)(登入页面 login.h ...

  10. Android 共享参数 SharedPreferences

    完成共享参数的读写 public class SharedPreference { private Context context; public SharedPreference(Context c ...