数论转换

数论转换是一种计算折积的快速演算法。计算折积的快速演算法中最常用的一种是使用快速傅里叶变换，然而快速傅立叶变换具有一些实现上的缺点，举例来说，资料向量必须乘上复数系数的矩阵加以处理，而且每个复数系数的实部和虚部是一个正弦及余弦函数，因此大部分的系数都是浮点数，也就是说，必须做复数而且是浮点数的运算，因此计算量会比较大，而且浮点数运算产生的误差会比较大。
而在数论转换中，资料向量需要乘上的矩阵是一个整数的矩阵，因此不需要作浮点数的乘法运算，更进一步，在模数为formula_1的情况下，只有formula_2种可能的加法与formula_2种可能的乘法，这可以使用记忆体把这些有限数目的加法和乘法存起来，利用查表法来计算，使得数论转换完全不须任何乘法与加法运算，不过需要较大的记忆体与消耗较大的存取记忆体量。
虽然使用数论转换可以降低计算折积的复杂度，不过由于只进行整数的运算，所以只能用于对整数的信号计算折积，而利用快速傅立叶变换可以对任何复数的信号计算折积，这是降低运算复杂度所要付出的代价。
转换公式.
数论转换的转换式为
formula_4
而数论转换的反转换式为
formula_5
注解：
(1) formula_1是一个质数。
(2) formula_7 表示除以M的余数
(4)formula_13是formula_8对模数formula_1之模反元素
(5)formula_16为模M的N阶单位根，即formula_17而且formula_18。若此时formula_19，我们称formula_16为模M的原根
举一个例子：
一个formula_21的formula_22点数论转换与反转换如下，取formula_23，注意此时formula_24
正转换
formula_25
反转换
formula_26
数论转换的性质.
(1) 正交性质
数论转换矩阵的每个列是互相正交的，即formula_27
(2) 对称性
若formula_28，则formula_29的数论转换formula_30也会有formula_31的特性。
若formula_32，则formula_29的数论转换formula_30也会有formula_35的特性。
(3) 反数论转换可由正数论转换实现
formula_36，即formula_37的数论转换。
步骤一：把formula_30改写成formula_39，若formula_40，则formula_41
步骤二：求formula_39的数论转换。
步骤三：乘上formula_13。
(4) 线性性质
若formula_44，formula_45，(formula_46表互为数论转换对)则有formula_47。
(5) 平移性质
若formula_44，则formula_49，而且formula_50。
(6) 圆周折积性质
若formula_44，formula_45，则formula_53，而且formula_54。(formula_55代表圆形折积。)
这个性质是数论转换可以用来作为折积的快速演算法的主要原因。
若formula_44，则formula_57，而且formula_58
快速数论转换.
如果转换点数N是一个2的次方，则可以使用类似基数-2快速傅立叶变换的蝴蝶结构来有效率的实现数论转换。同样的互质因子算法也可以应用在数论转换上。
formula_59
其中，formula_60，formula_61。
因此一个formula_8点数论转换可以拆解成两个formula_63点的数论转换。
N, M, alpha的选择.
由于数论转换可以拥有类似快速傅立叶变换的快速演算法，因此通常formula_8会选择适合使用快速演算的值，比如说取为2的幂次，或是可以分解成许多小质数相乘的数。
在数论转换中，需要大量地和formula_16的幂次做乘法，因此，如果可以取formula_16为2或2的幂次，则每一次的乘法在二进制中只会是一个移位的操作，可以省下大量的乘法运算。
因为要做模formula_1的运算，所以formula_1的二进位表示法中，1的个数越少越好，同时formula_1的值不能取太小，这是因为数论转换后的值都小于formula_1，因此当真实的折积的结果会大于formula_1时就会发生错误，所以必须谨慎选取formula_1的大小。
特殊的数论转换.
梅森质数数论转换.
梅森质数是指形如formula_73的质数记做formula_74，其中formula_75是一个质数。
在梅森质数数论转换中，取formula_76，可以证明formula_77可以如下选取：
(1) formula_78
(2) formula_79
在这两种选取方式下，由于formula_16是2的幂次，所以只需移位运算，但formula_8不是2的幂次，所以基数-2的蝴蝶结构不适用于此例中，同时formula_8为质数或一个质数的两倍，并不是一个可以拆解成很多质因数乘积的数，因此也无法由互质因子演算法得到太大的好处。梅森质数数论转换通常用于较短序列的折积运算
费马数数论转换.
费马数是指形如formula_83的数记做formula_84。
在费马数数论转换中，取formula_85，可以证明在formula_86之下formula_77可以如下选取：
(1) formula_88
(2) formula_89
在这两种选取方式下，formula_8是2的幂次，所以基数-2的蝴蝶结构适用于此例中，而若formula_16是2的幂次，只需移位运算。费马数数论转换通常用于较长序列的折积运算。
实作议题.
由于数论转换需运用到余数下的反元素，这边提供一些不同的演算法。
(一) Euclidean algorithm - iterative version
假设M为质数的mod，N为我们当前的元素且符合M-1的因数，借由Euclidean Algorithm知道必然存在x, y的整数使得
xM + yN = 1 - (1)
由上式左右mod M 可以得到 yN mod M= 1，显然y就是我们这边想求的反元素。
我们已知最大公因数(gcd)有如下性质。
gcd(M, N) = gcd(N, M mod N) = gcd(M mod N, N mod (M mod N)) = ... = 1
这边设定q为商数，r为余数，接上面的等式方程式化如下。
formula_92
formula_93
formula_94
formula_95
整理一下
formula_96
formula_97
formula_98
formula_99
最后的r 一定会变成1，所以我们只要不断的将r乘上-q带往下一个式子(像是r1*(-q1))，跟代往下下个式子(像是r3的左边式子要带入r1)即可求得最后我们想得到的 (1)，最后的N旁的系数就是反元素。
def modInv(x, M): #by Euclidean algorithm - iterative version
 t, new_t, r, new_r = 0, 1, M, x
 while new_r != 0:
 quotient = int(r / new_r)
 tmp_new_t = new_t
 tmp_t = t
 tmp_new_r = new_r
 tmp_r = r
 t, new_t = new_t, (t - quotient * new_t)
 r, new_r = new_r, (r % new_r)
 if r &gt; 1:
 print("Inverse doesn't exist")
 if t &lt; 0:
 t = t + M
 return t
(二) Euclidean algorithm - recursion version
根据gcd的性质gcd(M, N) = gcd(N, M mod N) = gcd(M mod N, N mod (M mod N)) = ... = 1
可以看出递回的关系，借此我们可以从这边得到N的系数，也就是反元素。
gcd(M, N) = 1来看，我们知道存在formula_100 。
gcd(N, M mod N)=1，则存在formula_101
formula_102
这边q就是相除的商数，比较M跟N的系数，这边可以得到一个递回关系，
formula_103
formula_104
可以借由下一层的系数来推出上一层的系数。
def egcd(a, b): # y = x1 - quotient * y1 , x = y1 
 if a == 0:
 return (b, 0, 1)
 else:
 g, y, x = egcd(b % a, a)
 return (g, x - (b // a) * y, y)
def modInv(n, m): #by Euclidean algorithm - recursion version 
 g, y, x = egcd(n, m)
 if g != 1:
 print("Inverse doesn't exist")
 else:
 return y % m
(三) Fermat little theorem
当M是质数时，我们知道任何一个数字N，formula_105
formula_106
显然formula_107就是N的反元素。
搭配快速mod可以容易的算出反元素，power是偶数时则可以用formula_108; power是基数时则可以用formula_109，让power变成偶数。反复直到power变成1。
def modInv(a, m): #fermat little thm
 return modExponent(a, m - 2, m)
def modExponent(base, power, M): #quick mod O(log(power))
 result = 1
 power = int(power)
 base = base % M
 while power &gt; 0:
 if power &amp; 1:
 result = (result * base) % M
 base = (base * base) % M
 power = power » 1
 return result
演算法.
以下程式预设。
import numpy as np
Root of Unity 是从1到M-1中选出一个适当的 alpha，其满足"转换公式"段落的(5)。
def RootOfUnity(N, M):
 def mod_exp(base, exp, mod):
 result = 1
 base %= mod
 while exp &gt; 0:
 if exp % 2 == 1: # If exp is odd
 result = (result * base) % mod
 base = (base * base) % mod
 exp //= 2
 return result
 for r in range(1, M): # Check integers from 1 to M-1
 if mod_exp(r, N, M) == 1:
 return r # Return the first root of unity
 return None
def NTT(x,N,M):
 alpha = RootOfUnity(N, M)
 NInv = modInv(N, M)
 A = np.ones((N,N))
 for i in range(1,N):
 for j in range(1,i+1):
 A[i][j] = A[i][j-1]*modExponent(alpha,i,M) % M
 A[j][i] = A[i][j]
 return np.matmul(A,x) % M, alpha
def invNTT(x,alpha,N,M):
 alphaInv = modInv(alpha, M)
 NInv = modInv(N, M)
 B = np.ones((N,N))
 for i in range(1,N):
 for j in range(1,i+1):
 B[i][j] = B[i][j-1]*modExponent(alphaInv,i,M) % M
 B[j][i] = B[i][j]
 B = B * NInv 
 return np.matmul(B,x) % M
参考资料.
[1] R.C. Agarval and C.S. Burrus,"Number Theoretic Transforms to Implement Fast Digital Convolution," "Proc. IEEE", vol.63, no.4, pp. 550-560, Apr. 1975.
[2] I. Reed and T.K. Truong,"The use of finite fileds to compute convolution," "IEEE Trans. Info. Theory", vol.21 ,pp.208-213, Mar. 1975.