多精度里FNT和SSA的点滴.
Karatsuba,TOOM3,4.5...Toom-Cook
可以看成是插值算法的逐步扩展.
比如TOOM3,
对Q(x),x=取5个不同值,即可一矩阵(行列式)
通过解上面矩阵既可以得到A,B,C,D,E,即Q(x)多项式系数.
这个推广后即是Toom-Cook算法.
FFT则是Toom-Cook对取值的一个特化.
对于长度为N的FFT,其取值为复数域内N次方根,
就是一个范德蒙德(Vandermonde)矩阵.
其求解只需要nlogn就可以了.具体的解法随便一本关于傅里叶变换的书都会有介绍.
题外话:FFT还有一个特性就是变换后,频域和时域相互映射起来,这个在工程某些方面这个特性是很有用的.比如滤波,信号处理,数据压缩等等多个方面.有很大的作用(其他如余弦变换,小波变换也有类似的功能,但在有限域内这个特性一般都没有了),在高精度计算里,这个特性并没有用上.略过不表.
FNT,一般指代的是快速数论变换(也有人指FNT是指快速数论变换里面的费马变换,但是由于费马素数太少,实际的应用只存在特定领域),
这里基于代码简单化起见,特指是基2的快速数论变换,但要说明的是,FNT不仅仅包含基2的快速数论变换.
FNT实际只是FFT在有限域的一种表现形式.类比说就是FFT其插值是取复数域内的N次方根,FNT其插值是取某个剩余系内的N次方根.其余的运算和FFT基本一致.
FNT额外的好处就是由于在有限域下计算,只要数据不产生溢出,里面的精度是不会丢失的.这个要比FFT要好.
FNT里面的相关参数主要有M,N,α(或记作r),M为模,N为变换长度, α(r)为N次方根(MOD M下),只要选取的M存在N次方根,这个选取M就是可用的.就这点来说,这个选取还是比较宽松的,如果还需要考虑计算的效率问题(主要是a * b % M这个过程),选取的M则需要好好考虑.
选择M基于下面一些考虑.
变换长度为N,则必须存在N次方根(这个是FNT的内在要求)
高精度计算里,卷积的结果范围为,则必须 (R为进制)
计算尽可能简单.(这个是高精度要求,也可以算是卷积要求)
附:计算尽可能简单不等于尽可能小.比如这个的计算就都比M = 123456789要简单.(实际运行效率要求)
显然,这里没有要求M必须是素数.这就留出了较大的优化空间.
优化方向,主要是选取M方面着手.
1. M尽可能小
2. CRT(中国剩余定理),
3. MOD M尽可能简单
4. 选取比较特殊的M,
1:这个略过(感觉废话),
2:这个主要是aploat主要干的事,(我的代码主要也是基于这个)
3:这个主要是GMP的优化方向,(不是太旧的版本是这样,4.x之前的据说不是)
4: Fürer‘s algorithm似乎就是基于这个方向,但是没有看懂原理
比如M = 65537(),在十进制下其变换长度最大为 65536/81 > 512,即10进制下最大变换长度为512,
比如1649267441665()/ 81 > ,在10000进制下,
如果M不是素数,,就实际应用效率而言,一般取M为3个接近自然位长(32位,64位机类似去接近64位)素数乘积.且每个素数都存在N次方根.即可.
比如 apfloat里面使用的3个素数就是2113929217, 2013265921, 1811939329,(源代码为2.41版本)
这里考虑加减法上的优化,都没有使用超过2^31的数.
自身代码的选择则是2013265921, 1811939329,1711276033,实际选取的原则其实并没有什么不同.
如不考虑乘法复杂度就乘法次数而言,直接MOD M运算的乘法次数要小于分三段,由于M超出了自然位长(80+位,接近3个自然位长,因此分三段最后再用CRT合并,这个中途都是32位长,只有最后CRT时才是3个自然位长长度,这样可能会比直接一次3个自然位长的FNT更好的效率.
较为细致较为精确的复杂度后面(可能)会进行补充说明.
GMP应用的是SSA,我的理解是M为,r = 2,的FNT的一个特化.
上面这个的流程可以算是说得比较清楚的了.
取一个适合大的简单的模M(2次幂加1),根也是2的幂.因此当中的计算应该算是简单的.
上面的流程写成递归的形式比较方便.
另外还有一个优化方法是选取的M是某个小素数的幂.但这个了解不多.
关于FNT,一些比较细致的常熟级复杂度优化会在后面进行补充说明
我的代码已经全部放上了github上了.另外也在51nod上的大数乘法里使用有关于FNT的具体代码AC通过了.
至于GMP和Apfloat库源代码可以在其官方上下载.
看不到公式的请查看附件