多项式乘法
我们知道,多项式可以表示成:
A=∑i=0naixi A = ∑ i = 0 n a i x i
的形式。
对于两个多项式 A(x) A ( x ) 和 B(x) B ( x ) ,我们可以计算乘积 A⋅B A ⋅ B :
A⋅B=∑i=0sizeA∑j=0sizeBaibjxi+j A ⋅ B = ∑ i = 0 s i z e A ∑ j = 0 s i z e B a i b j x i + j
但是,这样算是 O(sizeA⋅sizeB) O ( s i z e A ⋅ s i z e B ) 的,太慢了,怎么办?
我们需要换一条思路。
首先,我们得知道一个东西:多项式的点值表示法。
我们把上面的称为多项式的系数表示法,而点值表示法就是:
若 A A 多项式的次数为
,则任取 n n 个不相同的
,求出 A A 多项式的
。记为:
<(x0,A(x0)),(x1,A(x1)),⋯,(xn,A(xn))> < ( x 0 , A ( x 0 ) ) , ( x 1 , A ( x 1 ) ) , ⋯ , ( x n , A ( x n ) ) >
<script type="math/tex; mode=display" id="MathJax-Element-15"><(x_0,A(x_0)),(x_1,A(x_1)),\cdots,(x_n,A(x_n))></script>显然,一个有
n+1 n + 1
个点的点对唯一表示一个
n n
次多项式。
对于一个点值表示法下多项式
<(x0,B(x0)),(x1,B(x1)),⋯,(xn,B(xn))> < ( x 0 , B ( x 0 ) ) , ( x 1 , B ( x 1 ) ) , ⋯ , ( x n , B ( x n ) ) >
<script type="math/tex; mode=display" id="MathJax-Element-19"><(x_0,B(x_0)),(x_1,B(x_1)),\cdots,(x_n,B(x_n))></script>它们的乘积是
<(x0,A(x0)⋅B(x0)),(x1,A(x1)⋅B(x1)),⋯,(xn,A(xn)⋅B(xn))> < ( x 0 , A ( x 0 ) ⋅ B ( x 0 ) ) , ( x 1 , A ( x 1 ) ⋅ B ( x 1 ) ) , ⋯ , ( x n , A ( x n ) ⋅ B ( x n ) ) >
<script type="math/tex; mode=display" id="MathJax-Element-20"><(x_0,A(x_0)\cdot B(x_0)),(x_1,A(x_1)\cdot B(x_1)),\cdots,(x_n,A(x_n)\cdot B(x_n))></script>可以看出点值表示法的多项式相乘是
O(n) O ( n )
的。
等等,我们好像找到了一个突破口!
为啥不把原来的系数表示法下的多项式转化成点值表示法呢?
仔细想一想:系数表示法与点值表示法互相转换,这个步骤好像是 O(n2) O ( n 2 ) 的。
而FFT(快速傅里叶变换)就是为了优化这个 O(n2) O ( n 2 ) 。
PS:对于 O(n2) O ( n 2 ) 的点值表示法转化成系数表示法可以看百度百科中关于插值法的介绍。
FFT(快速傅里叶变换)
如果未特别说明,那么下面的多项式次数将是 2k−1 2 k − 1 的形式。
如果不是关键部分的公式或定理,不提供证明,自己出门右转百度。
首先介绍两个概念:
DFT(离散傅里叶变换)是将多项式由系数表示法转化成点值表示法;
IDFT(离散傅里叶逆变换)是将多项式由点值表示法转化成系数表示法;
而FFT就是上述两种变换的优化。
DFT部分
前置技能:
下面的内容将会提到复数,不会的可以参考百度百科中关于复数的介绍;
为了介绍FFT中的DFT部分,首先要介绍的是一个概念:单位根。
单位根:若有
zn=1 z n = 1
此时将
z z
称为
次单位根。
若有 z∈R z ∈ R ,显然, z z 可以等于 ,如果 n n 是偶数,则 还可以等于 −1 − 1 。
我们把范围扩大到 z∈C z ∈ C ,那么,我们可以得到 n n 个复数,它们将复平面上的单位圆等分成 份。
为了表示 n n 次单位根,我们引入一个公式。
欧拉公式:
如果我们令:
ωn=