快速计算多项式相乘系数 FFT快速傅里叶变换

快速计算多项式相乘系数 FFT快速傅里叶变换

快速傅里叶变换(FFT)——有史以来最巧妙的算法?

正常求两个多项式乘积

A ( x ) = ∑ i = 0 n A i x i , B ( x ) = ∑ i = 0 n B i x i C ( x ) = ∑ i = 0 n ∑ j = 0 n A i B j x i + j A(x)=\sum_{i=0}^{n}{A_ix^i},B(x)=\sum_{i=0}^{n}{B_ix^i} \\ C(x)=\sum_{i=0}^{n}\sum_{j=0}^nA_iB_jx^{i+j} A(x)=i=0nAixi,B(x)=i=0nBixiC(x)=i=0nj=0nAiBjxi+j

复杂度为 O ( n 2 ) O(n^2) O(n2)

分别设n+1个在A与B上的点

根据高斯消元法,n+1个不同的点一定能确定一个唯一的n次多项式

A ( x ) = { ( x 0 , A ( x 0 ) ) , ( x 1 , A ( x 1 ) ) , ( x 2 , A ( x 2 ) ) . . . ( x n , A ( x n ) ) } B ( x ) = { ( x 0 , B ( x 0 ) ) , ( x 1 , B ( x 1 ) ) , ( x 2 , B ( x 2 ) ) . . . ( x n , B ( x n ) ) } A(x)=\{(x_0,A(x_0)),(x_1,A(x_1)),(x_2,A(x_2))...(x_n,A(x_n))\}\\ B(x)=\{(x_0,B(x_0)),(x_1,B(x_1)),(x_2,B(x_2))...(x_n,B(x_n))\}\\ A(x)={(x0,A(x0)),(x1,A(x1)),(x2,A(x2))...(xn,A(xn))}B(x)={(x0,B(x0)),(x1,B(x1)),(x2,B(x2))...(xn,B(xn))}

横坐标相同的点相乘

C ( x ) = { ( x 0 , A ( x 0 ) B ( x 0 ) ) , ( x 1 , A ( x 1 ) B ( x 1 ) ) . . . ( x n , A ( x n ) B ( x n ) ) } C(x)=\{(x_0,A(x_0)B(x_0)),(x_1,A(x_1)B(x_1))...(x_n,A(x_n)B(x_n))\} C(x)={(x0,A(x0)B(x0)),(x1,A(x1)B(x1))...(xn,A(xn)B(xn))}

那么就有两个问题:

1.怎么快速得到n+1个点
2.怎么将点值表达法变回系数

如果 A ( x ) A(x) A(x)是一个偶函数,那么 A ( x ) = A ( − x ) A(x)=A(-x) A(x)=A(x),因此带入一个值也就得到了两个点

同理如果 A ( x ) A(x) A(x)是一个奇函数,那么有 A ( x ) = − A ( − x ) A(x)=-A(-x) A(x)=A(x),也可以带一个点得到两个点

因此可以将 A ( x ) A(x) A(x)分成奇函数部分与偶函数部分

P ( x ) = a 0 + a 1 x + a 2 x 2 + . . . + a n x n P ( x ) = ( a 0 + a 2 x 2 + a 4 x 4 + . . . ) + x ( a 1 + a 3 x 2 + a 5 x 4 + . . . ) P ( x ) = P e ( x 2 ) + x P o ( x 2 ) P ( x i ) = P e ( x i 2 ) + x i P o ( x i 2 ) P ( − x i ) = P e ( x i 2 ) − x i P o ( x i 2 ) P(x)=a_0+a_1x+a_2x^2+...+a_nx^n\\ P(x)=(a_0+a_2x^2+a_4x^4+...)+x(a_1+a_3x^2+a_5x^4+...)\\ P(x)=P_e(x^2)+xP_o(x^2)\\ P(x_i)=P_e(x_i^2)+x_iP_o(x_i^2)\\ P(-x_i)=P_e(x_i^2)-x_iP_o(x_i^2) P(x)=a0+a1x+a2x2+...+anxnP(x)=(a0+a2x2+a4x4+...)+x(a1+a3x2+a5x4+...)P(x)=Pe(x2)+xPo(x2)P(xi)=Pe(xi2)+xiPo(xi2)P(xi)=Pe(xi2)xiPo(xi2)

然后如果将 P e ( x ) , P o ( x ) P_e(x),P_o(x) Pe(x),Po(x)继续分解

但我们的 P e ( x ) , P o ( x ) P_e(x),P_o(x) Pe(x),Po(x)是通过 P ( x ) = P e ( x 2 ) + x P o ( x 2 ) P(x)=P_e(x^2)+xP_o(x^2) P(x)=Pe(x2)+xPo(x2)得来的,因此取相反数也无法让它们一一符号相反

那么我们取复数呢?

i 2 = ( − i ) 2 = − 1 1 2 = ( − 1 ) 2 = 1 因此有 i , − i , 1 , − 1 :   x 1 , − x 1 , x 2 , − x 2 − 1 , 1 : x 1 2 , x 2 2 1 : x 1 4 可解决 n ≤ 4 的问题 i^2=(-i)^2=-1\\ 1^2=(-1)^2=1\\ 因此有\\ i,-i,1,-1:\ x_1,-x_1,x_2,-x_2\\ -1,1:x_1^2,x_2^2\\ 1:x_1^4 \\可解决n\leq4的问题 i2=(i)2=112=(1)2=1因此有i,i,1,1: x1,x1,x2,x21,1:x12,x221:x14可解决n4的问题

那么更大的呢?

我们令 w = e 2 π i n = c o s ( 2 π n ) + i s i n ( 2 π n ) w=e^{\frac{2\pi i}{n}}=cos(\frac{2\pi}{n})+isin(\frac{2\pi}{n}) w=en2πi=cos(n2π)+isin(n2π),我们可以得到它的性质正好可以解决该问题

请添加图片描述

反过来呢?
w = e − 2 π i n w=e^{-\frac{2\pi i}{n}} w=en2πi,然后给每个数都乘上 1 n \frac{1}{n} n1即可
//初始化每个位置最终到达的位置 
void init(int k)
{
    int len=1<<k;
	for(int i=0;i<len;i++)
	rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}
//a表示要操作的系数,n表示序列长度
//若flag为1,则表示FFT,为-1则为IFFT(需要求倒数) 
void fft(cp *a,int n,int flag){ 
    for(int i=0;i<n;i++)
	{
	 //i小于rev[i]时才交换,防止同一个元素交换两次,回到它原来的位置。 
	  if(i<rev[i])swap(a[i],a[rev[i]]);
	}
	for(int h=1;h<n;h*=2)//h是准备合并序列的长度的二分之一
	{
		cp wn=exp(cp(0,flag*pie/h));//求单位根w_n^1 
		for(int j=0;j<n;j+=h*2)//j表示合并到了哪一位
		{
			cp w(1,0);
			for(int k=j;k<j+h;k++)//只扫左半部分,得到右半部分的答案
			{
			    cp x=a[k];
			    cp y=w*a[k+h];
		        a[k]=x+y;  //这两步是蝴蝶变换 
		        a[k+h]=x-y;
		        w*=wn; //求w_n^k 
			}
		 }
	 }
	 //判断是否是FFT还是IFFT 
	 if(flag==-1)
	 for(int i=0;i<n;i++)
     a[i]/=n;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值