1、FFT前言
FFT全称(Fast Fourier Transformation)快速傅里叶变换
NTT全称(Fast Number Theoretical Transformation)快速数论变换
1.1、引入问题
如何计算多项式 f ( x ) f(x) f(x)与 g ( x ) g(x) g(x)的乘积?
例如:计算 f ( x ) = 1 + x 2 + x 3 f(x)=1+x^2+x^3 f(x)=1+x2+x3与 g ( x ) = 1 + x 5 + x 6 + x 7 g(x)=1+x^5+x^6+x^7 g(x)=1+x5+x6+x7的乘积
通常而言,我们思考暴力的算法:
f ( x ) × g ( x ) = 1 × ( 1 + x 5 + x 6 + x 7 ) + x 2 × ( 1 + x 5 + x 6 + x 7 ) + x 3 × ( 1 + x 5 + x 6 + x 7 ) = 1 + x 5 + x 6 + x 7 + x 2 + x 7 + x 8 + x 9 + x 3 + x 8 + x 9 + x 10 = 1 + x 2 + x 3 + x 5 + x 6 + 2 x 7 + 2 x 8 + 2 x 9 + x 10 \begin{aligned} f(x)\times g(x)&=1\times(1+x^5+x^6+x^7)+x^2\times(1+x^5+x^6+x^7)+x^3\times(1+x^5+x^6+x^7) \\& =1+x^5+x^6+x^7+x^2+x^7+x^8+x^9+x^3+x^8+x^9+x^{10} \\& =1+x^2+x^3+x^5+x^6+2x^7+2x^8+2x^9+x^{10} \end{aligned} f(x)×g(x)=1×(1+x5+x6+x7)+x2×(1+x5+x6+x7)+x3×(1+x5+x6+x7)=1+x5+x6+x7+x2+x7+x8+x9+x3+x8+x9+x10=1+x2+x3+x5+x6+2x7+2x8+2x9+x10
显而易见,这个算法的时间复杂度是 O ( n 2 ) O(n^2) O(n2)
那么有快速的计算方法吗?
当然是有的,于1965年由J.W.库利和T.W.图基提出FFT算法,可以在 O ( n l o g n ) O(nlogn) O(nlogn)的时间复杂度下计算出答案。
1.2、定义一些记号:
# f ( x ) f(x) f(x)表示一个多项式
**#**对于一个 n n n次多项式 f ( x ) f(x) f(x)的第 m m m项,记为 f [ m ] f[m] f[m];换言之, f ( x ) = ∑ i = 0 n f [ i ] x i f(x)=\sum_{i=0}^{n}f[i]x^i f(x)=∑i=0nf[i]xi
1.3、多项式乘法理解
例如:现有两个 n n n次多项式 f ( x ) f(x) f(x)与 g ( x ) g(x) g(x),需要计算这两个多项式的乘积.
不妨设 h ( x ) = f ( x ) × g ( x ) h(x)=f(x)\times g(x) h(x)=f(x)×g(x)
那么通过1.1中的例子,我们可以发现,其实 h [ k ] = ∑ i = 0 k f [ i ] g [ k − i ] h[k]=\sum_{i=0}^{k}f[i]g[k-i] h[k]=∑i=0kf[i]g[k−i]
换言之,我们可以理解为:要想乘出 x k x^k xk,需要 f ( x ) f(x) f(x)的 x i x^i xi项和 g ( x ) g(x) g(x)的 x k − i x^{k-i} xk−i项相乘,然后将他们的相乘的贡献累加便是 h [ k ] h[k] h[k]
1.4、卷积
将形如: h [ k ] = ∑ i ⊕ j f [ i ] g [ j ] h[k]=\sum_{i⊕j}f[i]g[j] h[k]=∑i⊕jf[i]g[j]的式子称为卷积,其中⊕在数学中称为假设符号(假设什么运算)
例如,在多项式乘法中,为加法卷积: h [ k ] = ∑ i + j f [ i ] g [ j ] h[k]=\sum_{i+j}f[i]g[j] h[k]=∑i+jf[i]g[j]
因此,对于卷积可以利用FFT构造多项式进行计算!
2、FFT思想
2.1、多项式的点值表达式
我们可以从
得出推论:在平面直角坐标系中, n + 1 n+1 n+1个点就能唯一确定一个 n n n次多项式。
因此,对于一个 n n n次多项式 f ( x ) f(x) f(x)而言,我们可以用 n + 1 n+1 n+1个点来描述一个多项式;换言之,两个多项式相乘,等价于两个多项式的对应点值相乘。(点值的乘法对应着整个多项式的乘法,也就是浓缩了整个多项式)
将能唯一表达一个 n n n次多项式 f ( x ) f(x) f(x)的 n + 1 n+1 n+1个点,称为 f ( x ) f(x) f(x)的点值表达式。
$$
例如:n次多项式f(x)的点值表达式为:\
\left{(x_1,f(x_1)),(x_2,f(x_2)),(x_3,f(x_3))…(x_{n+1},f(x_{n+1}))\right}
$$
由于 n n n次多项式 f ( x ) f(x) f(x)与 n n n次多项式 g ( x ) g(x) g(x),他们的乘积一定是一个 2 n + 1 2n+1 2n+1次的多项式,所以我们只需要对 f ( x ) f(x) f(x)和 g ( x ) g(x) g(x)找到 2 n + 1 2n+1 2n+1个对应的点,然后将他们对应点的纵坐标相乘,便可以确定多项式 f ( x ) × g ( x ) f(x)\times g(x) f(x)×g(x)的点值表达式;
例如: 2 n + 1 次多项式 f ( x ) × g ( x ) 的点值表达式为: { ( x 1 , f ( x 1 ) g ( x 1 ) ) , ( x 2 , f ( x 2 ) g ( x 2 ) ) , ( x 3 , f ( x 3 ) g ( x 3 ) ) . . . . . . ( x 2 n + 1 , f ( x 2 n + 1 ) g ( x 2 n + 1 ) ) } 例如:2n+1次多项式f(x)\times g(x)的点值表达式为: \\ \left\{(x_1,f(x_1)g(x_1)),(x_2,f(x_2)g(x_2)),(x_3,f(x_3)g(x_3))......(x_{2n+1},f(x_{2n+1})g(x_{2n+1}))\right\} 例如:2n+1次多项式f(x)×g(x)的点值表达式为:{
(x1,f(x1)g(x1)),(x2,f(x2)g(x2)),(x3,f(x3)g(x3))......(x2n+1,f(x2n+1)g(x2n+1))}
最后我们将多项式 f ( x ) × g ( x ) f(x)\times g(x) f(x)×g(x)的点值表达式转化为多项式的一般表达式(系数表达式)便得到了我们想要的多项式乘积了。
而这也是FFT算法的流程
2.2、DFT&IDFT
DFT:把多项式的系数表达式转化为点值表达式的变换过程
IDFT:把多项式的点值表达式转化为系数表达式的变换过程
下面这张图便对比了FFT算法与暴力算法的区别
3、FFT的实现
3.1、DFT
由于我们想要多项式的点值表达式,所以我们需要利用一些方法获得多项式的上的点;但是细想一下会发现,
如果对于一个 n 次多项式 f ( x ) ,我们任选 n + 1 个点的横坐标 x 1 , x 2 , x 3 . . . . x n , x n + 1 , 再分别计算其对应的纵坐标 f ( x 1 ) , f ( x 2 ) , f ( x 3 ) . . . f ( x n ) , f ( x n + 1 ) 的时间复杂度是 O ( n 2 ) ( 由于计算每个横坐标的时间复杂度是 O ( n ) , 但是有 n 个点 ) ; 如果对于一个n次多项式f(x),我们任选n+1个点的横坐标x_1,x_2,x_3....x_n,x_{n+1},\\再分别计算其对应的纵坐标f(x_1),f(x_2),f(x_3)...f(x_n),f(x_{n+1})的时间复杂度是O(n^2)\\(由于计算每个横坐标的时间复杂度是O(n),但是有n个点); 如果对于一个n次多项式f(x),我们任选n+1个点的横坐标x1,x2,x3....xn,xn+1,再分别计算其对应的纵坐标f(x1),f(x2),f(x3)...f(xn),f(xn+1)的时间复杂度是O(n2)(由于计算每个横坐标的时间复杂度是O(n),但是有n个点);
这样一来,我们通过随便找 n + 1 n+1 n+1个点的方式,会让我们的时间复杂度和暴力没什么区别!
那么有快速找到 n + 1 n+1 n+1个点的方法么?答案当然是有的!
我们可以这样考虑问题:
3.1.1、Good idea 1:函数的奇偶性
首先,我们不考虑一般多项式,只考虑一个简单的多项式 f ( x ) = x 2 f(x)=x^2 f(x)=x2
那么,如果我们要从 f ( x ) f(x) f(x)中选择8个点,我们如何选择?
这里我们需要注意 f ( x ) f(x) f(x)的一个优美的性质———— f ( x ) f(x) f(x)是偶函数,所以我们计算出一个点 ( x , f ( x ) ) (x,f(x)) (x,f(x))后,便可以立马知道点 ( − x , f ( x ) ) (-x,f(x)) (−x,f(x))也一定是 y ( x ) y(x) y(x)上的点。
那么,如果 f ( x ) = x 3 f(x)=x^3 f(x)=x3,应该如何处理喃?
显而易见,我们会发现 f ( x ) = x 3 f(x)=x^3 f(x)=x3是一个奇函数,它与偶函数的区别在于,奇函数上的点是关于原点对称的,换言之,如果我们计算出点 ( x , f ( x ) ) (x,f(x)) (x,f(x)),那就可以知道点 ( − x , − f ( x ) ) (-x,-f(x)) (−x,−f(x))也是多项式上的点!
既然这样,对于多项式 f ( x ) = 1 + 2 x 2 + 3 x 3 + 4 x 4 + 5 x 5 f(x)=1+2x^2+3x^3+4x^4+5x^5 f(x)=1+2x2+3x3+4x4+5x5
那么我可以将 f ( x ) f(x) f(x)中按幂数分离,因此可得 f ( x ) = ( 1 + 2 x 2 + 4 x 4 ) + ( 3 x 3 + 5 x 5 ) f(x)=(1+2x^2+4x^4)+(3x^3+5x^5) f(x)=(1+2x2+4x4)+(3x3+5x5)
不妨设 f o ( x ) = 1 + 2 x + 4 x 2 , f e ( x ) = 3 x + 5 x 2 那么有: f ( x ) = ( 1 + 2 x 2 + 4 x 4 ) + x ( 3 x 2 + 5 x 4 ) = f o ( x 2 ) + x f e ( x 2 ) 不妨设f_o(x)=1+2x+4x^2,f_e(x)=3x+5x^2\\ 那么有: \begin{aligned} f(x)&=(1+2x^2+4x^4)+x(3x^2+5x^4) \\&=f_o(x^2)+xf_e(x^2) \end{aligned} 不妨设fo(x)=1+2x+4x2,fe(x)=3x+5x2那么有:f(x)=(1+2x2+4x4)+x(3x2+5x4)=fo(x2)+xfe(x2)
这样,对于 f o ( x 2 ) , f e ( x 2 ) f_o(x^2),f_e(x^2) fo(x2),fe(x2)都是关于 x 2 x^2 x2的多项式,我们便可以利用函数的奇偶性快速找到6个点,然后利用点值表达式的计算方式计算出该函数的点值表达式。
然后我们可以推广到一般多项式 f ( x ) = a 0 + a 1 x + a 2 x 2 + . . . . . a n x n f(x)=a_0+a_1x+a_2x^2+.....a_nx^{n} f(x)=a0+a1x+a2x2+.....anxn
可以设 f ( x ) 按照幂数分离,将偶幂数部分设为 f e ( x 2 ) , 将奇幂数部分提出一个 x 后的部分设为 f o ( x 2 ) 所以: f ( x ) = f e ( x 2 ) + x f o ( x 2 ) 然而对于, f e ( x ) 与 f o ( x ) 而言,最高次都变成了 n 2 − 1 同理可以推出重要结论 { f ( x i ) = f e ( x i 2 ) + x i f o ( x i 2 ) f ( − x i ) = f e ( x i 2 ) − x i f o ( x i 2 ) 这样一来,问题得到了简化: 分别求 f e ( x 2 ) , f o ( x 2 ) 在 x 1 2 , x 2 2 . . . . x n 2 2 的值 , 因为这样我们便可以得到 ± x 1 , ± x 2 . . . ± x n 2 这 n 个点 可以设f(x)按照幂数分离,将偶幂数部分设为f_e(x^2),将奇幂数部分提出一个x后的部分设为f_o(x^2)\\ 所以:f(x)=f_e(x^2)+xf_o(x^2)\\ 然而对于,f_e(x)与f_o(x)而言,最高次都变成了\frac{n}{2}-1\\ 同理可以推出重要结论 \begin{cases} f(x_i)=f_e(x_i^2)+x_if_o(x_i^2)\\ f(-x_i)=f_e(x_i^2)-x_if_o(x_i^2) \end{cases} \\ 这样一来,问题得到了简化: \\分别求f_e(x^2),f_o(x^2)在x_1^2,x_2^2....x_{\frac{n}{2}}^2的值,因为这样我们便可以得到\pm x_1,\pm x_2...\pm x_\frac{n}{2}这n个点 可以设f(x)按照幂数分离,将偶幂数部分设为fe(x2),将奇幂数部分提出一个x后的部分设为fo(x2)所以:f(x)=fe(x2)+xfo(x2)然而对于,fe(x)与fo(x)而言,最高次都变成了2n−1同理可以推出重要结论{
f(xi)=fe(xi2)+xifo(xi2)f(−xi)=fe(xi2)−xifo(xi2)这样一来,问题得到了简化:分别求fe(x2),fo(x2)在x12,x22....x2n2的值,因为这样我们便可以得到±x1,±x2...±x2n这n个点
而对于 f e ( x ) 与 f o ( x ) f_e(x)与f_o(x) fe(x)与fo(x),我们可以重复 f ( x ) f(x) f(x)的奇偶分离的方式;这样我们可以把对于 n n n次多项式转化为求 n 2 \frac{n}{2} 2n次多项式(类似分治的思想),所以这个思路下,求多项式点值表达式的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)
因此,只要我们最后找到的点为合法的点,那么算法就是合法的!
但是这里出现了一个问题:对于 f ( x ) f(x) f(x)中的 [ ± x 1 , ± x 2 . . . ± x n 2 ] [\pm x_1,\pm x_2...\pm x\frac{n}{2}] [±x1,±x