【模板】多项式乘法(FFT)

link

我竟然搞懂了FFT。好感动。说不定我的数学有救了呢。

首先它的思想就是求出 2×m 个值,分别是 f(ωm0),f(ωm1)f(ωmm1) ,以及 g(ωm0),g(ωm1)g(ωmm1) 的值。由于满足 iR,f(i)×g(i)=r(i) ,所以有了这些值我们就可以求出 r 函数上的 2×m 个点,于是就可以唯一地确定这个函数。FFT是在优化求值和确定函数这两个过程以使得程序复杂度降低到 O(NlogN) 的。

首先 ωnk 满足一些性质。

ωnk=cos2πkn+sin2πkn×i

由于单位根可以看成是以原点为圆心的一个圆上点的集合,所以求得夹角 (2πkn)和半径 (1) 之后就可以知道这个三角形的横纵坐标,也就可以对应唯一的一个复数了,

ωnk=ω2n2k

用几何法会更好理解一点。大不了把圆再均分一次。

ωnkn=1,kZ

也挺好理解,圆上第一个单位根是在 1+0i 的位置。

理解了这些就可以考虑进行FFT了。首先求点值。

A(x)=a0+a1x+a2x2++an2xn2+an1xn1

进行奇偶分类。创造两个新函数:

A1(x)=a0+a2x+a4x2++an2xn2A2(x)=a1+a3x+a5x2++an1xn1

会发现

A(x)=A1(x2)+xA2(x2)

而单位根满足一些绝妙的性质,上文提到 ωnk=ω2n2k ,所以假如当前求解的是 A(ωnk) ,那么有:

A(ωnk)=A1(ωn2k)+ωnkA2(ωn2k)=A1(ωn/2k)+ωnkA2(ωn/2k)

所以每一次要求的自变量的值上标是不变的,变的只有下标,于是递归的时候把逐层减半的 n 代成一个参数就可以了。而n恰好和序列长度的变化方式一样,所以带一个参数就可以了。

好了,我们完成了 O(NlogN) 求出 2N 个点值,接下来考虑怎么把这些点值还原成为一个多项式。假设结果多项式是 C

[1111ωn1ωnn11ωnn1ωn(n1)2]×[c0c1cn1]=[f(ωn0)f(ωn1)f(ωnn1)]

可以看成是 B×C=A ,所以我们要求的矩阵 C=A×B1 。有结论是:

B1=[1111ωn1ωn1n1ωn1nωn(1n)2]

鬼知道谁想出来的。反正会发现最后把点值转化成参数的过程也可以用相似的分治过程进行计算。

写法上,有朴素递归的写法,也从小到大进行合并,合并之前要把所有元素进行重新排列,递推式是 cnt[i]=(cnt[i>>1]>>1)+((i&1)<<scnt-1);。后者常数小一点,空间消耗也能小一点。差距能在30%左右。

有几个注意事项。复数尽量手写,C++自带的咱也不会用啊对吧。这个算法有精度问题,不能处理太大的数据,在取模场景下也不适用。

递归版:

#include<bits/stdc++.h>
//#define zczc
const int N=4000010;
const double Pi=acos(-1.0);
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

struct node{double a,b;}a[N],b[N];
inline node operator +(node s1,node s2){return (node){s1.a+s2.a,s1.b+s2.b};}
inline node operator -(node s1,node s2){return (node){s1.a-s2.a,s1.b-s2.b};}
inline node operator *(node s1,node s2){return (node){s1.a*s2.a-s1.b*s2.b,s1.a*s2.b+s1.b*s2.a};}

void FFT(node a[],int limit,int type){
	if(limit==1)return;
	node a1[limit>>1],a2[limit>>1];
	int len=limit>>1;
	for(int i=0;i<limit;i+=2){
		a1[i>>1]=a[i],a2[i>>1]=a[i+1];
	}
	FFT(a1,len,type);FFT(a2,len,type);
	node ww=(node){cos(2.0*Pi/limit),type*sin(2.0*Pi/limit)};
	node w=(node){1.0,0};
	for(int i=0;i<len;i++,w=w*ww){
		a[i]=a1[i]+w*a2[i];
		a[i+len]=a1[i]-w*a2[i];
	}
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	int m,n;read(m);read(n);
	for(int i=0;i<=m;i++)scanf("%lf",&a[i].a);
	for(int i=0;i<=n;i++)scanf("%lf",&b[i].a);
	int limit=1;
	while(limit<=m+n)limit<<=1;
	FFT(a,limit,1);
	FFT(b,limit,1);
	for(int i=0;i<limit;i++)a[i]=a[i]*b[i];
	
	
	
	FFT(a,limit,-1);
	for(int i=0;i<=m+n;i++)printf("%d ",(int)(a[i].a/limit+0.5));
	
	
	return 0;
}
#include<bits/stdc++.h>
//#define zczc
const int N=4000010;
const double Pi=acos(-1.0);
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

struct node{double a,b;}a[N],b[N];
inline node operator +(node s1,node s2){return (node){s1.a+s2.a,s1.b+s2.b};}
inline node operator -(node s1,node s2){return (node){s1.a-s2.a,s1.b-s2.b};}
inline node operator *(node s1,node s2){return (node){s1.a*s2.a-s1.b*s2.b,s1.a*s2.b+s1.b*s2.a};}

int cnt[N];

node s[N];
void FFT(node a[],int limit,int type){
	for(int i=0;i<limit;i++)s[cnt[i]]=a[i];
	for(int len=1;len<limit;len<<=1){//序列长度的一半 
		node ww=(node){cos(Pi/len),type*sin(Pi/len)};
		for(int l=0;l+len<limit;l+=(len<<1)){//枚举序列左端点 
			node w=(node){1,0};int mid=l+len;
			for(int i=0;i<len;i++,w=w*ww){
				node A=s[l+i],B=s[mid+i];
				s[l+i]=A+w*B,s[mid+i]=A-w*B;
			}
		}
	}
	for(int i=0;i<limit;i++)a[i]=s[i];
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	int m,n;read(m);read(n);
	for(int i=0;i<=m;i++)scanf("%lf",&a[i].a);
	for(int i=0;i<=n;i++)scanf("%lf",&b[i].a);
	int limit=1;
	while(limit<=m+n)limit<<=1;
	
	int scnt=0;
	while((1<<scnt)!=limit)scnt++;
	for(int i=0;i<limit;i++){
		cnt[i]=(cnt[i>>1]>>1)+((i&1)<<scnt-1);
		//printf("%d\n",cnt[i]);
	}
	
	
	FFT(a,limit,1);
	FFT(b,limit,1);
	for(int i=0;i<limit;i++)a[i]=a[i]*b[i];
	FFT(a,limit,-1);
	for(int i=0;i<=m+n;i++)printf("%d ",(int)(a[i].a/limit+0.5));
	
	
	return 0;
}
posted @   Feyn618  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示