利用快速傅里叶计算多项式相乘
FFT多项式计算
傅里叶变换是一个很大的概念,里面包含太多太多东西,整篇在傅里叶变换中只是一个小应用而已。就我的理解,描述快速傅里叶变换优化多项式乘法的原理和具体实践。
傅里叶变换
傅里叶变换是一种线性积分变换,用于信号在时域(或空域)和频域之间的变换。
一般情况下,若傅里叶变换一词不加任何限定语,则指的是连续傅里叶变换。(连续)傅里叶变换定义如下:傅里叶变换将可积函数f:R→Cf: \mathbb{R} \rightarrow \mathbb{C}f:R→C表示成复指数函数的积分或级数性质。
f^(ξ)=∫−∞∞f(x)e−2πixξdx\hat{f} \left( \xi \right) = \int_{-\infty}^{\infty} f \left( x \right) e^{-2\pi i x \xi } dx f^(ξ)=∫−∞∞f(x)e−2πixξdx
其中ξ\xiξ为任意实数。自变量xxx表示时间,变换变量ξ\xiξ表示频率。在多数情况下,傅里叶变换是可逆的,即可以通过f^\hat{f}f^得到其原函数fff,定义如下:
f(ξ)=∫−∞∞f^(ξ)e2πiξxdξf \left( \xi \right) = \int_{-\infty}^{\infty} \hat{f} \left( \xi \right) e^{2\pi i \xi x} d \xi f(ξ)=∫−∞∞f^(ξ)e2πiξxdξ
离散傅里叶变换
为了在科学计算和数字信号处理等领域使用计算机进行傅里叶变换,必须将函数xnx_{n}xn定义在离散点而非连续域内,且须满足有限性和周期性等提交。这种情况下,使用离散傅里叶变换(Discrete Fourier Transform,DFT),DFT是傅里叶变换在时域和频域上都呈离散的形式,函数定义如下:
x^n=∑k=0N−1xke−i2πNknn=0,⋯,N−1\hat{x}_{n} = \sum_{k=0}^{N-1}x_{k} e^{-i \frac{2\pi}{N} k n} \qquad n = 0, \cdots, N-1 x^n=k=0∑N−1xke−iN2πknn=0,⋯,N−1
其逆变换为:
xk=1N∑n=0N−1x^ne−i2πNnkk=0,⋯,N−1x_{k} =\frac{1}{N} \sum_{n=0}^{N-1} \hat{x}_{n} e^{-i \frac{2\pi}{N} n k} \qquad k = 0, \cdots, N-1 xk=N1n=0∑N−1x^ne−iN2πnkk=0,⋯,N−1
单位根
数学上,nnn次单位根是nnn次幂为1的负数。定义为ωn=1(n=1,2,3,⋯)\omega^{n}=1 \quad \left( n=1,2,3,\cdots\right)ωn=1(n=1,2,3,⋯),ω\omegaω为nnn次单位根。单位的nnn次根有nnn个:e2πki/n(k=0,1,2,⋯,n−1)e^{2\pi k i / n} \quad \left(k=0,1,2,\cdots, n-1 \right)e2πki/n(k=0,1,2,⋯,n−1)。我们用ωn0,ωn1,⋯,ωnn−1\omega_{n}^{0}, \omega_{n}^{1}, \cdots, \omega_{n}^{n-1}ωn0,ωn1,⋯,ωnn−1表示这个nnn个nnn次根,ωni=e2πki/n\omega_{n}^{i} = e^{2\pi k i / n}ωni=e2πki/n。
结合欧拉公式,可以很容易得看出,ωnk=e2πkni=e2πk−1ni⋅e2πni=ωnk−1⋅ωn\omega_{n}^{k} = e ^{2\pi \frac{k}{n} i } = e ^{2\pi \frac{k-1}{n} i } \cdot e ^{ \frac{2\pi}{n} i } = \omega_{n}^{k-1} \cdot \omega_{n}ωnk=e2πnki=e2πnk−1i⋅en2πi=ωnk−1⋅ωn
对于n≥0,k≥0,d≥0n \geq 0, k \geq 0, d \geq 0n≥0,k≥0,d≥0,有ωdndk=e2πdki/dn=e2πki/n=ωnk\omega_{dn}^{dk}= e^{2\pi dk i/dn} = e^{2\pi k i /n}= \omega_{n}^{k}ωdndk=e2πdki/dn=e2πki/n=ωnk。
对于∀n\forall n∀n满足n>0n > 0n>0且2∣n2 | n2∣n,有ωnk+n2=e2πin(k+n2)=e2πik/neπi=ωnk(cosπ+isinπ)=−ωnk\omega_{n}^{k + \frac{n}{2}} = e ^ {\frac{2 \pi i}{n} \left( k + \frac{n}{2}\right)} = e^{2 \pi i k / n} e^{\pi i} = \omega_{n}^{k} \left( cos \pi + i sin \pi \right) = -\omega_{n}^{k}ωnk+2n=en2πi(k+2n)=e2πik/neπi=ωnk(cosπ+isinπ)=−ωnk
多项式
形如a0+a1x+⋯+anxna_{0} + a_{1} x + \cdots + a_{n} x^{n}a0+a1x+⋯+anxn的多项式被称为多项式,记为A(x)=∑i=0n−1aixiA\left( x \right) = \sum_{i=0}^{n-1} a_{i} x^{i}A(x)=∑i=0n−1aixi,其中ai,i=0,1,⋯,na_{i}, i=0,1,\cdots,nai,i=0,1,⋯,n为系数,用系数表示多项式为a⃗=(a0,a1,⋯,an)\vec{a}=\left(a_{0}, a_{1}, \cdots, a_{n} \right)a=(a0,a1,⋯,an),a⃗\vec{a}a就为多项式的系数表示法。
我们知道通过n+1n+1n+1个不同的点{(x0,y0),⋯,(xn,yn)}\left\{\left(x_{0},y_{0}\right),\cdots, \left(x_{n},y_{n}\right) \right\}{(x0,y0),⋯,(xn,yn)}就可以唯一确定一个nnn次多项式,{(x0,y0),⋯,(xn,yn)}\left\{\left(x_{0},y_{0}\right),\cdots, \left(x_{n},y_{n}\right) \right\}{(x0,y0),⋯,(xn,yn)}为多项式的点值表示法。
快速傅里叶变换
快速傅里叶变换(Fast Fourier Transform,FFT),是计算离散傅里叶变换及其逆变换的快算算法。按照DFT的定义,计算一个长为nnn的序列需要的时间复杂度为O(n2)O\left( n^{2} \right)O(n2),而FFT通过把DFT矩阵分解为稀疏因子之积来快速计算变换,从而将时间复杂度降到O(nlogn)O\left( nlog n \right)O(nlogn)。
库里-图基快速傅里叶变换算法是最常见的FFT算法,基于分治策略,可将时间复杂度降为O(nlogn)O\left( nlog n \right)O(nlogn)。
假设一个多项式表示为A(x)=∑i=0n−1aixiA\left( x \right) = \sum_{i=0}^{n-1} a_{i} x^{i}A(x)=∑i=0n−1aixi,用系数表示法表示就是向量a⃗=(a0,a1,⋯,an)\vec{a}=\left(a_{0}, a_{1}, \cdots, a_{n} \right)a=(a0,a1,⋯,an)。用点值表示,将nnn个nnn次单位根ωn0,ωn1,⋯,ωnn−1\omega_{n}^{0}, \omega_{n}^{1}, \cdots, \omega_{n}^{n-1}ωn0,ωn1,⋯,ωnn−1带入到多项式为A(ωnk)=∑i=0n−1aiωnki,k=0,1,⋯,n−1A\left( \omega_{n}^{k} \right) = \sum_{i=0}^{n-1} a_{i} \omega_{n}^{ki}, k = 0,1,\cdots, n-1A(ωnk)=∑i=0n−1aiωnki,k=0,1,⋯,n−1。现在我们和DFT定义的公式比对,A(ωni)A\left(\omega_{n}^{i}\right)A(ωni)就是是aia_{i}ai的DFT,那么aia_{i}ai就是A(ωni)A\left(\omega_{n}^{i}\right)A(ωni)就是的IDFT。记为A(ωni)=DFT(ai)A\left(\omega_{n}^{i}\right) = DFT\left( a_{i} \right)A(ωni)=DFT(ai)。
下面我们就依据上面陈列的基础基础,对A(ωnk)A\left(\omega_{n}^{k}\right)A(ωnk)进行公式推导:
A(ωnk)=∑i=0n−1aiωnki=∑i=0n2−1a2iωn2ki+ωnk∑i=0n2−1a2i+1ωn2ki=∑i=0n2−1a2iωn2ki+ωnk∑i=0n2−1a2i+1ωn2ki\begin{matrix} A\left(\omega_{n}^{k}\right) &= \sum_{i=0}^{n-1} a_{i}\omega_{n}^{ki} \qquad \qquad \qquad \qquad \qquad \\ \\ & = \sum_{i=0}^{\frac{n}{2}-1} a_{2i}\omega_{n}^{2ki} + \omega_{n}^{k} \sum_{i=0}^{\frac{n}{2}-1} a_{2i+1}\omega_{n}^{2ki} \\ \\ & = \sum_{i=0}^{\frac{n}{2}-1} a_{2i}\omega_{\frac{n}{2}}^{ki} + \omega_{n}^{k} \sum_{i=0}^{\frac{n}{2}-1} a_{2i+1}\omega_{\frac{n}{2}}^{ki} \; \; \; \end{matrix} A(ωnk)=∑i=0n−1aiωnki=∑i=02n−1a2iωn2ki+ωnk∑i=02n−1a2i+1ωn2ki=∑i=02n−1a2iω2nki+ωnk∑i=02n−1a2i+1ω2nki
又由于
A(ωnk+n2)=∑i=0n−1aiωn(k+n2)i=∑i=0n2−1a2iωn2ki+ωnk+n2∑i=0n2−1a2i+1ωn2ki=∑i=0n2−1a2iωn2ki−ωnk∑i=0n2−1a2i+1ωn2ki\begin{matrix} A\left(\omega_{n}^{k + \frac{n}{2}}\right) &= \sum_{i=0}^{n-1} a_{i}\omega_{n}^{\left(k+ \frac{n}{2}\right)i}\qquad \qquad \qquad \qquad \quad \\ \\ & = \sum_{i=0}^{\frac{n}{2}-1} a_{2i}\omega_{n}^{2ki} + \omega_{n}^{k + \frac{n}{2}} \sum_{i=0}^{\frac{n}{2}-1} a_{2i+1}\omega_{n}^{2ki} \\ \\ & = \sum_{i=0}^{\frac{n}{2}-1} a_{2i}\omega_{\frac{n}{2}}^{ki} - \omega_{n}^{k} \sum_{i=0}^{\frac{n}{2}-1} a_{2i+1}\omega_{\frac{n}{2}}^{ki} \qquad \end{matrix} A(ωnk+2n)=∑i=0n−1aiωn(k+2n)i=∑i=02n−1a2iωn2ki+ωnk+2n∑i=02n−1a2i+1ωn2ki=∑i=02n−1a2iω2nki−ωnk∑i=02n−1a2i+1ω2nki
可以看出,对于k<n2k < \frac{n}{2}k<2n时,只要代入ωn2,ωn22,⋯,ωn2n2−1\omega_{\frac{n}{2}}, \omega_{\frac{n}{2}}^{2}, \cdots, \omega_{\frac{n}{2}}^{\frac{n}{2} - 1}ω2n,ω2n2,⋯,ω2n2n−1,就可以求出A(ωnk)A\left(\omega_{n}^{k}\right)A(ωnk)和A(ωnk+n2)A\left(\omega_{n}^{k + \frac{n}{2}}\right)A(ωnk+2n)。
A(ωnk)=A1(ωn2k)+ωnk⋅A2(ωn2k)A(ωnk+n2)=A1(ωn2k)−ωnk⋅A2(ωn2k)(公式1)\begin{matrix} A\left(\omega_{n}^{k}\right) \quad =& A_{1}\left(\omega_{\frac{n}{2}}^{k}\right) + \omega_{n}^{k} \cdot A_{2}\left(\omega_{\frac{n}{2}}^{k}\right) \\ \\ A\left(\omega_{n}^{k + \frac{n}{2}}\right) = & A_{1}\left(\omega_{\frac{n}{2}}^{k}\right) - \omega_{n}^{k} \cdot A_{2}\left(\omega_{\frac{n}{2}}^{k}\right) \end{matrix} (公式1) A(ωnk)=A(ωnk+2n)=A1(ω2nk)+ωnk⋅A2(ω2nk)A1(ω2nk)−ωnk⋅A2(ω2nk)(公式1)
不递归的伪代码如下所示:
void get_rev(int bit)
{for(int i=0; i<(1<<bit); i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}
/**
n为2的幂次方
op==1,DFT
op==-1,IDFT
*/
void fft(complex<double> *a,int n,int op)
{for(int i=0; i<n; i++){if(i<rev[i]){complex<double> tmp = a[i];a[i] = a[rev[i]];a[rev[i]] = tmp;}}for(int step=1; step<n; step<<=1){complex<double> wn=exp(complex<double> (0,op*PI/step));for(int j=0; j<n; j+=step<<1){complex<double> wnk(1,0);for(int k=j; k<j+step; k++){complex<double> x=a[k];complex<double> y=wnk*a[k+step];a[k]=x+y;a[k+step]=x-y;wnk*=wn;}}}if(op==-1){for(int i=0; i<n; i++)a[i]/=n;}
}
费了好大经历才把代码和原理结合在一起,在网上搜了好久都没有理解为何代码要这样写,后来在离散傅里叶变换和快速傅里叶变换课件中搜到一张图,瞬间明白,其中的子操作被称为蝴蝶操作。如下图所示,展示了一个8个点的DFT是怎样分解成4个2个点的DFT。
蝴蝶操作见下图,类似于蝴蝶形状,是不是和上述公式1的计算相符合。
FFT实现多项式乘法
当现在有多项式A(x)A\left(x\right)A(x)和多项式B(x)B\left(x\right)B(x),令C(x)=A(x)⋅B(x)C\left(x\right) = A\left(x\right) \cdot B\left(x\right)C(x)=A(x)⋅B(x),那么如何求CCC的系数cjc_{j}cj呢?
先引入一个定理,函数卷积的傅里叶变换是函数傅里叶变换的乘积。C(x)C\left(x\right)C(x)中次数为jjj的项由A(x)A \left(x\right)A(x)中次数为kkk的项和B(x)B\left(x\right)B(x)中次数j−kj-kj−k为的项相乘得到。那么我们先通过C(ωn)=A(ωn)B(ωn,)C\left(\omega_{n}\right) = A\left(\omega_{n}\right) B\left(\omega_{n},\right)C(ωn)=A(ωn)B(ωn,),然后在通过IDFT计算cjc_{j}cj。
fft(a,n,1);fft(b,n,1);for(int i=0; i<s; i++) a[i]*=b[i];fft(a,n,-1);
备注
欧拉公式eix=cosx+i⋅sinxe^{ix} = cos x + i \cdot sin xeix=cosx+i⋅sinx
参考
傅里叶变换
离散傅里叶变换
单位根
快速傅里叶变换
离散傅里叶变换和快速傅里叶变换
利用快速傅里叶计算多项式相乘相关推荐
- 多项式相乘快速算法原理及相应C代码实现
最近认真研究了一下算法导论里面的多项式乘法的快速计算问题,主要是用到了FFT,自己也实现了一下,总结如下. 1.多项式乘法 两个多项式相乘即为多项式乘法,例如:3*x^7+4*x^5+1*x^2+5与 ...
- 算法学习三:使用霍纳规则计算多项式
霍纳规则中的算法思想 在<算法导论>第二章的思考题中,描述了利用霍纳规则计算多项式的方法.以前自己在写程序的时候都是傻傻的简单粗暴地直接上了,看到这个算法的时候眼前一亮,就多看了一些,果然 ...
- 【PAT甲级 多项式相乘】1009 Product of Polynomials (25 分) C++ 全部AC
题目 思路 维护三个数组: arrA[1001]存储第一行数据 arrB[1001]存储第二行数据 c[1000000]存储计算结果 数组下标表示多项式的指数,数组存的内容表示多项式的系数 将arrA ...
- 信息学奥赛一本通 1012:计算多项式的值 | OpenJudge NOI 1.3 07
[题目链接] ybt 1012:计算多项式的值 OpenJudge NOI 1.3 07:计算多项式的值 [题目考点] 1. 计算表达式书写 了解*的运算优先级比+高. 了解()可以改变运算优先级 2 ...
- 利用FFT计算非平稳随机信号的WVD分布
up目录 一.理论基础 二.核心程序 三.测试结果 一.理论基础 fft: 快速傅里叶变换 (fast Fourier transform), 即利用计算机计算离散傅里叶变换(DFT)的高效.快速计算 ...
- python利用近似公式计算π_python如何利用公式计算π
python利用公式计算π的方法:首先导入数学模块及时间模块:然后计算Pi精确到小数点后几位数,代码为[print('n{:=^70}'.format('计算开始'))]:最后完成计算,代码为[pri ...
- BUAA(2021春)多项式相乘
BUAA数据结构第三次编程题--多项式相乘 看前须知 第三次上机题汇总 题目内容 问题描述 输入形式 输出形式 样例 样例说明 题解 易错点和难点 参考代码 补充测试的数据 看前须知 要点介绍和简要声 ...
- 霍纳法则——计算多项式的值
例:,计算x=7时p(x)的值. 平时计算我们都会想到直接将x=代入方程中直接求解.但是这样子的话计算量很大,效率不高.而使用霍纳法则,则可以提高计算的效率. 一.霍纳法则的步骤 1.首先建立一个二维 ...
- python计算csv文件内的数据_Python利用pandas计算多个CSV文件数据值的实例
功能:扫描当前目录下所有CSV文件并对其中文件进行统计,输出统计值到CSV文件 pip install pandas import pandas as pd import glob,os,sys in ...
- Matlab计算多项式的值(数值)
MATLAB 中,多项式用一个行向量表示,行向量的元素值为多项式系数按幂次的降序排列: 例如多项式, P(x) = 2*x^4 + 3*x^3 - 2*x^2 + 7*x + 11 可表示为, p = ...
最新文章
- eclipse 向HDFS中创建文件夹报错 permission denied
- shell脚本——注释(单行注释 多行注释)
- ARC122C-Calculator【乱搞,构造】
- Serverless 工作流给人工智能带来了哪些变化?
- rw1601可以用C语言写程序吗,用8051+1601LCD设计的整型计算器讲解.doc
- 某云商城发卡网源码 带视频教程
- QQ for Linux 复活,微信 for Linux 还远吗?
- binarytreenode”使用 类 模板 需要 模板 参数列表_0基础掌握Django框架(7)Django模板介绍...
- vue中页面跳转传值_vue 页面跳转传参
- 最后1天!阿里云双11拼团入官方热荐团直享最低折扣!还有机会瓜分百万现金!...
- fpga图片灰度处理
- 数据分析新手小白入门学习指南,这五大知识清单值得收藏
- u盘被隐藏的文件怎么恢复
- 企业微信服务号注册认证支付接入流程
- Shadow Map阴影贴图技术之探
- 发qq邮件被对方服务器拒绝,QQ被对方拉黑了。我发QQ邮件对对方能收到吗?
- 电脑重装系统简单小白教程
- 迅雷11抢先体验版,免费2T空间可离线下载高速取回
- TUF Notary
- 10个Kubernetes发行版引领了容器革命