本文介绍NTT算法的演变过程和主流方案以及部分工程优化方法。
1 算法介绍
1.1 多项式乘法引入
1.1.1 多项式乘法的表示方式
一般 n n n 次多项式如公式(1)所示。多项式具有两种表示方法分别是系数表示法和点值表示法。
A ( x ) = a 0 + a 1 x 1 + a 2 x 2 + … + a n − 1 x n − 1 + a n x n = ∑ i = 0 n a i x i (1) A(x)=a_0+a_1x^1+a_2x^2+\ldots +a_{n-1}x^{n-1}+a_nx^n=\sum^{n}_{i=0}a_ix^i \tag{1} A ( x ) = a 0 + a 1 x 1 + a 2 x 2 + … + a n − 1 x n − 1 + a n x n = i = 0 ∑ n a i x i ( 1 )
系数表示法:
任意多项式都可以通过一组系数所确定,而这组系数所组成的向量也叫做系数向量,通过系数向量表示一个多项的方式也叫做系数表示法。例如公式(1)中 A ( x ) A(x) A ( x ) 的系数向量为:a = [ a 0 , a 1 , … , a n − 1 , a n ] \mathbf{a}=[a_0 ,a_1,\ldots,a_{n-1},a_n] a = [ a 0 , a 1 , … , a n − 1 , a n ] 。
点值表示法:
对于一个已知的多项式例如公式(1)中的 A ( x ) A(x) A ( x ) ,将 x i x_i x i 代进去可以得到一个确定的值 y i y_i y i ,如:y 0 = A ( x 0 ) y_0=A(x_0) y 0 = A ( x 0 ) 。且可将 ( x 0 , y 0 ) (x_0,y_0) ( x 0 , y 0 ) 看作是坐标系上的一个点。可将任意多(互不相等)的自变量 ( x 1 , x 2 , … , x n − 1 , x n ) ( x_1,x_2,\ldots,x_{n-1}, x_n) ( x 1 , x 2 , … , x n − 1 , x n ) 代入到 A ( x ) A(x) A ( x ) 中,从而得到更多的点:( x 1 , y 1 ) , ( x 2 , y 2 ) , … ( x n , y n ) (x_1,y_1),(x_2,y_2),\ldots(x_n,y_n) ( x 1 , y 1 ) , ( x 2 , y 2 ) , … ( x n , y n ) 。通过 n + 1 n+1 n + 1 个不同点组成的点集 P = { ( x 0 , y 0 ) , ( x 1 , y 1 ) , … , ( x n , y n ) } P=\{(x_0,y_0),(x_1,y_1),\ldots,(x_n,y_n)\} P = {( x 0 , y 0 ) , ( x 1 , y 1 ) , … , ( x n , y n )} ,唯一确定一个 n 次多项式,该方式也叫做多项式的点值表示法。
1.1.2 多项式乘法
已知两个多项式 A ( x ) A(x) A ( x ) 和 B ( x ) B(x) B ( x ) ,分别是 n n n 次多项式和 m 次多项式。
A ( x ) = ∑ i = 0 n a i x i B ( x ) = ∑ i = 0 m b i x i \begin{align}
A(x)=\sum^{n}_{i=0}a_ix^i\\
B(x)=\sum^{m}_{i=0}b_ix^i
\end{align} A ( x ) = i = 0 ∑ n a i x i B ( x ) = i = 0 ∑ m b i x i
公式(2)和公式(3)相乘得到一个最高为 n + m n+m n + m 次的多项式 C ( x ) C(x) C ( x ) 。系数向量:c = [ c 0 , c 1 , … , c m + n ] \mathbf{c}=[c_0 ,c_1,\ldots,c_{m+n}] c = [ c 0 , c 1 , … , c m + n ] 。
系数乘法:
系数乘法,将两个多项式的系数相乘,系数相乘,如公式(5)所示。不难看出时间复杂为 O ( n 2 ) O(n^2) O ( n 2 ) 。整理可得公式(6)。
C i + j = ∑ i = 0 n ∑ j = 0 m a i b j C i = ∑ j = 0 i a j b i − j \begin{align}
C_{i+j}= \sum_{i=0}^{n} \sum_{j=0}^{m} a_i b_j \\
C_i=\sum^{i}_{j=0}a_jb_{i-j}
\end{align} C i + j = i = 0 ∑ n j = 0 ∑ m a i b j C i = j = 0 ∑ i a j b i − j
点值乘法:
点值乘法只需将要将对应的纵坐标相乘即可,但是因为新得到的多项式次数更高,所以每个因子多项式都需要提供 m + n + 1 m+n+1 m + n + 1 个点参与运算。时间复杂度为 O ( n ) O(n) O ( n ) 。
小结:
在计算多项式乘法时,点值表示的时间复杂度低。但是在计算系统中系数表示更加的常用。于是很自然的想到,在进行乘法时从系数表示转换到点值表示,使用点值表示法进行乘法运算,然后再转换为系数表示法。但是很不幸的是,从系数表示转换到点值表示,以及点值表示到系数表示,这两个过程的时间复杂度均为 O ( n 2 ) O(n^2) O ( n 2 ) 。
1.1.3 转换优化
如果通过减少求值的点数可以较少计算时间。根据函数的奇偶性我们可以只求一半的点值,从而减少一半的计算量。这里为了方便表示设一个 n − 1 n-1 n − 1 次的一般多项式,n = 2 a , a ∈ N n=2^a , a\in \mathbb{N} n = 2 a , a ∈ N 。
P ( x ) = p 0 + p 1 x 1 + p 2 x 2 + … + p n − 1 x n − 1 = ∑ i = 0 n − 1 p i x i P(x)=p_0+p_1x^1+p_2x^2+\ldots +p_{n-1}x^{n-1}=\sum^{n-1}_{i=0}p_ix^i P ( x ) = p 0 + p 1 x 1 + p 2 x 2 + … + p n − 1 x n − 1 = i = 0 ∑ n − 1 p i x i
将偶次项和奇数项分别组合:
P ( x ) = ( p 0 + p 2 x 2 … + p n − 2 x n − 2 ) + ( p 1 x 1 + … + p n − 1 x n − 1 ) P(x)=(p_0+p_2x^2\ldots+p_{n-2}x^{n-2})+(p_1x^1+\ldots +p_{n-1}x^{n-1}) P ( x ) = ( p 0 + p 2 x 2 … + p n − 2 x n − 2 ) + ( p 1 x 1 + … + p n − 1 x n − 1 )
对奇次项提取公因式 x x x :
P ( x ) = ( p 0 + p 2 x 2 … + p n − 2 x n − 2 ) + x ( p 1 x 0 + … + p n − 1 x n − 2 ) P(x)=(p_0+p_2x^2\ldots+p_{n-2}x^{n-2})+x(p_1x^0+\ldots +p_{n-1}x^{n-2}) P ( x ) = ( p 0 + p 2 x 2 … + p n − 2 x n − 2 ) + x ( p 1 x 0 + … + p n − 1 x n − 2 )
化简为:
P ( x ) = P e ( x 2 ) + x P o ( x 2 ) \begin{align}
P(x)=P_e(x^2)+xP_o(x^2)
\end{align} P ( x ) = P e ( x 2 ) + x P o ( x 2 )
则我们可以取 n 2 \frac{n}{2} 2 n 对相反数点,这样我们只需要计算一半的点值。
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 ) \begin{gathered}
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)
\end{gathered} 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 )
观察公式(7)不难发现,是将是将 P ( x ) P(x) P ( x ) 拆成了两个规模为 n 2 \frac{n}{2} 2 n 的 P e ( x ) P_e(x) P e ( x ) ,P o ( x ) P_o(x) P o ( x ) 。自然而然想到了递归执行。但是问题是相反数平方运算后所得结果均为正数,无法构造相反数对,无法实现递归。如果能一直保持相反数点对,那么时间复杂度可以表示为T ( n ) = T ( 2 n ) + O ( n ) T(n)=T(2n)+O(n) T ( n ) = T ( 2 n ) + O ( n ) ,即时间复杂度为 O ( n log n ) O(n\log n) O ( n log n ) 。
1.1.4 复平面单位根
既然实数域没有办法解决上面的问题,可以将起扩展到复平面。我们希望取一些点使其平方后的结果依然存在相反数对。
既然点是我们自己选的,那不如使最后一组相反数点对为 { 1 , − 1 } \{1,-1\} { 1 , − 1 } 。我们取n n n 等于 8 8 8 作为演示。不难看出最后选取的 8 8 8 个点均是 x 8 = 1 x^8=1 x 8 = 1 的解。
对于任意 n = 2 a , a ∈ N n=2^a , a\in \mathbb{N} n = 2 a , a ∈ N ,来说即为 x n = 1 x^n=1 x n = 1 。
由欧拉公式:
e i θ = cos θ + i sin θ e^{i \theta}=\cos\theta+i\sin\theta e i θ = cos θ + i sin θ
可以得到公式:
z n = 1 = cos 2 k π + i sin 2 k π = e 2 k π i , k ∈ [ 0 , n ) z^n=1=\cos2k\pi+i\sin2k\pi=e^{2k\pi i},k \in[ 0,n) z n = 1 = cos 2 k π + i sin 2 k π = e 2 k π i , k ∈ [ 0 , n )
所以:
z = e 2 k π i n = e 2 k π i n , k ∈ [ 0 , n ) z=\sqrt[n]{e^{2k\pi i}}=e^\frac{2k\pi i}{n} ,k \in[ 0,n) z = n e 2 k π i = e n 2 k π i , k ∈ [ 0 , n )
消去引理:
ω d n d k = ω n k ω n n 2 = − ω 2 = − 1 ω n k + n 2 = − ω n k \begin{gathered}
\omega_{dn}^{dk}=\omega_n^k\\
\omega_{n}^{\frac{n}{2}}=-\omega_2=-1\\
\omega_{n}^{k+\frac{n}{2}}=-\omega_n^k
\end{gathered} ω d n d k = ω n k ω n 2 n = − ω 2 = − 1 ω n k + 2 n = − ω n k
折半引理:
( ω n k ) 2 = ω n 2 k (\omega_n^k)^2=\omega_{\frac{n}{2}}^k ( ω n k ) 2 = ω 2 n k
求和引理:
∑ i = 0 n − 1 ( ω n k ) i = { 0 , k ≠ m n , m ∈ Z n , k = m n , m ∈ Z \sum_{i=0}^{n-1} (\omega_n^k)^i =
\begin{cases}
0, & k \neq mn, \ m \in \mathbb{Z} \\
n, & k = mn, \ m \in \mathbb{Z}
\end{cases} i = 0 ∑ n − 1 ( ω n k ) i = { 0 , n , k = mn , m ∈ Z k = mn , m ∈ Z
1.2 DFT 与 IDFT
经过 1.1 节的介绍已经对多项式乘法的相关内容有了相应的了解,下面开始正式介绍 D F T DFT D F T 与 I D F T IDFT I D F T ,将在 1.3 节介绍 F F T FFT F F T 与 I F F T IFFT I F F T 的相关内容。我们是从多项式乘法引入的D F T DFT D F T 和I D F T IDFT I D F T ,但是D F T DFT D F T 和I D F T IDFT I D F T 的应用的场景有很多,多项式求值、大数乘法,拉格朗日插值、矩阵乘法、中国剩余定理以及环同态。
1.2.1 离散傅里叶变换(DFT)
设有一个 n − 1 n-1 n − 1 次的多项式 P ( x ) P(x) P ( x ) :
P ( x ) = a 0 + a 1 x + a 2 x 2 + … + a n − 1 x n − 1 P(x) = a_0 + a_1x + a_2x^2 + \ldots + a_{n-1}x^{n-1} P ( x ) = a 0 + a 1 x + a 2 x 2 + … + a n − 1 x n − 1
多项式 P ( x ) P(x) P ( x ) 的 D F T DFT D F T 在单位根 ω n k = e 2 π i k / n \omega_n^k = e^{2\pi i k / n} ω n k = e 2 π ik / n 上的值计算如下:
P k = P ( ω n k ) = ∑ j = 0 n − 1 a j ω n k j k = 0 , 1 , … , n − 1 P_k = P(\omega_n^k) = \sum_{j=0}^{n-1} a_j \omega_n^{kj} \quad k = 0, 1, \ldots, n-1 P k = P ( ω n k ) = j = 0 ∑ n − 1 a j ω n k j k = 0 , 1 , … , n − 1
1.2.2 逆离散傅里叶变换(IDFT)
对 D F T DFT D F T 过程我们可以通过矩阵进行描述 y = W a y=Wa y = W a :
[ y 0 y 1 y 2 ⋮ y n − 1 ] = [ 1 1 … 1 1 ω n … ω n n − 1 1 ω n 2 … ω n 2 ( n − 1 ) ⋮ ⋮ ⋱ ⋮ 1 ω n n − 1 … ω n ( n − 1 ) 2 ] [ a 0 a 1 a 2 ⋮ a n − 1 ] \left[ \begin{array}{c}
y_0 \\
y_1 \\
y_2 \\
\vdots \\
y_{n-1} \\
\end{array} \right]
=
\left[ \begin{array}{cccc}
1 & 1 & \ldots & 1 \\
1 & \omega_n & \ldots & \omega_n^{n-1} \\
1 & \omega_n^2 & \ldots & \omega_n^{2(n-1)} \\
\vdots & \vdots & \ddots & \vdots \\
1 & \omega_n^{n-1} & \ldots & \omega_n^{(n-1)^2}
\end{array} \right]
\left[ \begin{array}{c}
a_0 \\
a_1 \\
a_2 \\
\vdots \\
a_{n-1} \\
\end{array} \right] y 0 y 1 y 2 ⋮ y n − 1 = 1 1 1 ⋮ 1 1 ω n ω n 2 ⋮ ω n n − 1 … … … ⋱ … 1 ω n n − 1 ω n 2 ( n − 1 ) ⋮ ω n ( n − 1 ) 2 a 0 a 1 a 2 ⋮ a n − 1
其中 V n V_n V n 是一个范德蒙矩阵,是一个可逆矩阵,因此 I D F T IDFT I D F T :a = W − 1 y a=W^{-1}y a = W − 1 y 。
下面求取:W n − 1 W_n^{-1} W n − 1 。矩阵 W W W 可以表示为:W j k = ω n j k W_{jk}=ω_ n^{jk} W j k = ω n j k 。W j k − 1 W_{jk}^{-1} W j k − 1 的值应该是 W j k W_{jk} W j k 的共轭除以 n n n 。由 ω n \omega _n ω n 的性质,我们知道 ω n k \omega _n^k ω n k 的共轭是 ω n − k \omega _n^{-k} ω n − k 。因此 W j k − 1 = 1 n ω n − j k W_{jk}^{-1}=\frac{1}{n}ω_ n^{-jk} W j k − 1 = n 1 ω n − j k 。所以 D F T DFT D F T 和 I D F T IDFT I D F T 的时间复杂一致,甚至计算流程基本一致。
a j = 1 n ∑ j = 0 n − 1 y j ω n − j k k = 0 , 1 , … , n − 1 a_j= \frac{1}{n}\sum_{j=0}^{n-1} y_j ω_ n^{-jk} \quad k = 0, 1, \ldots, n-1 a j = n 1 j = 0 ∑ n − 1 y j ω n − j k k = 0 , 1 , … , n − 1
1.3 快速傅里叶变化(FFT)
其实 F F T FFT F F T 的思路在 1.1.3 节中进行了介绍只是并不是很完善,下面进行一个较为细致的描述。详细介绍 FFT 的递归实现和迭代实现。
1.3.1 FFT 递归实现
将单位根带入公式(7)得:
P ( ω n k ) = P e ( ( ω n k ) 2 ) + ω n k P o ( ( ω n k ) 2 ) k ∈ [ 0 , n ) \begin{align}
P(\omega_n^k)=P_e((\omega_n^k)^2)+{\omega_n^k}P_o((\omega_n^k)^2) \quad k \in[ 0,n)
\end{align} P ( ω n k ) = P e (( ω n k ) 2 ) + ω n k P o (( ω n k ) 2 ) k ∈ [ 0 , n )
根据消去引理推导出得对称性以及折半引理对公式(8)进行化简。取 k ∈ [ 0 , n 2 ) k \in[0,\frac{n}{2}) k ∈ [ 0 , 2 n ) 。
P ( ω n k ) = P e ( ( ω n k ) 2 ) + ω n k P o ( ( ω n k ) 2 ) = P e ( ω n 2 k ) + ω n k P o ( ω n 2 k ) \begin{align*}
P(\omega_{n}^{k})&=P_{e}((\omega_{n}^{k})^2)+{\omega_{n}^{k}}P_{o}((\omega_{n}^{k})^2)\\
& =P_{e}(\omega_{\frac{n}{2}}^{k})+{\omega_{n}^{k}}P_{o}(\omega_{\frac{n}{2}}^{k})\\
\end{align*} P ( ω n k ) = P e (( ω n k ) 2 ) + ω n k P o (( ω n k ) 2 ) = P e ( ω 2 n k ) + ω n k P o ( ω 2 n k )
P ( ω n k + m ) = P e ( ( ω n k + m ) 2 ) + ω n k + m P o ( ( ω n k + m ) 2 ) = P e ( ω n 2 k + m ) + ω n k + m P o ( ω n 2 k + m ) = P e ( − ω n 2 k ) − ω n k P o ( − ω n 2 k ) = P e ( ω n 2 k ) − ω n k P o ( ω n 2 k ) \begin{align*}
P(\omega_{n}^{k+m})&=P_{e}((\omega_{n}^{k+m})^2)+\omega_{n}^{k+m}P_{o}((\omega_{n}^{k+m})^2)\\
& =P_{e}(\omega_{\frac{n}{2}}^{k+m})+\omega_{n}^{k+m}P_{o}(\omega_{\frac{n}{2}}^{k+m})\\
& =P_{e}(-\omega_{\frac{n}{2}}^{k})-\omega_{n}^{k}P_{o}(-\omega_{\frac{n}{2}}^{k})\\
& =P_{e}(\omega_{\frac{n}{2}}^{k})-\omega_{n}^{k}P_{o}(\omega_{\frac{n}{2}}^{k})
\end{align*} P ( ω n k + m ) = P e (( ω n k + m ) 2 ) + ω n k + m P o (( ω n k + m ) 2 ) = P e ( ω 2 n k + m ) + ω n k + m P o ( ω 2 n k + m ) = P e ( − ω 2 n k ) − ω n k P o ( − ω 2 n k ) = P e ( ω 2 n k ) − ω n k P o ( ω 2 n k )
即公式(9-10),这两个式子也被称为 CT(Cooley-Tukey)蝶形操作,ω n k \omega_{n}^{k} ω n k 被成为转换因子。
P ( ω n k ) = P e ( ω n 2 k ) + ω n k P o ( ω n 2 k ) P ( ω n k + m ) = P e ( ω n 2 k ) − ω n k P o ( ω n 2 k ) \begin{align}
P(\omega_{n}^{k})=P_{e}(\omega_{\frac{n}{2}}^{k})+{\omega_{n}^{k}}P_{o}(\omega_{\frac{n}{2}}^{k})\\
P(\omega_{n}^{k+m})=P_{e}(\omega_{\frac{n}{2}}^{k})-\omega_{n}^{k}P_{o}(\omega_{\frac{n}{2}}^{k})
\end{align} P ( ω n k ) = P e ( ω 2 n k ) + ω n k P o ( ω 2 n k ) P ( ω n k + m ) = P e ( ω 2 n k ) − ω n k P o ( ω 2 n k )
现在给出递归版本得算法:
Algorithm 1 Recursion FFT Require: P = [ p 0 , p 1 , … , p n − 1 ] n = 2 a , a ∈ N function FFT_R ( P ) n ← l e n ( P ) if n = = 1 return P endif ω n ← e 2 π i n P e ← [ p 0 , p 2 , … , p n − 2 ] P o ← [ p 1 , p 3 , … , p n − 1 ] y e ← F F T _ R ( P e ) y o ← F F T _ R ( P o ) for k to n 2 − 1 y [ k ] = y e [ k ] + ω n k y o [ k ] y [ k + n 2 ] = y e [ k ] − ω n k y o [ k ] endfor return y end function \begin{array}{ll}
\hline
\textbf{Algorithm 1} & \text{ Recursion FFT}\\
\hline
\textbf{Require:} & P=[p_0,p_1,\ldots,p_{n-1}]\quad n=2^{a},a\in\mathbb{N}\\
\textbf{function} & \text{FFT\_R}(P)\\
&n\leftarrow len(P)\\
&\text{if}\quad n==1 \\
&\quad \text{return}\quad P\\
&\text{endif}\\
&\omega_n \leftarrow e^{\frac{2\pi i}{n}}\\
&P_e\leftarrow [p_0,p_2,\ldots,p_{n-2}]\\
&P_o\leftarrow [p_1,p_3,\ldots,p_{n-1}]\\
&y_e\leftarrow FFT\_R(P_e)\\
&y_o\leftarrow FFT\_R(P_o)\\
& \text{for} \quad k \quad \text{to} \quad \frac{n}{2}-1\\
&\quad y[k]=y_e[k]+\omega_n^ky_o[k]\\
&\quad y[k+\frac{n}{2}]=y_e[k]-\omega_n^ky_o[k]\\
&\text{endfor}\\
&\text{return} \quad y \\
\textbf{end function} & \\
\hline &
\end{array} Algorithm 1 Require: function end function Recursion FFT P = [ p 0 , p 1 , … , p n − 1 ] n = 2 a , a ∈ N FFT_R ( P ) n ← l e n ( P ) if n == 1 return P endif ω n ← e n 2 π i P e ← [ p 0 , p 2 , … , p n − 2 ] P o ← [ p 1 , p 3 , … , p n − 1 ] y e ← F F T _ R ( P e ) y o ← F F T _ R ( P o ) for k to 2 n − 1 y [ k ] = y e [ k ] + ω n k y o [ k ] y [ k + 2 n ] = y e [ k ] − ω n k y o [ k ] endfor return y
蝶形操作:
蝶形操作得名于其数据流图的形状,上面的推导过程出现的是 CT 蝶形变换,还有一种 GS 蝶形变换。
通过公式(9-10)我们可以推导出 GS 蝶形变换的形式。将公式(9)和(10)分别加减运算得:
P ( ω n k ) + P ( ω n k + m ) = 2 P e ( ω n 2 k ) P ( ω n k ) − P ( ω n k + m ) = 2 ω n k P o ( ω n 2 k ) \begin{gathered}
P(\omega_{n}^{k}) + P(\omega_{n}^{k+m}) = 2P_{e}(\omega_{\frac{n}{2}}^{k})\\
P(\omega_{n}^{k}) - P(\omega_{n}^{k+m}) = 2\omega_{n}^{k}P_{o}(\omega_{\frac{n}{2}}^{k})
\end{gathered} P ( ω n k ) + P ( ω n k + m ) = 2 P e ( ω 2 n k ) P ( ω n k ) − P ( ω n k + m ) = 2 ω n k P o ( ω 2 n k )
整理得公式(11-12)不难看出,GS 操作是 CT 操作的逆过程,可以用于 I F F T IFFT I F F T 中,当然也可以用在F F T FFT F F T 中,通常 CT 方法也叫D I T DIT D I T (时域抽取)操作,GS 操作也叫D I F DIF D I F (频域抽取)操作。
P e ( ω n 2 k ) = 1 2 ( P ( ω n k ) + P ( ω n k + m ) ) P o ( ω n 2 k ) = 1 2 ω n k ( P ( ω n k ) − P ( ω n k + m ) ) \begin{gathered}
P_e(\omega_{\frac{n}{2}}^{k}) &= \frac{1}{2}(P(\omega_{n}^{k}) + P(\omega_{n}^{k+m})) \\
P_o(\omega_{\frac{n}{2}}^{k}) &= \frac{1}{2\omega_{n}^{k}}(P(\omega_{n}^{k}) - P(\omega_{n}^{k+m}))
\end{gathered} P e ( ω 2 n k ) P o ( ω 2 n k ) = 2 1 ( P ( ω n k ) + P ( ω n k + m )) = 2 ω n k 1 ( P ( ω n k ) − P ( ω n k + m ))
1.3.2 FFT 迭代实现
继续优化。依然拿 n=8 举例。递归数据操作如图 7 所示,在递归操作中是自定向下层层展开,然后逐层向上收缩。每一次递归都会消耗堆栈资源,影响效率。如果可以从底向上计算,那么可以省去堆栈资源的消耗,提高程序运行效率。于是现在的问题就变为了如何确定递归树叶子节点的元素排序。
将叶子节点元素顺序和原始元素顺序均使用二进制表示。发现二进制表示发生了左右对称反转,称之为位逆序变换。
//位逆序 Layer 4: 0 4 2 6 1 5 3 7 Binary: 000 100 010 110 001 101 011 111 Reverse: 000 001 010 011 100 101 110 111 Decimal: 0 1 2 3 4 5 6 7
下面是 Bit-Reverse-Copy 的实现:
Algorithm 2 Bit-Reverse-Copy Require: A function BitReverseCopy ( A ) n ← l e n ( A ) b ← log 2 ( n ) for i = 0 to n − 1 r ← ReverseBits ( i , b ) B [ r ] ← A [ i ] endfor return B end function \begin{array}{ll}\hline\textbf{Algorithm 2} & \text{Bit-Reverse-Copy}\\
\hline\textbf{Require:} & A\\ \textbf{function} & \text{BitReverseCopy}(A)\\ & n\leftarrow len(A)\\ & b\leftarrow\log_2(n)\\ & \text{for}\quad i=0\quad\text{to}\quad n-1\\ & \quad r\leftarrow\text{ReverseBits}(i,b)\\ & \quad B[r]\leftarrow A[i]\\ & \text{endfor}\\ & \text{return}\quad B\\ \textbf{end function} \\ \hline \end{array} Algorithm 2 Require: function end function Bit-Reverse-Copy A BitReverseCopy ( A ) n ← l e n ( A ) b ← log 2 ( n ) for i = 0 to n − 1 r ← ReverseBits ( i , b ) B [ r ] ← A [ i ] endfor return B
函数 ReverseBits 的实现如下,基本过程就是取原值低位赋值给目标值低位,然后原值右移一位,目标值左移一位,直到循环结束。
Function ReverseBits ( i , b ) r e s u l t ← 0 for j = 0 to b − 1 r e s u l t ← r e s u l t ≪ 1 b i t ← ( i ≫ j ) & 1 r e s u l t ← r e s u l t ∣ b i t endfor return r e s u l t \begin{array}{ll}
\hline
\textbf{Function} & \text{ReverseBits}(i, b) \\
\hline
& result \leftarrow 0 \\
& \text{for}\quad j = 0\quad\text{to}\quad b - 1 \\
& \quad result \leftarrow result \ll 1 \\
& \quad bit \leftarrow (i \gg j) \& 1 \\
& \quad result \leftarrow result \, | \, bit \\
& \text{endfor} \\
& \text{return}\quad result \\
\hline
\end{array} Function ReverseBits ( i , b ) r es u l t ← 0 for j = 0 to b − 1 r es u l t ← r es u l t ≪ 1 bi t ← ( i ≫ j ) &1 r es u l t ← r es u l t ∣ bi t endfor return r es u l t
迭代算法实现
算法 3 是D I T DIT D I T 实现,算法 4 是D I F DIF D I F 实现。s s s 可以看作是合并轮数。m m m 是当前合并次数下每个单元的规模。其中1 ^ \hat{1} 1 ^ 是乘法单位元都是ω n 0 \omega_n^0 ω n 0 。这里的ω n = e ± 2 π i / n \omega_n=e^{\pm2\pi i / n} ω n = e ± 2 π i / n ,在进行F F T FFT F F T 操作时取正,I F F T IFFT I F F T 操作时取负。当然I F F T IFFT I F F T 还需要乘上n − 1 n^{-1} n − 1 。
Algorithm 3 DIT Require: A = [ a 0 , a 1 , … , a n − 1 ] n = 2 x , x ∈ N function DIT ( A ) n ← l e n ( A ) P ← B i t R e v e r s e C o p y ( A ) for s = 1 to log 2 n m = 2 s ω m = ω n n m for k = 0 to n − 1 by m φ = 1 ^ for j = 0 to m 2 − 1 t = φ P [ k + j + m 2 ] u = P [ k + j ] P [ k + j ] = u + t P [ k + j + m 2 ] = u − t φ = φ ω m endfor endfor endfor return P end function \begin{array}{ll}\hline\textbf{Algorithm 3} & \text{DIT}\\
\hline
\textbf{Require:} & A=[a_0,a_1,\ldots,a_{n-1}]\quad n=2^{x},x\in\mathbb{N}\\ \textbf{function} & \text{DIT}(A)\\
& n\leftarrow len(A)\\
& P\leftarrow BitReverseCopy(A)\\
& \text{for}\quad s=1\quad\text{to}\quad \log_2n\\
& \quad m=2^s\\
& \quad \omega_m=\omega_n^{\frac{n}{m}}\\
& \quad\text{for}\quad k=0\quad \text{to} \quad n-1 \quad \text{by}\quad m\\
& \quad\quad \varphi=\hat{1}\\
& \quad\quad\text{for}\quad j=0\quad\text{to} \quad \frac{m}{2}-1\\
& \quad\quad\quad t=\varphi P[k+j+\frac{m}{2}]\\
& \quad\quad\quad u= P[k+j]\\
& \quad\quad\quad P[k+j]= u+t\\
& \quad\quad\quad P[k+j+\frac{m}{2}]= u-t\\
& \quad\quad\quad \varphi=\varphi \omega_m\\
& \quad\quad\text{endfor}\\
& \quad\text{endfor}\\
& \text{endfor}\\
& \text{return}\quad P\\
\textbf{end function}
& \\
\hline
& \end{array} Algorithm 3 Require: function end function DIT A = [ a 0 , a 1 , … , a n − 1 ] n = 2 x , x ∈ N DIT ( A ) n ← l e n ( A ) P ← B i tR e v er se C o p y ( A ) for s = 1 to log 2 n m = 2 s ω m = ω n m n for k = 0 to n − 1 by m φ = 1 ^ for j = 0 to 2 m − 1 t = φP [ k + j + 2 m ] u = P [ k + j ] P [ k + j ] = u + t P [ k + j + 2 m ] = u − t φ = φ ω m endfor endfor endfor return P
Algorithm 4 DIF Require: A = [ a 0 , a 1 , … , a n − 1 ] n = 2 x , x ∈ N function DIF ( P ) n ← l e n ( A ) P ← A for s = log 2 n to 1 m = 2 s ω m = ω n n m for k = 0 to n − 1 by m φ = 1 ^ for j = 0 to m 2 − 1 t = P [ k + j + m 2 ] u = P [ k + j ] P [ k + j ] = u + t P [ k + j + m 2 ] = φ ( u − t ) φ = φ ω m endfor endfor endfor P ← B i t R e v e r s e C o p y ( P ) return P end function \begin{array}{ll}\hline\textbf{Algorithm 4} & \text{DIF}\\
\hline
\textbf{Require:} & A=[a_0,a_1,\ldots,a_{n-1}]\quad n=2^{x},x\in\mathbb{N}\\ \textbf{function} & \text{DIF}(P)\\
& n\leftarrow len(A)\\
& P\leftarrow A\\
& \text{for}\quad s=\log_2n \quad\text{to}\quad 1\\
& \quad m=2^s\\
& \quad \omega_m=\omega_n^{\frac{n}{m}}\\
& \quad\text{for}\quad k=0\quad \text{to} \quad n-1 \quad \text{by}\quad m\\
& \quad\quad \varphi=\hat{1}\\
& \quad\quad\text{for}\quad j=0\quad\text{to} \quad \frac{m}{2}-1\\
& \quad\quad\quad t= P[k+j+\frac{m}{2}]\\
& \quad\quad\quad u= P[k+j]\\
& \quad\quad\quad P[k+j]= u+t\\
& \quad\quad\quad P[k+j+\frac{m}{2}]= \varphi(u-t)\\
& \quad\quad\quad \varphi=\varphi \omega_m\\
& \quad\quad\text{endfor}\\
& \quad\text{endfor}\\
& \text{endfor}\\
& P\leftarrow BitReverseCopy(P)\\
& \text{return}\quad P\\
\textbf{end function}
& \\
\hline
& \end{array} Algorithm 4 Require: function end function DIF A = [ a 0 , a 1 , … , a n − 1 ] n = 2 x , x ∈ N DIF ( P ) n ← l e n ( A ) P ← A for s = log 2 n to 1 m = 2 s ω m = ω n m n for k = 0 to n − 1 by m φ = 1 ^ for j = 0 to 2 m − 1 t = P [ k + j + 2 m ] u = P [ k + j ] P [ k + j ] = u + t P [ k + j + 2 m ] = φ ( u − t ) φ = φ ω m endfor endfor endfor P ← B i tR e v er se C o p y ( P ) return P
下面是 n=8 时,D I T DIT D I T 迭代 F F T FFT F F T 的数据流图。
如果不对输入进行位逆序而是对输出进行位逆序应当怎样计算,下面给出一个示例算法,该算法和算法 3 的逻辑是类似的。只是出现了些许变动。这里的Φ r e v \Phi_{rev} Φ r e v 是位逆序后的ω \omega ω 的幂次表,需要注意的是这里不对输入进行位逆序而是对单位根幂次表进行位逆序。由于上述的变换,数据流图也发生了相应的改变。m 表示的是当前轮次 NTT 的大小,n 表示当前轮次 CT 跨度,j 1 j_1 j 1 表示分组的起点,数据流图如图 10 所示,输出结果是位逆序后的。
Algorithm 5 DIT root Require: A = [ a 0 , a 1 , … , a n − 1 ] n = 2 x , x ∈ N Φ r e v function DIT ( A ) t = n for m = 1 to n by m t = t 2 for i = 0 to m − 1 j = 2 ⋅ i ⋅ t φ = Φ r e v [ m + i ] for k = 0 to t v = φ P [ k + j + t ] u = P [ k + j ] P [ k + j ] = u + v P [ k + j + t ] = u − v endfor endfor endfor return P end function \begin{array}{ll}\hline\textbf{Algorithm 5} & \text{DIT root}\\
\hline
\textbf{Require:} & A=[a_0,a_1,\ldots,a_{n-1}]\quad n=2^{x},x\in\mathbb{N} \quad \Phi_{rev} \\
\textbf{function} & \text{DIT}(A)\\
& t=n\\
& \text{for}\quad m=1\quad\text{to}\quad n \quad\text{by}\quad m\\
& \quad t=\frac{t}{2}\\
& \quad\text{for}\quad i=0\quad \text{to} \quad m-1 \\
& \quad\quad j =2\cdot i\cdot t\\
& \quad\quad \varphi =\Phi_{rev}[m+i]\\
& \quad\quad\text{for}\quad k=0\quad\text{to} \quad t\\
& \quad\quad\quad v=\varphi P[k+j+t]\\
& \quad\quad\quad u= P[k+j]\\
& \quad\quad\quad P[k+j]= u+v\\
& \quad\quad\quad P[k+j+t]= u-v\\
& \quad\quad\text{endfor}\\
& \quad\text{endfor}\\
& \text{endfor}\\
& \text{return}\quad P\\
\textbf{end function}
& \\
\hline
& \end{array} Algorithm 5 Require: function end function DIT root A = [ a 0 , a 1 , … , a n − 1 ] n = 2 x , x ∈ N Φ r e v DIT ( A ) t = n for m = 1 to n by m t = 2 t for i = 0 to m − 1 j = 2 ⋅ i ⋅ t φ = Φ r e v [ m + i ] for k = 0 to t v = φP [ k + j + t ] u = P [ k + j ] P [ k + j ] = u + v P [ k + j + t ] = u − v endfor endfor endfor return P
1.4 快速数论变换(NTT)
F F T FFT F F T 存一些问题。首先 FFT 是在复数域上的表示,而计算机系统中表示复数,需要比实数花费更多的资源,且复数运算也比实数运算更加复杂。此外 FFT 涉及大量的正余弦运算,对于精度有影响。于是我们期望在实数域内寻找类似单位根性质的数学概念。有限域上的原根满足相应的要求。
1.4.1 数学基础
欧拉函数
欧拉函数,即 φ ( n ) \varphi(n) φ ( n ) ,表示的是小于等于 n n n 且和 n n n 互素的数的个数。当 n 是素数时 φ ( n ) = n − 1 \varphi(n)=n-1 φ ( n ) = n − 1 。
欧拉定理
对于 a ∈ Z a \in \mathbb{Z} a ∈ Z ,m ∈ N ∗ m\in \mathbb{N}^* m ∈ N ∗ ,若 gcd ( a , m ) = 1 \gcd(a,m)=1 g cd( a , m ) = 1 ,则 a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi(m)} \equiv 1 \pmod{m} a φ ( m ) ≡ 1 ( mod m ) 。
费马小定理
若 p p p 为素数,gcd ( a , p ) = 1 \gcd(a,p)=1 g cd( a , p ) = 1 ,则 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} a p − 1 ≡ 1 ( mod p ) 。
也可表达为,对于任意整数 a a a ,有 a p ≡ a ( m o d p ) a^p \equiv a \pmod{p} a p ≡ a ( mod p ) 。
利用费马小定理求逆元:a − 1 = a p − 2 a^{-1}=a^{p-2} a − 1 = a p − 2
阶
由欧拉定理可知,若 gcd ( a , p ) = 1 \gcd(a,p)=1 g cd( a , p ) = 1 ,一定存在一个最小的正整数 n n n 满足同余式 a n ≡ 1 ( m o d m ) a^n \equiv 1 \pmod{m} a n ≡ 1 ( mod m ) 。这个 n n n 被称作 a a a 模 m m m 的阶记作 δ m ( a ) \delta_m(a) δ m ( a ) 或者 o r d m ( a ) ord_m(a) or d m ( a ) 。具有以下性质:
性质 1:a , a 2 , … , a δ m ( a ) a,a^2,\ldots,a^{\delta_m(a)} a , a 2 , … , a δ m ( a ) 模 m m m 两两不同余,之后进入周期。
性质 2:若 a n ≡ 1 ( m o d m ) a^n \equiv 1 \pmod{m} a n ≡ 1 ( mod m ) ,则 δ m ( a ) ∣ n \delta_m(a)|n δ m ( a ) ∣ n 。可以推导出若 a p ≡ a q ( m o d m ) a^p \equiv a^q \pmod{m} a p ≡ a q ( mod m ) ,则有 p ≡ q ( m o d δ m ( a ) ) p \equiv q \pmod{\delta_m(a)} p ≡ q ( mod δ m ( a )) 。
原根
给定 n ∈ N ∗ n \in \mathbb{N}^* n ∈ N ∗ ,g ∈ Z g \in \mathbb{Z} g ∈ Z ,满足 gcd ( g , m ) = 1 \gcd(g, m) = 1 g cd( g , m ) = 1 ,且 δ m ( g ) = φ ( m ) \delta_m(g) = \varphi(m) δ m ( g ) = φ ( m ) ,则称 g g g 为模 m m m 的原根。
即 g g g 满足 δ m ( g ) = ∣ Z m ∗ ∣ = φ ( m ) \delta_m(g)=| \mathbb{Z}_m^* |=\varphi(m) δ m ( g ) = ∣ Z m ∗ ∣ = φ ( m ) 。当 m m m 是素数时,我们有 g i m o d m g^i \mod m g i mod m ,0 < i < m 0<i<m 0 < i < m 的结果互不相同
原根个数:若一个数 m m m 有原根,则它原根的个数为 φ ( φ ( m ) ) \varphi(\varphi(m)) φ ( φ ( m )) 。
原根存在定理:一个数 m m m 存在原根当且仅当 m = 2 , 4 , p a , 2 p a m=2,4,p^a,2p^a m = 2 , 4 , p a , 2 p a ,其中 p p p 为奇素数,a ∈ N ∗ a\in \mathbb{N}^* a ∈ N ∗ 。
原根的性质
原根具有以下性质,不难看与单位根具有类似的性质。所以可以用于替代单位根,用于简化 D F T DFT D F T 计算。
不重性:∀ 0 ≤ i < j < φ ( p ) , g i ≢ g j ( m o d p ) \forall 0 \leq i < j < \varphi(p),\ g^i \not\equiv g^j \pmod{p} ∀0 ≤ i < j < φ ( p ) , g i ≡ g j ( mod p )
折半性:定义 g n = g δ p ( g ) n ≡ g p − 1 n g_n = g^{\frac{\delta_p(g)}{n}} \equiv g^{\frac{p-1}{n}} g n = g n δ p ( g ) ≡ g n p − 1 ,g n k = ( g n ) k g_n^k = (g_n)^k g n k = ( g n ) k 则 g a n a k ≡ g n k ( m o d p ) g_{an}^{ak} \equiv g_n^k \pmod{p} g an ak ≡ g n k ( mod p ) 。
对称性:g 2 n k + n ≡ − g 2 n k ( m o d p ) g_{2n}^{k+n} \equiv - g_{2n}^k \pmod{p} g 2 n k + n ≡ − g 2 n k ( mod p )
求和性:∑ i = 0 n − 1 ( g n ) k i ≡ n [ k = 0 ] ( m o d p ) \sum_{i=0}^{n-1} (g_n)^{ki} \equiv n[k=0] \pmod{p} ∑ i = 0 n − 1 ( g n ) k i ≡ n [ k = 0 ] ( mod p ) ,k = 0 k=0 k = 0 为真 [ k = 0 ] = 1 [k=0]=1 [ k = 0 ] = 1 ,否则为 0 0 0 。
原根与模数的选择
为了实现多次二分,模数 p p p 应选可以拆分为 q × 2 k + 1 q\times 2^k +1 q × 2 k + 1 的素数,q q q 为奇素数,k k k 为整数,2 k 2^k 2 k 模数的阶,也就是原根的最大数量。可以看下表的例子。
表 1:原根和模数的相关数据 原根 g 模数 p 分解 p 模数的阶 3 469762049 7 × 2 26 + 1 2 26 3 998244353 119 × 2 23 + 1 2 23 3 2281701377 17 × 2 27 + 1 2 27 \text{表 1:原根和模数的相关数据}\\ \begin{array}{cccc} \hline \text{原根 } g & \text{模数 } p & \text{分解 } p & \text{模数的阶} \\ \hline 3 & 469762049 & 7 \times 2^{26} + 1 & 2^{26} \\ 3 & 998244353 & 119 \times 2^{23} + 1 & 2^{23} \\ 3 & 2281701377 & 17 \times 2^{27} + 1 & 2^{27} \\ \hline \end{array} 表 1 :原根和模数的相关数据 原根 g 3 3 3 模数 p 469762049 998244353 2281701377 分解 p 7 × 2 26 + 1 119 × 2 23 + 1 17 × 2 27 + 1 模数的阶 2 26 2 23 2 27
1.4.2 NTT 的递归实现
将原根带入公式(7)得:
P ( g n k ) = P e ( ( g n k ) 2 ) + g n k P o ( ( g n k ) 2 ) m o d p k ∈ [ 0 , n ) \begin{align}
P(g_n^k)=P_e((g_n^k)^2)+{g_n^k}P_o((g_n^k)^2)\mod p \quad k \in[ 0,n)
\end{align} P ( g n k ) = P e (( g n k ) 2 ) + g n k P o (( g n k ) 2 ) mod p k ∈ [ 0 , n )
根据消去引理推导出得对称性以及折半引理对公式(13)进行化简。取 k ∈ [ 0 , n 2 ) k \in[0,\frac{n}{2}) k ∈ [ 0 , 2 n ) 。
P ( g n k ) = P e ( ( g n k ) 2 ) + g n k P o ( ( g n k ) 2 ) m o d p = P e ( g n 2 k ) + g n k P o ( g n 2 k ) m o d p \begin{gathered}
P(g_{n}^{k})&=P_{e}((g_{n}^{k})^2)+{g_{n}^{k}}P_{o}((g_{n}^{k})^2) \mod p\\
& =P_{e}(g_{\frac{n}{2}}^{k})+{g_{n}^{k}}P_{o}(g_{\frac{n}{2}}^{k}) \mod p
\end{gathered} P ( g n k ) = P e (( g n k ) 2 ) + g n k P o (( g n k ) 2 ) mod p = P e ( g 2 n k ) + g n k P o ( g 2 n k ) mod p
P ( g n k + m ) = P e ( ( g n k + m ) 2 ) + g n k + m P o ( ( g n k + m ) 2 ) m o d p = P e ( g n 2 k + m ) + g n k + m P o ( g n 2 k + m ) m o d p = P e ( − g n 2 k ) − g n k P o ( − g n 2 k ) m o d p = P e ( g n 2 k ) − g n k P o ( g n 2 k ) m o d p \begin{align*}
P(g_{n}^{k+m})&=P_{e}((g_{n}^{k+m})^2)+g_{n}^{k+m}P_{o}((g_{n}^{k+m})^2) \mod p\\
& =P_{e}(g_{\frac{n}{2}}^{k+m})+g_{n}^{k+m}P_{o}(g_{\frac{n}{2}}^{k+m}) \mod p\\
& =P_{e}(-g_{\frac{n}{2}}^{k})-g_{n}^{k}P_{o}(-g_{\frac{n}{2}}^{k}) \mod p\\
& =P_{e}(g_{\frac{n}{2}}^{k})-g_{n}^{k}P_{o}(g_{\frac{n}{2}}^{k}) \mod p
\end{align*} P ( g n k + m ) = P e (( g n k + m ) 2 ) + g n k + m P o (( g n k + m ) 2 ) mod p = P e ( g 2 n k + m ) + g n k + m P o ( g 2 n k + m ) mod p = P e ( − g 2 n k ) − g n k P o ( − g 2 n k ) mod p = P e ( g 2 n k ) − g n k P o ( g 2 n k ) mod p
简化为:
P ( g n k ) = P e ( g n 2 k ) + g n k P o ( g n 2 k ) P ( g n k + m ) = P e ( g n 2 k ) − g n k P o ( g n 2 k ) \begin{gathered}
P(g_{n}^{k})=P_{e}(g_{\frac{n}{2}}^{k})+{g_{n}^{k}}P_{o}(g_{\frac{n}{2}}^{k})\\
P(g_{n}^{k+m})=P_{e}(g_{\frac{n}{2}}^{k})-g_{n}^{k}P_{o}(g_{\frac{n}{2}}^{k})
\end{gathered} P ( g n k ) = P e ( g 2 n k ) + g n k P o ( g 2 n k ) P ( g n k + m ) = P e ( g 2 n k ) − g n k P o ( g 2 n k )
现在给出递归版本得算法,不难发现除了将单位根替换为原根,增加模运算,以及增加了参数 ,原根 g g g ,模数 p p p 外与 F F T FFT F F T 并无太大区别。
Algorithm 6 Recursion Number Theoretic Transform (NTT) Require: A = [ a 0 , a 1 , … , a n − 1 ] , n = 2 k , g , p function NTT_R ( A , g , p ) n ← len ( A ) if n = = 1 return A endif g n ← g p − 1 n ( m o d p ) A e ← [ a 0 , a 2 , … , a n − 2 ] A o ← [ a 1 , a 3 , … , a n − 1 ] Y e ← NTT_R ( A e , g n 2 , p ) Y o ← NTT_R ( A o , g n 2 , p ) for k from 0 to n / 2 − 1 Y [ k ] = ( Y e [ k ] + g n k Y o [ k ] ) ( m o d p ) Y [ k + n / 2 ] = ( Y e [ k ] − g n k Y o [ k ] ) ( m o d p ) end for return Y end function \begin{array}{ll}
\hline
\textbf{Algorithm 6} & \text{ Recursion Number Theoretic Transform (NTT)}\\
\hline
\textbf{Require:} & A=[a_0,a_1,\ldots,a_{n-1}],\quad n=2^{k},\quad g ,\quad p \\
\textbf{function} & \text{NTT\_R}(A, g, p)\\
&n\leftarrow \text{len}(A)\\
&\text{if}\quad n==1 \\
&\quad \text{return}\quad A\\
&\text{endif}\\
&g_n \leftarrow g^{\frac{p-1}{n}} \pmod p\\
&A_e\leftarrow [a_0,a_2,\ldots,a_{n-2}]\\
&A_o\leftarrow [a_1,a_3,\ldots,a_{n-1}]\\
&Y_e\leftarrow \text{NTT\_R}(A_e, g_n^2, p)\\
&Y_o\leftarrow \text{NTT\_R}(A_o, g_n^2, p)\\
& \text{for} \quad k \quad \text{from} \quad 0 \quad \text{to} \quad n/2-1\\
&\quad Y[k]= (Y_e[k] + g_n^k Y_o[k]) \pmod p\\
&\quad Y[k+n/2]= (Y_e[k] - g_n^k Y_o[k]) \pmod p\\
&\text{end for}\\
&\text{return} \quad Y \\
\textbf{end function} & \\
\hline &
\end{array} Algorithm 6 Require: function end function Recursion Number Theoretic Transform (NTT) A = [ a 0 , a 1 , … , a n − 1 ] , n = 2 k , g , p NTT_R ( A , g , p ) n ← len ( A ) if n == 1 return A endif g n ← g n p − 1 ( mod p ) A e ← [ a 0 , a 2 , … , a n − 2 ] A o ← [ a 1 , a 3 , … , a n − 1 ] Y e ← NTT_R ( A e , g n 2 , p ) Y o ← NTT_R ( A o , g n 2 , p ) for k from 0 to n /2 − 1 Y [ k ] = ( Y e [ k ] + g n k Y o [ k ]) ( mod p ) Y [ k + n /2 ] = ( Y e [ k ] − g n k Y o [ k ]) ( mod p ) end for return Y
注意NTT里的g − 1 g^{-1} g − 1 是取逆元操作,在计算I N T T INTT I N T T 时请注意。
1.4.3 NTT 的迭代实现
N T T NTT N T T 的迭代实现同理,因此不再赘述,直接给出相应的算法。
Algorithm 7 Iteration NTT Require: A = [ a 0 , a 1 , … , a n − 1 ] , n = 2 k , g n , p function NTT_I ( A , g n , p ) n ← len ( A ) P ← BitReverseCopy ( A ) for s = 1 to log n m = 2 s g m = g n n m ( m o d p ) for k = 0 to n − 1 by m φ = 1 ^ for j = 0 to m 2 − 1 t = φ P [ k + j + m 2 ] ( m o d p ) u = P [ k + j ] ( m o d p ) P [ k + j ] = ( u + t ) ( m o d p ) P [ k + j + m 2 ] = ( u − t ) ( m o d p ) φ = ( φ ⋅ g m ) ( m o d p ) endfor endfor endfor return P end function \begin{array}{ll}
\hline
\textbf{Algorithm 7} & \text{Iteration NTT}\\
\hline
\textbf{Require:} & A=[a_0,a_1,\ldots,a_{n-1}],\ n=2^{k},\quad g_n, \quad p\\
\textbf{function} & \text{NTT\_I}(A, g_n, p)\\
& n \leftarrow \text{len}(A)\\
& P \leftarrow \text{BitReverseCopy}(A)\\
& \text{for}\ s=1\ \text{to}\ \log{n}\\
& \quad m=2^s\\
& \quad g_m=g_n^{\frac{n}{m}} \pmod p\\
& \quad \text{for}\ k=0\ \text{to}\ n-1\ \text{by}\ m\\
& \quad\quad \varphi=\hat{1}\\
& \quad\quad \text{for}\ j=0\ \text{to}\ \frac{m}{2}-1\\
& \quad\quad\quad t=\varphi P[k+j+\frac{m}{2}] \pmod p\\
& \quad\quad\quad u= P[k+j] \pmod p\\
& \quad\quad\quad P[k+j]= (u+t) \pmod p\\
& \quad\quad\quad P[k+j+\frac{m}{2}]= (u-t) \pmod p\\
& \quad\quad\quad \varphi=(\varphi \cdot g_m) \pmod p\\
& \quad\quad \text{endfor}\\
& \quad \text{endfor}\\
& \text{endfor}\\
& \text{return}\ P\\
\textbf{end function} & \\
\hline
&
\end{array} Algorithm 7 Require: function end function Iteration NTT A = [ a 0 , a 1 , … , a n − 1 ] , n = 2 k , g n , p NTT_I ( A , g n , p ) n ← len ( A ) P ← BitReverseCopy ( A ) for s = 1 to log n m = 2 s g m = g n m n ( mod p ) for k = 0 to n − 1 by m φ = 1 ^ for j = 0 to 2 m − 1 t = φP [ k + j + 2 m ] ( mod p ) u = P [ k + j ] ( mod p ) P [ k + j ] = ( u + t ) ( mod p ) P [ k + j + 2 m ] = ( u − t ) ( mod p ) φ = ( φ ⋅ g m ) ( mod p ) endfor endfor endfor return P
1.5 Bailey NTT 算法
现在介绍另外一种算法,这种算法提高缓存命中率,从而提高执行效率。这种算法是将输入看作是一个行优先的矩阵进行计算,请注意对于NTT来说实际的输入指的是系数a i a_i a i 。
如果 n = R ⋅ C n=R\cdot C n = R ⋅ C 我们可以用 i r ∈ [ 0 , R ) i_r\in[0,R) i r ∈ [ 0 , R ) 和 i c ∈ [ 0 , C ) i_c \in [0,C) i c ∈ [ 0 , C ) 重写i = i r ⋅ C + i c i=i_r\cdot C+i_c i = i r ⋅ C + i c 。这将创建一个对应 [ 0 , n ) ⋍ [ 0 , R ) × [ 0 , C ) [0,n) \backsimeq[0,R) \times[0,C) [ 0 , n ) ⋍ [ 0 , R ) × [ 0 , C ) 。另一种对应关系是 k = k r + k c ⋅ R k=k_r+k_c\cdot R k = k r + k c ⋅ R ,注意这里输出的顺序已经变成了列优先了。将i i i 和k k k 带入下式。
P k = P ( ω n k ) = ∑ i = 0 n − 1 a i ω n k i k = 0 , 1 , … , n − 1 P_{k}=P(\omega_{n}^{k})=\sum_{i=0}^{n-1}a_{i}\omega_{n}^{ki}\quad k=0,1,\ldots,n-1 P k = P ( ω n k ) = i = 0 ∑ n − 1 a i ω n k i k = 0 , 1 , … , n − 1
需要提前明确的一点是这里公式中ω \omega ω 既可以是原根又可以是单位根,不过为了统一表示省去了原根的取模操作。替换可得:
P k r + k c ⋅ R = ∑ i c = 0 C − 1 ∑ i r = 0 R − 1 a i r ⋅ C + i c ⋅ ω R ⋅ C ( k r + k c ⋅ R ) ( i r ⋅ C + i c ) \begin{align}P_{k_{r}+k_{c}\cdot R}=\sum_{i_{c}=0}^{C-1}\sum_{i_{r}=0}^{R-1}a_{i_{r}\cdot C+i_{c}}\cdot\omega_{R\cdot C}^{(k_{r}+k_{c}\cdot R)(i_{r}\cdot C+i_{c})}\end{align} P k r + k c ⋅ R = i c = 0 ∑ C − 1 i r = 0 ∑ R − 1 a i r ⋅ C + i c ⋅ ω R ⋅ C ( k r + k c ⋅ R ) ( i r ⋅ C + i c )
将ω \omega ω 的幂次展开:
ω R ⋅ C ( k r + k c ⋅ R ) ( i r ⋅ C + i c ) = ω R ⋅ C i c k r + i c k c ⋅ R + i r k r ⋅ C + i r k c ⋅ R C = ω R ⋅ C i c k r ⋅ ω R ⋅ C i c k c ⋅ R ⋅ ω R ⋅ C i r k r ⋅ C ⋅ ω R ⋅ C i r k c ⋅ R C \begin{align*}
\omega_{R\cdot C}^{(k_{r}+k_{c}\cdot R)(i_{r}\cdot C+i_{c})}&=\omega_{R\cdot C}^{i_{c}k_{r}+i_{c}k_{c}\cdot R+i_{r}k_{r}\cdot C+i_{r}k_{c}\cdot RC} \\&=\omega_{R\cdot C}^{i_{c}k_{r}}\cdot \omega_{R\cdot C}^{i_{c}k_{c}\cdot R}\cdot \omega_{R\cdot C}^{i_{r}k_{r}\cdot C}\cdot \omega_{R\cdot C}^{i_{r}k_{c}\cdot RC}
\end{align*} ω R ⋅ C ( k r + k c ⋅ R ) ( i r ⋅ C + i c ) = ω R ⋅ C i c k r + i c k c ⋅ R + i r k r ⋅ C + i r k c ⋅ R C = ω R ⋅ C i c k r ⋅ ω R ⋅ C i c k c ⋅ R ⋅ ω R ⋅ C i r k r ⋅ C ⋅ ω R ⋅ C i r k c ⋅ R C
根据原根的折半性质或者单位根的消去引理ω a n a k = ω n k \omega_{an}^{ak}=\omega_n^k ω an ak = ω n k ,可得:
ω R ⋅ C i c k r ⋅ ω R ⋅ C i c k c ⋅ R ⋅ ω R ⋅ C i r k r ⋅ C ⋅ ω R ⋅ C i r k c ⋅ R C = ω n i c k r ⋅ ω C i c k c ⋅ ω R i r k r ⋅ 1 \omega_{R\cdot C}^{i_{c}k_{r}}\cdot \omega_{R\cdot C}^{i_{c}k_{c}\cdot R}\cdot \omega_{R\cdot C}^{i_{r}k_{r}\cdot C}\cdot \omega_{R\cdot C}^{i_{r}k_{c}\cdot RC}=\omega_{n}^{i_{c}k_{r}}\cdot \omega_{C}^{i_{c}k_{c}}\cdot \omega_{R}^{i_{r}k_{r}}\cdot1 ω R ⋅ C i c k r ⋅ ω R ⋅ C i c k c ⋅ R ⋅ ω R ⋅ C i r k r ⋅ C ⋅ ω R ⋅ C i r k c ⋅ R C = ω n i c k r ⋅ ω C i c k c ⋅ ω R i r k r ⋅ 1
则公式(14)变为如下形式:
P k r + k c ⋅ R = ∑ i c = 0 C − 1 ∑ i r = 0 R − 1 a i r ⋅ C + i c ⋅ ω n i c k r ⋅ ω C i c k c ⋅ ω R i r k r P_{k_{r}+k_{c}\cdot R}=\sum_{i_{c}=0}^{C-1}\sum_{i_{r}=0}^{R-1}a_{i_{r}\cdot C+i_{c}}\cdot \omega_{n}^{i_{c}k_{r}}\cdot \omega_{C}^{i_{c}k_{c}}\cdot \omega_{R}^{i_{r}k_{r}} P k r + k c ⋅ R = i c = 0 ∑ C − 1 i r = 0 ∑ R − 1 a i r ⋅ C + i c ⋅ ω n i c k r ⋅ ω C i c k c ⋅ ω R i r k r
添加一些括号来确定运算顺序则变成了:
P k r + k c ⋅ R = ∑ i c = 0 C − 1 [ ( ∑ i r = 0 R − 1 a i r ⋅ C + i c ⋅ ω R i r k r ) ω n i c k r ] ω C i c k c P_{k_{r}+k_{c}\cdot R}=\sum_{i_{c}=0}^{C-1}\Bigg[\bigg(\sum_{i_{r}=0}^{R-1}a_{i_{r}\cdot C+i_{c}}\cdot \omega_{R}^{i_{r}k_{r}}\bigg)\omega_{n}^{i_{c}k_{r}} \Bigg] \omega_{C}^{i_{c}k_{c}} P k r + k c ⋅ R = i c = 0 ∑ C − 1 [ ( i r = 0 ∑ R − 1 a i r ⋅ C + i c ⋅ ω R i r k r ) ω n i c k r ] ω C i c k c
首先对输入a子序列进行长度为R R R 的D F T DFT D F T 操作,然后对结果乘上旋转因子,最后进行长度为C C C 的D F T DFT D F T 操作。这么看可能不直观,我们将输入写成一个R × C R \times C R × C 大小的矩阵:
a = [ a 0 a 1 a 2 … a C − 1 a C a C + 1 a C + 2 … a C + ( C − 1 ) a 2 C a 2 C + 1 a 2 C + 2 … a 2 C + ( C − 1 ) ⋮ ⋮ ⋮ ⋱ ⋮ a ( R − 1 ) C a ( R − 1 ) C + 1 a ( R − 1 ) C + 2 … a ( R − 1 ) C + ( C − 1 ) ] a
=
\left[ \begin{array}{ccccc}
a_0 & a_1 & a_2 &\ldots & a_{C-1} \\
a_C & a_{C+1} & a_{C+2} & \ldots & a_{C+(C-1)} \\
a_{2C} & a_{2C+1} & a_{2C+2} & \ldots & a_{2C+(C-1)} \\
\vdots & \vdots & \vdots & \ddots & \vdots \\
a_{(R-1)C} & a_{(R-1)C+1} & a_{(R-1)C+2}& \ldots & a_{(R-1)C+(C-1)}
\end{array} \right] a = a 0 a C a 2 C ⋮ a ( R − 1 ) C a 1 a C + 1 a 2 C + 1 ⋮ a ( R − 1 ) C + 1 a 2 a C + 2 a 2 C + 2 ⋮ a ( R − 1 ) C + 2 … … … ⋱ … a C − 1 a C + ( C − 1 ) a 2 C + ( C − 1 ) ⋮ a ( R − 1 ) C + ( C − 1 )
先对对所有列进行DFT运算,然后所有元素乘上相应的转换因子,最后对所有行进行DFT运算。下面是转换转换因子矩阵T T T ,注意转换因子矩阵与对列进行DFT的结果进行的是逐点相乘。因为输出是列优先排序,所以需要对结果进行一个转置,从而使得输入与输出保持同一顺序。
T = [ 1 1 1 … 1 1 ω ω 2 … ω C − 1 1 ω 2 ω 4 … ω 2 ( C − 1 ) ⋮ ⋮ ⋮ ⋱ ⋮ 1 ω R − 1 ω 2 ( R − 1 ) … ω ( C − 1 ) ( R − 1 ) ] T=\left[\begin{array}{ccccc}
1 & 1 & 1 & \ldots & 1\\
1 & \omega &\omega^2 & \ldots & \omega^{C-1}\\
1 & \omega ^2 & \omega ^4 &\ldots & \omega^{2(C-1)}\\
\vdots & \vdots& \vdots & \ddots & \vdots\\
1 & \omega^{R-1}&\omega^{2(R-1)} & \ldots & \omega^{(C-1)(R-1)}
\end{array}\right] T = 1 1 1 ⋮ 1 1 ω ω 2 ⋮ ω R − 1 1 ω 2 ω 4 ⋮ ω 2 ( R − 1 ) … … … ⋱ … 1 ω C − 1 ω 2 ( C − 1 ) ⋮ ω ( C − 1 ) ( R − 1 )
1.5.1 Four-step
对上面的算法进行一个总结就是所谓的四步DFT算法,[ R × C ] [R \times C] [ R × C ] ,表示为大小为R × C R\times C R × C 的行优先矩阵。
I D F T ∗ IDFT* I D F T ∗ 是省略乘上n − 1 n^{-1} n − 1 的I D F T IDFT I D F T ,所以最后需要补上n − 1 n^{-1} n − 1 。
DFT:
[ R × C ] [R \times C] [ R × C ] ,对每一列进行长度为R R R 的D F T DFT D F T 运算。
[ R × C ] [R \times C] [ R × C ] ,所有元素乘上对应的转换因子ω i k \omega^{ik} ω ik 。
[ R × C ] [R \times C] [ R × C ] ,对每一行进行长度为C C C 的D F T DFT D F T 运算。
[ R × C ] [R \times C] [ R × C ] ,将矩阵进行转置。
IDFT : *
[ C × R ] [C \times R] [ C × R ] ,将矩阵进行转置。
[ R × C ] [R \times C] [ R × C ] ,对每一行进行长度为C C C 的I D F T ∗ IDFT* I D F T ∗ 运算。
[ R × C ] [R \times C] [ R × C ] ,所有元素乘上对应的转换因子ω − i k \omega^{-ik} ω − ik 。
[ R × C ] [R \times C] [ R × C ] ,对每一列进行长度为R R R 的I D F T ∗ IDFT* I D F T ∗ 运算。
1.5.2 Six-step
对列进行操作,无法读取连续的内存会影响计算效率,可以增加转置操作,于是获得六步DFT算法:
DFT:
[ R × C ] [R \times C] [ R × C ] ,将矩阵进行转置。
[ C × R ] [C \times R] [ C × R ] ,对每一行进行长度为R R R 的D F T DFT D F T 运算。
[ C × R ] [C \times R] [ C × R ] ,将矩阵进行转置。
[ R × C ] [R \times C] [ R × C ] ,所有元素乘上对应的转换因子ω i k \omega^{ik} ω ik 。
[ R × C ] [R \times C] [ R × C ] ,对每一行进行长度为C C C 的D F T DFT D F T 运算。
[ R × C ] [R \times C] [ R × C ] ,将矩阵进行转置。
IDFT : *
[ C × R ] [C \times R] [ C × R ] ,将矩阵进行转置。
[ R × C ] [R \times C] [ R × C ] ,对每一行进行长度为C C C 的I D F T ∗ IDFT* I D F T ∗ 运算。
[ R × C ] [R \times C] [ R × C ] ,所有元素乘上对应的转换因子ω − i k \omega^{-ik} ω − ik 。
[ R × C ] [R \times C] [ R × C ] ,将矩阵进行转置。
[ C × R ] [C \times R] [ C × R ] ,对每一行进行长度为R R R 的I D F T ∗ IDFT* I D F T ∗ 运算。
[ C × R ] [C \times R] [ C × R ] ,将矩阵进行转置。
2 NTT 设计
本节主要是介绍对NTT的设计,例如模约简方案、转置优化以及预计算。最后探讨有哪些可以进行的优化。
2.1 模约简
2.1.1 朴素方案
在模数约简的朴素方法中,如果硬件支持的话,会使用一个硬件指令将两个字(word)长度的被除数除以一个字长度的除数,得到一个字长度的商和一个字长度的余数。如果硬件不支持这种指令,那么就需要在软件中模拟这种指令。
这样的指令在x86和x86-64架构中是存在的。对于软件模拟,GNU GCC和Clang编译器在32位平台上提供了uint64_t类型,在64位平台上提供了unsigned __int128类型,并且这些类型都支持除法操作。
2.1.2 Barrett 约简
巴雷特约简的思路很简单,将计算z = a m o d p z =a \mod p z = a mod p ,转换为z = a − t p , t = ⌊ a p − 1 ⌋ z=a- tp,t=\lfloor ap^{-1}\rfloor z = a − tp , t = ⌊ a p − 1 ⌋ 。于是问题就从模运算转换为了求取p − 1 p^{-1} p − 1 的近似值,也就是t t t 的近似值。
对t t t 进行进行变换:
t = ⌊ a p ⌋ = ⌊ a b k − 1 b 2 k p b k + 1 ⌋ t=\lfloor \frac{a}{p} \rfloor=\lfloor \frac{\frac{a}{b^{k-1}}\frac{b^{2k}}{p}}{b^{k+1}} \rfloor t = ⌊ p a ⌋ = ⌊ b k + 1 b k − 1 a p b 2 k ⌋
其中b b b 是基底,计算机系统中通常使用二进制表示,即b = 2 b=2 b = 2 ,k k k 是模数相对于基底的位宽k = log b p + 1 k=\log_{b}p+1 k = log b p + 1 ,之所以是b 2 k b^{2k} b 2 k 是因为通常运用模约简的场景是模乘,因此a ∈ [ 0 , p 2 ) a\in[0,p^2) a ∈ [ 0 , p 2 ) 。经过变换,我们发现分离出来一个一个仅和模数有关的量:α = b 2 k p \alpha = \frac{b^{2k}}{p} α = p b 2 k ,这意味着对同一模数的约简可以提前计算α \alpha α ,其他部分可以通过右移和乘法完成,乘法运算效率显著高于除法运算,位移效率更快,计算速度提升了很多。
为了避免浮点数计算,令β = ⌊ b 2 k p ⌋ \beta=\lfloor \frac{b^{2k}}{p} \rfloor β = ⌊ p b 2 k ⌋ ,替换掉α \alpha α ,得到t t t 的近似t ^ \hat{t} t ^ :
t ^ = ⌊ ⌊ a b k − 1 ⌋ β b k + 1 ⌋ = ⌊ ⌊ a b k − 1 ⌋ ⌊ b 2 k p ⌋ b k + 1 ⌋ \hat{t}=\lfloor \frac{\lfloor\frac{a}{b^{k-1}}\rfloor \beta}{b^{k+1}} \rfloor=\lfloor \frac{\lfloor\frac{a}{b^{k-1}}\rfloor \lfloor \frac{b^{2k}}{p} \rfloor}{b^{k+1}} \rfloor t ^ = ⌊ b k + 1 ⌊ b k − 1 a ⌋ β ⌋ = ⌊ b k + 1 ⌊ b k − 1 a ⌋ ⌊ p b 2 k ⌋ ⌋
现在考虑t t t 与t ^ \hat{t} t ^ 的误差:
λ = a b k − 1 − ⌊ a b k − 1 ⌋ \lambda = \frac{a}{b^{k-1}} - \lfloor \frac{a}{b^{k-1}} \rfloor λ = b k − 1 a − ⌊ b k − 1 a ⌋
μ = b 2 k p − ⌊ b 2 k p ⌋ \mu = \frac{b^{2k}}{p} - \lfloor \frac{b^{2k}}{p} \rfloor μ = p b 2 k − ⌊ p b 2 k ⌋
且0 ≤ λ < 1 0 \le \lambda \lt 1 0 ≤ λ < 1 ,0 ≤ μ < 1 0 \le \mu \lt 1 0 ≤ μ < 1 ,此外还有:
t = ⌊ a b k − 1 ⋅ b 2 k p ⋅ 1 b k + 1 ⌋ = ⌊ ( ⌊ a b k − 1 ⌋ + λ ) ( ⌊ b 2 k p ⌋ + μ ) b k + 1 ⌋ ≤ ⌊ t ^ + ⌊ a b k − 1 ⌋ + ⌊ b 2 k p ⌋ + 1 b k + 1 ⌋ t=\lfloor\frac{a}{b^{k-1}}\cdot\frac{b^{2k}}{p}\cdot\frac{1}{b^{k+1}}\rfloor=\lfloor\frac{(\lfloor\frac{a}{b^{k-1}}\rfloor+\lambda)(\lfloor\frac{b^{2k}}{p}\rfloor+\mu)}{b^{k+1}}\rfloor\leq\lfloor\hat{t}+\frac{\lfloor\frac{a}{b^{k-1}}\rfloor+\lfloor\frac{b^{2k}}{p}\rfloor+1}{b^{k+1}}\rfloor t = ⌊ b k − 1 a ⋅ p b 2 k ⋅ b k + 1 1 ⌋ = ⌊ b k + 1 (⌊ b k − 1 a ⌋ + λ ) (⌊ p b 2 k ⌋ + μ ) ⌋ ≤ ⌊ t ^ + b k + 1 ⌊ b k − 1 a ⌋ + ⌊ p b 2 k ⌋ + 1 ⌋
因为a < b 2 k a\lt b^{2k} a < b 2 k ,所以⌊ a b k − 1 ⌋ ≤ b k + 1 − 1 \lfloor \frac{a}{b^{k-1}} \rfloor \leq b^{k+1}-1 ⌊ b k − 1 a ⌋ ≤ b k + 1 − 1 ;又因为p ≥ b k − 1 p \ge b^{k-1} p ≥ b k − 1 ,所以⌊ b 2 k p ⌋ ≤ b k + 1 \lfloor \frac{b^{2k}}{p}\rfloor \leq b^{k+1} ⌊ p b 2 k ⌋ ≤ b k + 1 。对上面不等式放缩:
t ≤ ⌊ t ^ + b k + 1 − 1 + b k + 1 + 1 b k + 1 ⌋ = ⌊ t ^ + 2 ⌋ t\leq\lfloor\hat{t}+\frac{b^{k+1}-1+b^{k+1}+1}{b^{k+1}}\rfloor=\lfloor\hat{t}+2\rfloor t ≤ ⌊ t ^ + b k + 1 b k + 1 − 1 + b k + 1 + 1 ⌋ = ⌊ t ^ + 2 ⌋
又因为t ^ ≤ t \hat{t} \le t t ^ ≤ t ,所以可得:
t − 2 ≤ t ^ ≤ t t-2 \le \hat{t} \le t t − 2 ≤ t ^ ≤ t
近似性不错,进一步可得:
0 < a − t ^ p ≤ a − ( t − 2 ) p = a − t p + 2 p 0 \lt a - \hat{t}p \le a - (t-2)p=a-tp+2p 0 < a − t ^ p ≤ a − ( t − 2 ) p = a − tp + 2 p
又因为a − t p = z < p a-tp=z\lt p a − tp = z < p ,所以:
0 < a − t ^ p < 3 p 0 \lt a - \hat{t}p \lt 3p 0 < a − t ^ p < 3 p
不难看出使用近似方法得出得值有可能比p大,但是通过最多两次减法就可以修正误差,下面给出完整的算法:
Algorithm 8 Barrett 约简 Require: a β = ⌊ b 2 k p ⌋ p function B a r r e t t ( a , β , p ) t ^ ← ⌊ ⌊ a b k − 1 ⌋ β b k + 1 ⌋ z 1 ← a m o d b k + 1 z 2 ← t ^ ⋅ p m o d b k + 1 z = z 1 − z 2 if z < 0 z = z + b k + 1 endif while z ≥ p z = z − p endwhile return z end function \begin{array}{ll}
\hline
\textbf{Algorithm 8} & \text{Barrett 约简}\\
\hline
\textbf{Require:} & a\quad \beta=\lfloor \frac{b^{2k}}{p} \rfloor\quad p\\
\textbf{function} & Barrett(a,\beta,p)\\
& \hat{t} \leftarrow \lfloor \frac{\lfloor\frac{a}{b^{k-1}}\rfloor \beta}{b^{k+1}} \rfloor\\
& z_1 \leftarrow a \mod b^{k+1}\\
& z_2 \leftarrow \hat{t}\cdot p \mod b^{k+1}\\
& z=z_1-z_2\\
& \text{if} \ z \lt 0\\
& \quad z=z+b^{k+1}\\
& \text{endif}\\
& \text{while} \ z \ge p\\
& \quad z=z-p\\
& \text{endwhile}\\
& \text{return}\ z\\
\textbf{end function} & \\
\hline
&
\end{array} Algorithm 8 Require: function end function Barrett 约简 a β = ⌊ p b 2 k ⌋ p B a r r e tt ( a , β , p ) t ^ ← ⌊ b k + 1 ⌊ b k − 1 a ⌋ β ⌋ z 1 ← a mod b k + 1 z 2 ← t ^ ⋅ p mod b k + 1 z = z 1 − z 2 if z < 0 z = z + b k + 1 endif while z ≥ p z = z − p endwhile return z
z 1 z_1 z 1 和z 2 z_2 z 2 的运算可以通过位与操作直接去掉k位的高位即可,简化了计算。下面给出β \beta β 的算法:
Algorithm 9 计算 β Require: p k b function g e t β ( p , k , b ) β ← b k repeat s ← β β ← 2 β − ⌊ p ⌊ β 2 b k ⌋ b k ⌋ until β ≤ s t ← b 2 k − p β while t < 0 β ← β − 1 t ← t + p endwhile return β end function \begin{array}{ll}
\hline
\textbf{Algorithm 9} & \text{计算} \beta\\
\hline
\textbf{Require:} & p\quad k \quad b\\
\textbf{function} & get\beta(p,k,b)\\
& \beta \leftarrow b^k\\
& \text{repeat} \\
& \quad s \leftarrow \beta \\
& \quad \beta \leftarrow 2\beta -\lfloor\frac{ p\lfloor \frac{\beta^2}{b^k} \rfloor }{b^k} \rfloor\\
& \text{until} \ \beta \le s \\
& t \leftarrow b^{2k}-p\beta\\
& \text{while} \ t \lt 0\\
& \quad \beta \leftarrow \beta -1\\
& \quad t \leftarrow t+p\\
& \text{endwhile}\\
& \text{return}\ \beta\\
\textbf{end function} \\
\hline
&\end{array} Algorithm 9 Require: function end function 计算 β p k b g e tβ ( p , k , b ) β ← b k repeat s ← β β ← 2 β − ⌊ b k p ⌊ b k β 2 ⌋ ⌋ until β ≤ s t ← b 2 k − pβ while t < 0 β ← β − 1 t ← t + p endwhile return β
2.1.3 Montgomery 约简
蒙哥马利模乘约减的思路是通过变换,将需要取模的数控制到很小的范围([ 0 , ( p − 1 ) 2 ] → [ 0 , 2 p − 1 ] [0,(p-1)^2] \rightarrow [0,2p-1] [ 0 , ( p − 1 ) 2 ] → [ 0 , 2 p − 1 ] )然后通过少量减法获取最后结果。并通过位运算简化计算,例如:右移除法、位与取模。
蒙哥马利约简
对于∀ t ∈ Z \forall t \in \mathbb{Z} ∀ t ∈ Z 且满足a < r p a < r p a < r p ,r > p r\gt p r > p ,gcd ( r , p ) = 1 \gcd(r,p)=1 g cd( r , p ) = 1 。p p p 是以b b b 为基底,长度为k k k 的整数,则r的取值为b k b^k b k ,计算机中b b b 为2 2 2 ,显然r > p r\gt p r > p 。但是只有当gcd ( b , p ) = 1 \gcd(b,p)=1 g cd( b , p ) = 1 时才满足gcd ( r , p ) = 1 \gcd(r,p)=1 g cd( r , p ) = 1 ,之前提到的格式是满足条件的。
由于r r r 和p p p 互素,所以存在r ′ , p ′ ∈ [ 0 , p ) r',p' \in [0,p) r ′ , p ′ ∈ [ 0 , p ) 使得下式成立,r ′ r' r ′ 是r r r 在p p p 下的逆元 ,p ′ p' p ′ 是p p p 在r r r 下的负逆元。
r r ′ − p p ′ = 1 rr'-pp'=1 r r ′ − p p ′ = 1
于是:
a r ′ = a r ′ r r = a r r ′ r = a ( 1 + p p ′ ) r = a + a p p ′ r = a + ( ⌊ a p ′ r ⌋ r + ( a p ′ m o d r ) ) p r = a + ( a p ′ m o d r ) p r + ⌊ a p ′ r ⌋ p ≡ a + ( a p ′ m o d r ) p r ( m o d p ) ≡ a + ( ( a m o d r ) p ′ m o d r ) p r ( m o d p ) \begin{align*}
ar' &=ar'\frac{r}{r} \\
&=\frac{arr'}{r} \\
&=\frac{a(1+pp')}{r} \\
&=\frac{a+app'}{r} \\
&=\frac{a+(\lfloor \frac{ap'}{r} \rfloor r+(ap' \bmod r))p}{r} \\
&=\frac{a+(ap' \bmod r)p}{r}+\lfloor \frac{ap'}{r} \rfloor p \\
&\equiv \frac{a+(ap' \bmod r)p}{r} &\pmod{p} \\
&\equiv \frac{a+((a\bmod r)p' \mod r)p}{r} &\pmod{p} \\
\end{align*} a r ′ = a r ′ r r = r a r r ′ = r a ( 1 + p p ′ ) = r a + a p p ′ = r a + (⌊ r a p ′ ⌋ r + ( a p ′ mod r )) p = r a + ( a p ′ mod r ) p + ⌊ r a p ′ ⌋ p ≡ r a + ( a p ′ mod r ) p ≡ r a + (( a mod r ) p ′ mod r ) p ( mod p ) ( mod p )
其中⌊ a p ′ r ⌋ r + ( a p ′ m o d r ) \lfloor \frac{ap'}{r} \rfloor r+(ap' \bmod r) ⌊ r a p ′ ⌋ r + ( a p ′ mod r ) 是将a p ′ ap' a p ′ 表达为成商乘上除数加余数的形式。上面的推导过程将对a r ′ ar' a r ′ 模p p p 转换为了对a + ( ( a m o d r ) p ′ m o d r ) p r \frac{a+((a\bmod r)p' \bmod r)p}{r} r a + (( a mod r ) p ′ mod r ) p 模p p p 操作。显然( ( a m o d r ) p ′ m o d r ) p < r p ((a\mod r)p' \mod r)p \lt rp (( a mod r ) p ′ mod r ) p < r p 又因为a < r p a < r p a < r p 所以可得下面的关系,不难发现最多只需一次减法就可以完成取模操作,其中模r r r 可以通过位与运算,除r r r 可以通过位右移运算完成,之所以做两次模r r r 运算是为了降低乘法位宽。
t + ( ( t m o d r ) p ′ m o d r ) p r < 2 p \frac{t+((t\mod r)p' \mod r)p}{r} \lt 2p r t + (( t mod r ) p ′ mod r ) p < 2 p
下面给出相应的算法:
Algorithm 10 蒙哥马利约简 Require: p r p ′ ( p p ′ ≡ − 1 m o d r ) function R E D C ( a ) m ← ( a m o d r ) p ′ m o d r t ← ( a + m p ) div r if t > p return t − p else return t endif end function \begin{array}{ll}
\hline
\textbf{Algorithm 10} & \text{蒙哥马利约简} \\
\hline
\textbf{Require:} & p\quad r \quad p' \quad (pp' \equiv-1\mod r )\quad \\
\textbf{function} & REDC(a)\\
& m \leftarrow (a\bmod r)p' \bmod r \\
& t \leftarrow (a+mp) \text{div }r \\
& \text{if} \ t \gt p\\
& \quad \quad \text{return}\ t-p\\
& \text{else} \\
& \quad \quad \text{return}\ t\\
& \text{endif}\\
\textbf{end function} \\
\hline
&\end{array} Algorithm 10 Require: function end function 蒙哥马利约简 p r p ′ ( p p ′ ≡ − 1 mod r ) R E D C ( a ) m ← ( a mod r ) p ′ mod r t ← ( a + m p ) div r if t > p return t − p else return t endif
蒙哥马利域
由上面蒙哥马利约简的过程不难发现实际是对x R − 1 xR^{-1} x R − 1 (这里的R − 1 R^{-1} R − 1 就是上面提到的r ′ r' r ′ ,只是为了方便区分)进行约简,所以在使用蒙哥马利约简时,首先需要转换到蒙哥马利域中,蒙哥马利域中的形式表示为x ˉ \bar{x} x ˉ ,转换入蒙哥马利域的方式:
x ˉ = x ⋅ R ( m o d p ) \bar{x} =x \cdot R \pmod p x ˉ = x ⋅ R ( mod p )
显然,转入蒙哥马利域的成本是高昂的,只有在蒙哥马利域内计算值得时才会使用蒙哥马利约简,在蒙哥马利域中加法和减法(结合律)不受影响。
x ˉ + y ˉ = x R + y R = ( x + y ) R = x + y ‾ ( m o d p ) \bar{x}+\bar{y} = xR+yR=(x+y)R = \overline{x+y} \pmod p x ˉ + y ˉ = x R + y R = ( x + y ) R = x + y ( mod p )
但是蒙哥马利域上的乘法是不正确的,这里蒙哥马利域中的乘法用 × \times × 表示,正常的乘法用⋅ \cdot ⋅ 表示。我们期望的结果是:
x ˉ × y ˉ = x ⋅ y ‾ = ( x ⋅ y ) R ( m o d p ) \bar{x}\times\bar{y}=\overline{x\cdot y}=(x\cdot y) R \pmod{p} x ˉ × y ˉ = x ⋅ y = ( x ⋅ y ) R ( mod p )
但是实际上如果直接相乘获得结果如下:
x ˉ ⋅ y ˉ = x R ⋅ y R = ( x ⋅ y ) R R ( m o d p ) \bar{x}\cdot\bar{y}=xR\cdot yR=(x\cdot y) RR \pmod{p} x ˉ ⋅ y ˉ = x R ⋅ y R = ( x ⋅ y ) R R ( mod p )
蒙哥马利域上的乘法定义如下:
x ˉ × y ˉ = ( x ˉ ⋅ y ˉ ) R − 1 m o d p = ( x ⋅ y ) R m o d p = R E C D ( x ˉ ⋅ y ˉ ) \bar{x}\times\bar{y}=(\bar{x}\cdot \bar{y}) R^{-1}\mod{p} =(x\cdot y) R \mod{p}=RECD(\bar{x}\cdot \bar{y}) x ˉ × y ˉ = ( x ˉ ⋅ y ˉ ) R − 1 mod p = ( x ⋅ y ) R mod p = R E C D ( x ˉ ⋅ y ˉ )
最后就是蒙哥马利域的转出,转出如下所示。
x = x ˉ ⋅ R − 1 m o d p = R E C D ( x ˉ ) x = \bar{x}\cdot R^{-1} \mod{p}=RECD(\bar{x}) x = x ˉ ⋅ R − 1 mod p = R E C D ( x ˉ )
蒙哥马利模乘
在执行蒙哥马利乘法本身开销并不高,但是需要完成一些提前计算,这些计算的开销较高。
蒙哥马利域的转入
p ′ p' p ′ 的计算
蒙哥马利域的转出
在约简过程中已经完成了蒙哥马利域的转出。p'的计算可以通过扩展欧几里得算法获得。对于转入还存在另一种策略:
x ˉ = x ⋅ R m o d p = x × R 2 = R E D C ( x ⋅ ( R 2 m o d p ) ) \bar{x} =x \cdot R \mod{p} =x \times R^2 =REDC(x\cdot (R^2 \mod p)) x ˉ = x ⋅ R mod p = x × R 2 = R E D C ( x ⋅ ( R 2 mod p ))
第二种策略策略是省去模p的运算,但是失去了乘R位运算优势,取而代之的是乘上R 2 m o d p R^2 \mod p R 2 mod p ,可以提前计算,并需要执行约简操作,因为取模开销很大,大概率第二种策略效率更高一点。于是蒙哥马利模乘可以表示如下:
z = x ⋅ y m o d p = R E D C ( R E D C ( x ˉ ⋅ y ˉ ) ) x ˉ = R E D C ( x ⋅ ( R R m o d p ) ) y ˉ = R E D C ( y ⋅ ( R R m o d p ) ) \begin{align*}
z&=x\cdot y \mod p=REDC(REDC(\bar{x}\cdot \bar{y}))\\
\bar{x}&=REDC(x\cdot (RR \bmod p))\\
\bar{y}&=REDC(y\cdot (RR \bmod p))
\end{align*} z x ˉ y ˉ = x ⋅ y mod p = R E D C ( R E D C ( x ˉ ⋅ y ˉ )) = R E D C ( x ⋅ ( R R mod p )) = R E D C ( y ⋅ ( R R mod p ))
2.2 root power 预计算
算法3中需要反复执行φ = φ ω m \varphi=\varphi \omega_m φ = φ ω m ,这里的m是当前合并轮次下每个单元的数据规模或者是蝶形运算的步长,s s s 可以看作是当前合并轮数,n n n 是输入规模,l l l 是总的轮数。ω m = ω 2 n − s \omega_m=\omega^{2^{n-s}} ω m = ω 2 n − s ,s = log 2 m s=\log_2m s = log 2 m 。一共具有两种预计算方案
方案一: 预计算ω \omega ω 的幂到一个root power表t t t 中,即t [ i ] = ω i , i ∈ [ 0 , n 2 ) t[i]=\omega^i ,i\in[0,\frac{n}{2}) t [ i ] = ω i , i ∈ [ 0 , 2 n ) 。对于给定的j j j 和m = 2 s m=2^s m = 2 s ,我们可以表示旋转因子φ = t [ j ⋅ 2 α ] , α = n − s , j ∈ [ 0 , 2 s − 1 ) \varphi =t[j\cdot 2^{\alpha }], \alpha =n-s,j\in [0,2^{s-1}) φ = t [ j ⋅ 2 α ] , α = n − s , j ∈ [ 0 , 2 s − 1 ) 。
方案二: 计算每个粒度s ∈ [ 1 , n ] s\in[1,n] s ∈ [ 1 , n ] ,单独的root power表:
T s [ i ] = t [ i ⋅ 2 n − s ] = ( ω 2 n − s ) i , i ∈ [ 0 , 2 s − 1 ) T_s[i]=t[i\cdot 2^{n-s}]=(\omega^{2^{n-s}})^i, i\in [0,2^{s-1}) T s [ i ] = t [ i ⋅ 2 n − s ] = ( ω 2 n − s ) i , i ∈ [ 0 , 2 s − 1 )
记T n = t T_n=t T n = t ,∑ s = 1 n ∣ T s ∣ = ∑ s = 1 n 2 s − 1 = 2 n − 1 \sum_{s=1}^n|T_s|=\sum_{s=1}^n 2^{s-1}=2^n-1 ∑ s = 1 n ∣ T s ∣ = ∑ s = 1 n 2 s − 1 = 2 n − 1 。对于给定的j j j 和m = 2 s m=2^s m = 2 s ,我们可以表示旋转因子φ = T [ j ] \varphi =T[j] φ = T [ j ] 。然后我们将将所有的T s T_s T s 组成一个大的表T T T 。
T s [ i ] = T [ ( 2 0 + 2 1 + 2 2 + ⋯ + 2 s − 2 ) + i ] = T [ 2 s − 1 − 1 + i ] T_s[i]=T[(2^0+2^1+2^2+ \cdots +2^{s-2})+i]=T[2^{s-1}-1+i] T s [ i ] = T [( 2 0 + 2 1 + 2 2 + ⋯ + 2 s − 2 ) + i ] = T [ 2 s − 1 − 1 + i ]
对于给定的j j j 和m = 2 s m=2^s m = 2 s ,我们可以表示旋转因子φ = T [ β + j ] , β = 2 s − 1 − 1 = m 2 − 1 \varphi =T[\beta + j],\beta=2^{s-1}-1=\frac{m}{2}-1 φ = T [ β + j ] , β = 2 s − 1 − 1 = 2 m − 1 。
两种方式T [ β + j ] T[\beta+j] T [ β + j ] 和t [ j ⋅ 2 α ] t[j \cdot 2^\alpha ] t [ j ⋅ 2 α ] ,内层索引都是相同的,α \alpha α 和 β \beta β 表示循环中不变的表达式,在循环开始前计算一次。但是T T T ,可以使用指针指向T [ β ] T[\beta] T [ β ] ,
一步解引用。而t t t 需要先位移后解引用。
Four-step算法: 因为要用到所有的幂次,所以直接预计算所有的幂次,构建幂次表T ∗ T_* T ∗ 即可。需要设计合适的索引。我的想法是利用折半引理,在对行或是对列的NTT中索引格式只需要在方案二的基础上进行细微改动即可,只需要乘上总规模N N N 除当前NTT规模n n n 的商即可,T ∗ [ ( β + j ) N n ] = T ∗ [ β μ + j μ ] T_*[(\beta+j)\frac{N}{n}]=T_*[\beta \mu+j\mu] T ∗ [( β + j ) n N ] = T ∗ [ β μ + j μ ] ,只是这种方法会使得方案本一步完成的解引用,需要两步而且增加的是开销较高的乘法。至于列运算结束后的旋转因子矩阵直接按照输入索引去查询即可,因为输入矩阵和旋转因子因子矩阵是一一对应的。
2.3 转置优化
矩阵的转置有两种方式一种是out-place,一种是in-place。前者需要申请一块同等大小的空间,从而完成转置,即空间复杂度O ( R × C ) O(R\times C) O ( R × C ) 。后一种,空间复杂度远小于O ( R × C ) O(R\times C) O ( R × C ) 。这里主要讨论in-place转置方案,对于方阵很简单只需要交换< i , j > <i,j> < i , j > 与< j , i > <j,i> < j , i > 处的元素即可。
下面讨论n × c n n\times cn n × c n 形式的矩阵转置,为了方便表示不妨取c = 2 c=2 c = 2 。将一个n × 2 n n\times 2n n × 2 n 的矩阵M M M 拆分成两个n × n n\times n n × n 的方阵A A A 和B B B :
M = [ A B ] M=[A \quad B] M = [ A B ]
则转置表达为:
M T = [ A B ] T = [ A T B T ] M^T=[A \quad B]^T=
\begin{bmatrix}
A^T\\
B^T
\end{bmatrix} M T = [ A B ] T = [ A T B T ]
定义ϕ ( M ) \phi(M) ϕ ( M ) :
ϕ ( M ) = [ A T B T ] \phi(M)=[A^T\quad B^T] ϕ ( M ) = [ A T B T ]
现在将两个子矩阵A T A^T A T ,B T B^T B T 的行用向量表示:
ϕ ( M ) = [ A T B T ] = [ α 1 β 1 ⋯ ⋯ α n β n ] ⟷ < α 1 , β 1 , α 2 , β 2 , ⋯ , α n , β n > \phi(M)=[A^T\quad B^T]=
\begin{bmatrix}
\alpha_1 & \beta_1\\
\cdots &\cdots \\
\alpha_n & \beta_n
\end{bmatrix}
\longleftrightarrow<\alpha_1,\beta_1,\alpha_2,\beta_2,\cdots,\alpha_n,\beta_n > ϕ ( M ) = [ A T B T ] = α 1 ⋯ α n β 1 ⋯ β n ⟷< α 1 , β 1 , α 2 , β 2 , ⋯ , α n , β n >
M T = [ A T B T ] = [ α 1 ⋯ α n β 1 ⋯ β n ] ⟷ < α 1 , α 2 , ⋯ , α n , β 1 , β 2 , ⋯ , β n > M^T=
\begin{bmatrix}
A^T\\
B^T
\end{bmatrix}
=
\begin{bmatrix}
\alpha_1 \\
\cdots \\
\alpha_n \\
\beta_1 \\
\cdots\\
\beta_n
\end{bmatrix}
\longleftrightarrow <\alpha_1,\alpha_2,\cdots,\alpha_n,\beta_1,\beta_2,\cdots,\beta_n> M T = [ A T B T ] = α 1 ⋯ α n β 1 ⋯ β n ⟷< α 1 , α 2 , ⋯ , α n , β 1 , β 2 , ⋯ , β n >
所以我们可以通过某种方法ρ \rho ρ 重新排列ϕ ( M ) \phi(M) ϕ ( M ) 的索引,使其等于M T = ρ ϕ ( M ) M^T=\rho \phi(M) M T = ρϕ ( M ) ,对于索引i i i 的目标位置j j j 满足:
j = ρ ( i ) = n ⋅ ( i m o d 2 ) + ( i / 2 ) i ∈ [ 0 , 2 n ) j=\rho (i)=n \cdot (i \bmod 2) + (i / 2) \quad i\in[0,2n) j = ρ ( i ) = n ⋅ ( i mod 2 ) + ( i /2 ) i ∈ [ 0 , 2 n )
下面探究对于列排布能否实现同样的作用, 首先我们定义对方阵的行进行索引重排为ρ r \rho_r ρ r ,对其列进行索引成排为ρ c \rho_c ρ c 。很明显对于一个矩阵A,满足一下关系:
( ρ c X ) T = ρ r X T (\rho_cX)^T=\rho_rX^T ( ρ c X ) T = ρ r X T
我们已经证明:
M T = ρ r ϕ ( M ) M^T=\rho_r \phi(M) M T = ρ r ϕ ( M )
现在令M = ρ c N M=\rho_c N M = ρ c N ,即可获得:
ρ r ϕ ( ρ c N ) = ( ρ c N ) T \rho_r \phi(\rho_cN)=(\rho_cN)^T ρ r ϕ ( ρ c N ) = ( ρ c N ) T
ρ r ϕ ( ρ c N ) = ρ r ( N ) T \rho_r \phi(\rho_cN)=\rho_r(N)^T ρ r ϕ ( ρ c N ) = ρ r ( N ) T
ϕ ( ρ c N ) = N T \phi(\rho_cN)=N^T ϕ ( ρ c N ) = N T
现在证明对列进行排布同样可以完成相应的操作,只不过要现在哎方阵转置前进行列排布。对列进行排布对那内存更友好,需要O ( 2 n ) O(2n) O ( 2 n ) 的额外空间。
对于c n × n cn\times n c n × n 形式的矩阵,只需要对列执行ρ − 1 \rho^{-1} ρ − 1 然后对方阵转置就行。
2.4可能的优化
2.4.1 位逆序省略
我们回到最开始,我们引入DFT是为了进行多项式乘法:
A ( x ) ⋅ B ( x ) = I D F T ( D F T ( A ) ⋅ D F T ( B ) ) A(x)\cdot B(x)=IDFT(DFT(A)\cdot DFT(B)) A ( x ) ⋅ B ( x ) = I D F T ( D F T ( A ) ⋅ D F T ( B ))
如果使用D I F DIF D I F 完成D F T DFT D F T ,使用D I T DIT D I T 完成I D F T IDFT I D F T ,我们就可以省略D I F DIF D I F 最后的位逆序,以及D I T DIT D I T 最开始的位逆序,紧挨着的两个位逆序可以省略。
2.4.2 省略转置
矩阵算法省略转置的原理与位逆序省略的原理相同,如果连续做D F T DFT D F T 和I D F T IDFT I D F T ,我们可以省略D F T DFT D F T 最后的转置以及I D F T IDFT I D F T 最开始的转置。
2.4.3 合并
可以将对列之后乘旋转因子的操作合并到对列操作或者对行操作中,这样可以减少内存读写的次数。
对于I D F T IDFT I D F T 可以将乘以n − 1 n^{-1} n − 1 ,合并到四步I D F T ∗ IDFT* I D F T ∗ 中,例如乘旋转因子的过程中。
3 参考文献
[1] 七海. 基础知识:FFT - 简单入门[EB/OL]//七海の参考書. (2021-03-29)[2024-01-21]. https://shiraha.cn/2021/The-concept-of-fft-introducing-edition/ .
[2]快速数论变换(NTT)及蝴蝶操作构造详解[EB/OL]//知乎专栏. [2024-01-21]. https://zhuanlan.zhihu.com/p/80297169 .
[3] Frigo M, Leiserson C E, Prokop H, et al. Cache-oblivious algorithms[J]. ACM Transactions on Algorithms (TALG), 2012, 8(1): 1-22. https://doi.org/10.1145/2071379.2071383 .
[4] LESAVOUREY A, NEGRE C, PLANTARD T. Efficient Randomized Regular Modular Exponentiation using Combined Montgomery and Barrett Multiplications[C/OL]//ICETE: International Joint Conference on e-Business and Telecommunications: 4 [SECRYPT]. Lisbon, Portugal, 2016: 368-375[2024-02-28]. https://hal.science/hal-01330898 . DOI:10.5220/0005998503680375 .
[5] Finite Field Implementations[Z/OL]. [2024-01-26]. https://docs.google.com/presentation/d/1I5QS58LtA3iiiPiVHHcN7oufCoo8sIDh9UvnJHWjA2Q .
[6] RISC ZERO. Finite Field Implementations: Barrett & Montgomery[Z/OL]. (2023-02-17)[2024-01-26]. https://www.youtube.com/watch?v=hUl8ZB6hpUM .
[7] AGARWAL R C, COOLEY J W. Fourier transform and convolution subroutines for the IBM 3090 Vector Facility[J/OL]. IBM Journal of Research and Development, 1986, 30(2): 145-161. DOI:10.1147/rd.302.0145 .
[8] 董晓算法. G41 快速傅里叶变换 FFT 算法 多项式乘法哔哩哔哩 bilibili[EB/OL]. [2024-01-21]. https://www.bilibili.com/video/BV1Le4y1V78D/ .
[9] 董晓算法. G43 快速数论变换 NTT 算法哔哩哔哩 bilibili[EB/OL]. [2024-01-21]. https://www.bilibili.com/video/BV1a3411Z7vL/ .
[10] HEIDEAN M, JOHNSON D, BURRUS C. Gauss and the history of the fast fourier transform[J/OL]. IEEE ASSP Magazine, 1984, 1(4): 14-21. DOI:10.1109/MASSP.1984.1162257 .
[11] COHEN H, FREY G, AVANZI R, 等. Handbook of Elliptic and Hyperelliptic Curve Cryptography[M/OL]. 0 版. Chapman and Hall/CRC, 2005[2024-02-26]. https://www.taylorfrancis.com/books/9781420034981 . DOI:10.1201/9781420034981 .
[12] Hardcaml Zprize[EB/OL]. [2024-01-30]. https://zprize.hardcaml.com/ntt-top-level.html .
[13] IEEE Xplore Full-Text PDF:[EB/OL]. [2024-02-28]. https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=4176858 .
[14] Math & Engineering[EB/OL]. [2024-02-22]. https://xn--2-umb.com/ .
[15] LIANG Z, ZHAO Y. Number Theoretic Transform and Its Applications in Lattice-based Cryptosystems: A Survey[M/OL]. arXiv, 2022[2024-01-30]. http://arxiv.org/abs/2211.13546 .
[16] KNAUTH C, ADAS B, WHITFIELD D, 等. Practically efficient methods for performing bit-reversed permutation in C++11 on the x86- 64 architecture[M/OL]. arXiv, 2017[2024-01-29]. http://arxiv.org/abs/1708.01873 .
[17] DUPAQUIS V, VENELLI A. Redundant Modular Reduction Algorithms[C/OL]//PROUFF E. Smart Card Research and Advanced Applications. Berlin, Heidelberg: Springer, 2011: 102-114. DOI:10.1007/978-3-642-27257-8_7 .
[18] Knezevic M, Vercauteren F, Verbauwhede I. Speeding up Barrett and Montgomery modular multiplications[J]. IEEE Transactions on Comput, 2009, 2.
[19] KRAPIVENSKY V. Speeding up decimal multiplication[M/OL]. arXiv, 2020[2024-01-21]. http://arxiv.org/abs/2011.11524 . DOI:10.48550/arXiv.2011.11524 .
[20] LONGA P, NAEHRIG M. Speeding up the Number Theoretic Transform for Faster Ideal Lattice-Based Cryptography[A/OL]. (2016)[2024-01-30]. https://eprint.iacr.org/2016/504 .
[21] REDUCIBLE. The Fast Fourier Transform (FFT): Most Ingenious Algorithm Ever?[Z/OL]. (2020-11-15)[2024-01-21]. https://www.youtube.com/watch?v=h7apO7q16V0 .
[22] LONGA P, NAEHRIG M. Speeding up the Number Theoretic Transform for Faster Ideal Lattice-Based Cryptography[A/OL]. (2016)[2024-07-11]. https://eprint.iacr.org/2016/504 .