[算法模板]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. CSS动态表达式

    样式:style:expression(脚本) 如: 1 _top: expression(this.offsetHeight); _top: expression(eval(document.doc ...

  2. VMware永久激活密钥

    VMware Workstation 14 Pro永久激活密钥 1. ZC3WK-AFXEK-488JP-A7MQX-XL8YF 2. AC5XK-0ZD4H-088HP-9NQZV-ZG2R4 3. ...

  3. 2020年值得你去试试的10个React开发工具

    本文由葡萄城技术团队翻译并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. JavaScript每天都在出现大量的框架和工具,而React是除了上次我们 ...

  4. Sockit 硬件接口编程——点亮一个LED

    1.话不多说上代码 #include <stdio.h> #include <stdlib.h> #include <string.h> #include < ...

  5. 如何为.NETCore安装汉化包智能感知

    引言 具体不记得是在群里还是什么地方有人问过,.NETCore有没有汉化包,答案是有,目前微软已经为我们提供了.NETCore多种语言的语言包.下面看看如何安装与使用吧. 在哪下载? 在微软官方下载 ...

  6. Java 从入门到进阶之路(十四)

    在之前的文章我们介绍了一下 Java 中的抽象类和抽象方法,本章我们来看一下 Java 中的接口. 在日常生活中,我们会接触到很多类似接口的问题,比如 USB 接口,我们在电脑上插鼠标,键盘,U盘的时 ...

  7. iOS----------证书的制作

    https://developer.umeng.com/docs/66632/detail/66748#createappid Certificates-> 卫生许可证 identifiers ...

  8. Python3-logging日志模块

    日志模块 logging模块默认收集的日志是warning以上等级的 日志一共分为5个等级,从低到高分别是: 级别 说明 DEBUG 输出详细的运行情况,主要用于调试 INFO 确定一切按预期运行,一 ...

  9. Oracle trunc函数的使用

    1. 对日期的操作 2. 对数字的操作 1.对日期的操作 /**************日期********************/ SELECT TRUNC(SYSDATE) FROM DUAL; ...

  10. Bash脚本编程之脚本基础和bash配置文件

    脚本基础 参考资料:Shell Scripts (Bash Reference Manual) 不严谨地说,编程语言根据代码运行的方式,可以分为两种方式: 编译运行:需要先将人类可识别的代码文件编译成 ...