[算法模板]FFT-快速傅里叶变换
[算法模板]FFT-快速傅里叶变换
感谢ZYW聚聚为我们讲解FFT~
FFT
思路
我懒,思路和证明部分直接贴链接:
代码
主要思想是利用了单位根特殊的性质(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;
}
#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-快速傅里叶变换的更多相关文章
- 模板 FFT 快速傅里叶变换
FFT模板,原理不难,优质讲解很多,但证明很难看太不懂 这模板题在bzoj竟然是土豪题,服了 #include <cmath> #include <cstdio> #inclu ...
- CQOI2018 九连环 打表找规律 fft快速傅里叶变换
题面: CQOI2018九连环 分析: 个人认为这道题没有什么价值,纯粹是为了考算法而考算法. 对于小数据我们可以直接爆搜打表,打表出来我们可以观察规律. f[1~10]: 1 2 5 10 21 4 ...
- 「学习笔记」FFT 快速傅里叶变换
目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...
- FFT 快速傅里叶变换 学习笔记
FFT 快速傅里叶变换 前言 lmc,ikka,attack等众多大佬都没教会的我终于要自己填坑了. 又是机房里最后一个学fft的人 早背过圆周率50位填坑了 用处 多项式乘法 卷积 \(g(x)=a ...
- FFT快速傅里叶变换算法
1.FFT算法概要: FFT(Fast Fourier Transformation)是离散傅氏变换(DFT)的快速算法.即为快速傅氏变换.它是根据离散傅氏变换的奇.偶.虚.实等特性,对离散傅立叶变换 ...
- 「算法笔记」快速傅里叶变换(FFT)
一.引入 首先,定义多项式的形式为 \(f(x)=\sum_{i=0}^n a_ix^i\),其中 \(a_i\) 为系数,\(n\) 为次数,这种表示方法称为"系数表示法",一个 ...
- 模板 - 数学 - 快速傅里叶变换/快速数论变换(FFT/NTT)
先看看. 通常模数常见的有998244353,1004535809,469762049,这几个的原根都是3.所求的项数还不能超过2的23次方(因为998244353的分解). 感觉没啥用. #incl ...
- matlab中fft快速傅里叶变换
视频来源:https://www.bilibili.com/video/av51932171?t=628. 博文来源:https://ww2.mathworks.cn/help/matlab/ref/ ...
- FFT —— 快速傅里叶变换
问题: 已知A[], B[], 求C[],使: 定义C是A,B的卷积,例如多项式乘法等. 朴素做法是按照定义枚举i和j,但这样时间复杂度是O(n2). 能不能使时间复杂度降下来呢? 点值表示法: 我们 ...
随机推荐
- Callable接口实现线程
public class CallableDemo { public static void main(String[] args) throws Exception, ExecutionExcept ...
- linux 各目录 常用用处
/bin : 存储常 用用户指令 /boot : 存储 核心.模块 映像等启 动用文件/dev : 存储 设备文件/etc : 存储 系统. 服 务的配置目录 与 文件/home : 存放 个人主目录 ...
- Cmder介绍和配置
一.命令行神器cmder介绍 windows上做开发,不管是cmd还是powershell,似乎都不够美观,不够强大.今天就来介绍一款可以替代cmd的神器"Cmder",话不多说, ...
- JS基础知识——原型与原型链
1.如何准确判断一个变量的数组类型 2.写一个原型链继承的例子 3.描述new一个对象的过程 4.zepto(或其他框架中如何使用原型链) 知识点: (1)构造函数 function Foo(name ...
- Spring Cloud第十一篇 | 分布式配置中心高可用
本文是Spring Cloud专栏的第十一篇文章,了解前十篇文章内容有助于更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring Cl ...
- 比较typeof与instanceof
相同点: JavaScript中typeof和instanceof常用来判断一个变量是否为空,或者是什么类型的. 不同点: typeof的定义和用法: 返回值是一个字符串,用来说明变量的数据类型. 细 ...
- 常见问题解决办法=》.net后台
1:后台返回前端长度过大的问题 除了在web.config中设置最大值外还可以修改返回值 [web.config中配置最大值有时候无效,直接修改返回值效果会好一些] List<User> ...
- Python面向对象-多重继承之MixIN
以Animal类为例,假设要实现以下4种动物: Dog(狗).Bat(蝙蝠).Parrot(鹦鹉)和Ostrich(鸵鸟) 如果按照哺乳类和鸟类来区分的话,可以这样设计: Animal: |--Mam ...
- Springboot vue.js html 跨域 前后分离 shiro权限 集成代码生成器
本代码为 Springboot vue.js 前后分离 + 跨域 版本 (权限控制到菜单和按钮) 后台框架:springboot2.1.2+ mybaits+maven+接口 前端页面:html + ...
- 解决在IE11浏览器下,JQuery的AJAX方法不响应问题
在项目的时候一直都是在使用谷歌浏览器在调试,后来在现场部署到服务器上的时候,客户使用的是IE浏览器,版本是11 在测试的过程中,出现几个问题,虽然是几个问题,但是问题的原因就是AJAX第一次响应,第二 ...