[算法模板]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. Python3 类的继承

    目录 继承的基本概念 什么是继承 继承有什么用 如何实现继承 初识继承 寻找继承关系 如何寻找继承关系 实例演示 继承背景下的对象属性查找顺序 派生 新式类和经典类 钻石继承 通过继承实现修改json ...

  2. 《java面试十八式》第一式 --冈本零点零一

    第一式 [冈本零点零一] 冈本零点零一:将简历包装于无形,博得人事的芳心,用过的人都说好 . 要想有更多的面试机会,简历是不可缺少的,机会都没有何谈面:所以这也是我们的第一步. 首先是简历模板: 模板 ...

  3. selenium滑块操作

    from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains from ...

  4. CCF-CSP题解 201812-3 CIDR合并

    题目想求与给定前缀列表等价的包含IP前缀数目最少的前缀列表. 首先是怎么存储前缀列表.用一个long long存储IP地址,再存一个前缀长度,封装在一个结构体里\(<ipNum, len> ...

  5. 《Java基础知识》Java多态对象的类型转换

    这里所说的对象类型转换,是指存在继承关系的对象,不是任意类型的对象.当对不存在继承关系的对象进行强制类型转换时,java 运行时将抛出 java.lang.ClassCastException 异常. ...

  6. (2019版本可用)Pycharm的安装,破解

    前言 python的操作工具pycharm,是专门用来写python语言的. 因为之前在网上找到了,但是太麻烦了,所以整理整理. pycharm安装 官网可以选择下载(pycharm最新版有可能破解不 ...

  7. 爬虫(七):BeatifulSoup模块

    1. Beautiful Soup介绍 Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库.能将即将要进行解析的源码加载到bs对象,调用bs对象中相关的方法或属性进 ...

  8. JPA中实现双向一对一的关联关系

    场景 JPA入门简介与搭建HelloWorld(附代码下载): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/103473937 ...

  9. Sunset: Nightfall Vulnhub Walkthrough

    靶机链接: https://www.vulnhub.com/entry/sunset-nightfall,355/ 主机扫描: ╰─ nmap -p- -A 10.10.202.162Starting ...

  10. 在Ubuntu 18.04系统上安装Pydio Cells详细图文教程

    前言   基于云的协作工具Pydio cell提供了一系列灵活的特性,包括应用内消息传递.文件共享和版本控制.下面逐步介绍安装过程. Pydio cell最初是一个简单的基于云的文件共享系统,但经过升 ...