[算法模板]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. 28. 实现strStr() (双指针)

    实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返 ...

  2. 基于 Blazui 的 Blazor 后台管理模板 BlazAdmin 正式尝鲜

    简介 BlazAdmin 是一个基于Blazui的后台管理模板,无JS,无TS,非 Silverlight,非 WebForm,一个标签即可使用. 我将在下一篇文章讨论 Blazor 服务器端渲染与客 ...

  3. Python之如何修改运行的快捷键

    如果你在Pycharm中运行程序使用Ctrl+shift+F10快捷键,运行失败,使用Pycharm工具组,右键一下选择“Run+文件名称AAA”运行程序,直接运行成功的话,那么你就可以 更换自己的运 ...

  4. jdk13-新特性预览

    一新特性 350: Dynamic CDS Archives(动态CDS档案) 351: ZGC: Uncommit Unused Memory(ZGC:取消提交未使用的内存) 353: Reimpl ...

  5. ps命令输出进程状态S后面加号的含义

    最近发现一个问题,ps命令输出里面进程状态为S+的含义,网上好多文章都说是表明进程“位于在后台进程组”. 例如下面这个ps命令输出说明: D 不可中断 Uninterruptible sleep (u ...

  6. GitHub 设置和取消代理,加速 git clone

    git 设置代理: git config --global git 取消代理: git config --global --unset http.proxy 针对 github.com 设置代理: g ...

  7. centos7网口添加IP,修改默认路由永久地址生效

    1永久增加ip地址和路由 网卡永久添加ip地址 注释:ens192为管理地址网卡,请根据实际情况进行修改,网关以192.168.160.1为例 复制一份网卡配置文件命名为ifcfg-ens192:1 ...

  8. 面试连环炮系列(一):如何保证Redis高可用和高并发

    如何保证Redis高可用和高并发? Redis主从架构,一主多从,可以满足高可用和高并发.出现实例宕机自动进行主备切换,配置读写分离缓解Master读写压力. Redis高可用方案具体怎么实施? 使用 ...

  9. [AI开发]DeepStream开发填坑记录

    下面是在deepstream使用过程中碰到的一些坑: (1)Pipeline中的Sink如果需要编码存文件或者推rtmp的流,注意控制编码的参数,编码质量不要太高.否则可能Sink带不动,整个Pipe ...

  10. Spring 常犯的十大错误,(收藏后)永远不要在犯了

    1. 错误一:太过关注底层 我们正在解决这个常见错误,是因为 “非我所创” 综合症在软件开发领域很是常见.症状包括经常重写一些常见的代码,很多开发人员都有这种症状. 虽然理解特定库的内部结构及其实现, ...