多精度计算备忘录之乘法 ------ 复杂度分析

多精度计算里,多精度乘法是其中最重要的运算之一,编写的多精度库(类)的其中一个重要效率标识就是其乘法的速度.

根据曾经写的大数类的记忆,简单记录下其中的一些技巧,以备查询.

一般的算法有如:

多精度乘法,所知的几个主要的优化方法有:

1:直接乘法.

2:comba乘法

3:Karatsuba乘法

4:toom_cook乘法

5:FFT乘法

6:FNT算法,或其他类似的有限域上对应类似的FFT算法

7:自己未曾理解的算法.

8:改进技巧,

1:直接乘法.

a   b    c

*          e   f    g

------------------------

ag  bg   cg

af  bf  cf

+    ae  be  ce

------------------------

(我个人通常称它为硬乘法,后面直接使用这名字),这个是小学就已经学到的普通乘法,一般是没有其他更好的优化条件下使用.一般不大于十几或几十之内都差不多是最好的.

其是n^2复杂度的,准确地说,是C(2,N)

2:comba乘法

a   b    c

*          e   f    g

------------------------

ag  bg   cg

af  bf  cf

+    ae  be  ce

------------------------

cg  = C

bg + cf  = D

ag + bf + ce  = E

af + be           = F

ae                    = G

-------------------------

实际这个算法和硬乘法的基本原理是一致的,也是n^2复杂度,准确地说也是C(2,N)

但是这里有个特殊的地方,这里计算C,D,E,F,G是可以并行的,这个特性在可允许并行计算时是可以更快地处理.而且这个特性在后面高级优化会有特别用处.

3:Karatsuba乘法

R为进制

a  b

*     c  d

--------------

acR^2 - ((a - b) * (c - d) - ac - bd)R + bd

这里只需要计算 ac,(a - b) *(c - d),bd 3项,较硬乘法里面的4项,少,

其复杂度是n^1.58左右

4:toom_cook乘法

toom_cook乘法,实际可以看成Karatsuba的泛化.

其中可以细化可以有toom3,toom4.toom5.toom6.....

a  b  c

*  d  e  f

----------

这里abc,def可以看成多项式F(x),G(x)的系数

ax^2 + bx + c

dx^2 + ex + f

-----------------

Ax^4 + Bx^3 + Cx^2 + Dx + E   = Q(x)

这里分别取

x = 0,-2,1,-1,2,(或者把2或-2换成无穷大)5个点

这里5个点Q(x)刚好够组成5个等式(也可以看成乘于一个矩阵)

(矩阵Q)|0   0  0  0  1|     |F(0)*G(0)|

|16  8  4  2  1|     |F(2)*G(2)|

(A,B,C,D,E)*    |1   1  1  1  1| =   |F(1)*G(1)|

|1  -1  1 -1  1|     |F(-1)*G(-1)|

|16 -8  4 -2  1|     |F(2)*G(2)|

通过这方程组就能解出A,B,C,D,E,也可以看成求这矩阵的逆.同样可得A,B,C,D,E

toom4,5,6...这些都基本类似方式,选取不同点而得到结果.

至于选取什么点,只是计算技巧问题.

其复杂度是C n^k, k = log(5) / log(3)

其他的 k = log(2k - 1) / log 3;

其中准确的递推公式为 T(3N) = 5T(N) + pN,

由于里面还需要包含了大约5 - 7次左右的N*1位的乘除法法,和若干次N位的加减法,因此此算法额外开销比Karatsuba算法要大,因而尽管复杂度比Karatsuba低,但也只能应用于比Karatsuba更大一些范围才可能有比较好的速度处理.

至于对x选点的问题,一般是看矩阵Q的逆如何更简单一些(不是说数据足够小).

5:FFT乘法

对TOOM_COOK算法选点选择更特殊的点比如是2次幂的根,那实际矩阵Q就变成了范德蒙德矩阵,

这样就利用FFT变换来处理.FFT变换原理不在这展开.

其复杂度是nlogn,对于基2的FFT来说,其比较准确的复杂度是3nlogn 次复数乘法,

6:FNT算法

这里一般是指快速数论变换,而不是快速数论变换里的费马变换.

其复杂度是nlogn,对于基2的FNT来说,其比较准确的复杂度是3 nlogn 次模M剩余系下的乘法运算.

当然还有如二次域,分圆域,伽罗瓦域等一些有限域的扩张下都有类似的处理.通过额外的操作可能换取得到对根和模M的选取更为宽松,

7:自己未曾理解的算法.

自1971年至2007年,最快的是Schnhage–Strassen algorithm,简称SSA,

2007:Fürer‘s algorithm,比SSA更快

这两个似乎都是在有限域上的处理,可惜没看懂.

8:改进技巧,

一般来说

FFT需要3nlogn 次复数乘法,实际应该是3nlogn * 4 = 12nlogn次double乘法

FNT需要3 nlogn 次模M运算下的乘法运算,如果选择一个较大的M 保证计算不溢出,因此M需要选择至少是接近80+ --- 96bit位即3个32bit长度,才能保证计算不出错.即每次运算都需要计算一个96 * 96bit乘法

和一个192 和 96 bit 的除法,才能计算出一次计算结果.实际这样的处理并不是很方便,甚至可能还不如直接计算12 nlogn次double乘法.

因此一般的利用CRT中国剩余定理,

选择合适的 M = M1 * M2 * M3, 其中M1,M2,M3都是32bit范围下的数,这样就变成了

3 nlogn次 模M下的乘法运算,转换成 3 * 3 nlogn = 9 nlogn 次32bit范围内的乘法运算.

CRT最后的合并工作是 n线性的,其工作量相对来nlogn说基本可以忽略

这样处理的优点:

这个较FFT系数上就已经减少了.而且还不算double运算和unsigned int在模乘法速度的差异,

还有一个重要的原因.

FNT并行性要比FFT要好.

理论上

FFT,能够并行处理的是前面两次的变换可以并行处理,

并行处理后

时间上相当于是2 * nlogn次复数乘法 大约是 2 * nlogn * 4 = 8 nlogn次double乘法.

FNT如果能够完全能够做做到并行,前面6次变换是可以并行处理的,后面三次也是完全可以做到并行处理的.即只需要2 nlogn 次乘法,大约是 2nlogn 次unsigned int 模乘法.

当然这个是理想状态,实际虽然有速度差异,但一般没有这么大的区别.

时间: 2024-10-10 05:27:26

多精度计算备忘录之乘法 ------ 复杂度分析的相关文章

使用快速傅里叶变换计算大整数乘法

我们知道,两个 N 位数字的整数的乘法,如果使用常规的算法,时间复杂度是 O(N2).然而,使用快速傅里叶变换,时间复杂度可以降低到 O(N logN loglogN). 假设我们要计算以下两个 N 位数字的乘积: a = (aN-1aN-2...a1a0)10 = aN-1x10N-1 + aN-2x10N-2 + ... + a1x101 + a0x100 b = (bN-1bN-2...b1b0)10 = bN-1x10N-1 + bN-2x10N-2 + ... + b1x101 + b

精度计算-大数乘小数

精度计算-大数乘小数 本算法是用来计算一个大数(现有的数据类型无法表示的数)乘以一个小数(10以内的数). 算法思路是把大数的每一位都当做一个字符放入一个字符数组中,再从最后一位开始于要乘的小数相乘并加上前一位的进位,如果有进位存入一个变量中,加到下一位的计算中去. 下面是我的C语言实现过程. int main() { char c[100] = "1231231231231231231231231231231232"; char t[101] ; int m = 10; mult(c

php的精度计算问题(bcadd和bcsub)

一.前言 我们在进行php开发的时候经常会遇到浮点型的问题,特别是涉及金额的部分,常常需要进行加减运算.当小数点的位数比较多的时候,往往容易犯一些很低级的错误.这里记录一下php的精度计算和封装的小demo. 二.关于php的高精度问题 1.概念解释 这篇文章的解释最清楚: php高精度计算问题 2.高精度数值对比大小问题 下面这篇文章讲的很好: 临时发一个项目遇到的PHP浮点计算问题 默认保留两位小数 bcadd(参数1,参数2,参数3) 参数1 和2 是要相加的参数 3是保留几位小数. 1.

用python计算lda语言模型的困惑度并作图

转载请注明:电子科技大学EClab——落叶花开http://www.cnblogs.com/nlp-yekai/p/3816532.html 困惑度一般在自然语言处理中用来衡量训练出的语言模型的好坏.在用LDA做主题和词聚类时,原作者D.Blei就是采用了困惑度来确定主题数量.文章中的公式为: perplexity=exp^{ - (∑log(p(w))) / (N) } 其中,P(W)是指的测试集中出现的每一个词的概率,具体到LDA的模型中就是P(w)=∑z p(z|d)*p(w|z)[z,d

精度计算-大数阶乘

精度计算-大数阶乘 本算法的目的在于计算一个比较大的数的阶乘,由于得到的结果比较大,是现有的数据类型无法存储的,所以我决定将结果存储在一个long a[]数组中. 我们的思路是把每4位数看做数组的一个元素来存储,例如:个.十.百.千存在a[0],万.十万.百万.千万存在a[1]以此类推. 我们用10的阶乘来模拟一下求结果大于4位数阶乘的过程,9的阶乘为362880,而10的阶乘为9的阶乘乘以10,在计算完9的阶乘时a[0] = 2880,a[1]=36,因为362880*10 = (36*10+

精度计算-大数乘大数

精度计算                     大数乘大数 本算法是用来计算一个大数(现有的数据类型无法表示的数)乘以一个大数(现有的数据类型无法表示的数). 算法思路是把大数的每一位都当做一个字符放入一个字符数组中,再把乘数的各个位与被乘数的各个位从最高位依次相乘,将结果存放在一个二维数组res中.例如计算12*12,res[0][0] = 1,res[0][1] = 2,res[1][0] = 2,res[1][1] = 4,那么最终结果的数组s,s[0] = res[0][0]= 1,s

精度计算-大数加大数

精度计算                大数加大数 本算法是用来计算一个大数(现有的数据类型无法表示的数)加上一个大数(现有的数据类型无法表示的数). 算法思路是把作为被加数和加数的大数的每一位都当做一个字符分别放入一个字符数组中,再把加数的各个位与被加数的各个位从最低位依次相加,将结果存放在一个字符指针中,最后再放入一个结果数组中. 下面是我的C语言实现过程 #include<stdio.h> #include<string.h> void add(char a[],char b

杨辉三角(Pascal Triangle)的几种C语言实现及其复杂度分析

说明 本文给出杨辉三角的几种C语言实现,并简要分析典型方法的复杂度. 本文假定读者具备二项式定理.排列组合.求和等方面的数学知识. 一  基本概念 杨辉三角,又称贾宪三角.帕斯卡三角,是二项式系数在三角形中的一种几何排列.此处引用维基百科上的一张动态图以直观说明(原文链接http://zh.wikipedia.org/wiki/杨辉三角): 从上图可看出杨辉三角的几个显著特征: 1. 每行数值左右对称,且均为正整数. 2. 行数递增时,列数亦递增. 3. 除斜边上的1外,其余数值均等于其肩部两数

复杂度分析(上):如何分析、统计算法的执行效率和资源消耗

复杂度分析是什么? 复杂度分析就是分析执行一个给定算法需要消耗的计算资源数量(例如计算时间,存储器使用等)的过程. 为什么要学习复杂度分析? 没有复杂度分析怎么得到算法执行的时间和占用的内存大小 把代码运行一遍,通过统计.监控,就能得到算法执行的时间和占用的内存大小. 该方法的缺点在于: 1.测试结果非常依赖测试环境 拿同样一段代码,在 Intel Core i9 处理器上运行的速度肯定要比 Intel Core i3 快得多.同一段代码,在不同机器上运行,也可能会有截然相反的结果. 2.测试结