找回密码
 欢迎注册
楼主: 无心人

[讨论] B计划之大数乘以10,除以10的快速算法的讨论和实现

[复制链接]
发表于 2008-6-13 08:00:17 | 显示全部楼层
原帖由 gxqcn 于 2008-6-12 21:15 发表
我没有针对除10作特殊优化。

且我给的算法仅适用于基数是10的整数次幂的情形。
这是刚刚注意到的,难怪你在6#曾强调“长整数”,
因为我看到10,脑子里就默认用10的整数次幂做基数了。


上面的回复不太正确,当时脑子有点乱,也没有源代码可供参考。
昨天晚上在台式机上安装 Visual Studio 2008,我是在老婆的笔记本上登录论坛的。

我说一下我的做法,该算法通用于以任意数为基数的大数运算。

对于除数d为int型的大数除法,下面仅针对无符号型:
1、若d==0,则 HugeCalc::SetLastError( HCERR_DIV_ZERO ),return;
2、如果恰好等于大数内部基数,右移“一位”即可;
3、按小学生的除法算法,从高位向低位挺进:(上次的余数*基数+当前值)/d=商…本次的余数
但,当d为2的整数次幂时,上述除法指令可被移位指令替代优化。

对于“大整数*常规小整数”,类似,只是改为从低位向高位挺进模式进行而已。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-6-13 08:20:36 | 显示全部楼层
你要做这个优化的目的是什么呢?
如果仅仅要做进制转化,是不需要这个优化的
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-6-13 08:25:55 | 显示全部楼层
进制转化确实不应这么做,因为这样做的算法复杂度太高了。
可能楼主因为经常需要碰到10进制下的“移位”处理吧。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-6-13 09:18:28 | 显示全部楼层
如果这样,可能直接进行十进制计算更加好些
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-6-13 09:44:27 | 显示全部楼层
在 常规小整数<大整数基数 前提下,不同基数下的乘除法效率基本是一致的。

差异仅在于其中蕴涵的“*基数”过程是否可用移位优化,
以及因基数大小的不同,相同大整数被分割的次数。

用 n^b 为基数的大整数,在乘以(或除以)n^k 时,
当 k>>b 时,可先通过以基数的“移位”,而后再乘以(或除以)n^(k mod b) 即可。
前者是真正意义上的“移位”,是个O(1)的操作。

所以,如果算法中存在大量乘以(或除以)“10的整数次幂”运算时,
考虑以 10^b 为基数是有必要的。

最显著的例子,如大整数的输出效率。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-6-13 10:06:07 | 显示全部楼层
乘以10的移位肯定是快于直接乘吧?

能有兴趣测试么?

不过,C代码在这个问题上应该不合适
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-6-13 10:16:25 | 显示全部楼层
这很难说,1次mul 与 2次shift+1次add 谁快?可能不同系统上有不同答案。

还有,大数基数*10 ≥ 2^32 ?这个在算法设计优化时也很重要。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-6-13 11:15:23 | 显示全部楼层
下面仅是一些想法

如果32位系统,这个问题的话,用SSE2存在一次移位64位的算法
考虑循环展开的话,一次移位操作可以达到256位

加肯定存在一次加128位的4路循环展开算法的,这个我和liangbch已达到共识

乘也存在一次乘128位的的4路循环展开算法的,这个我也和liangbch已达到共识
但乘的逻辑复杂的多

考虑指令时间
乘是9个周期
加和移位是1-2个

如果安排合理,
按双字算,一个运算单位消耗的时钟
乘能达到12个(可能不准确)
加是5个
移位是0.5个

所以推测,至少是不输于直接乘
但运算时间最快只能达到直接乘的1/2左右
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2013-6-4 20:54:01 | 显示全部楼层
17# gxqcn
嗯,除了像P4之类的要考虑下,乘法其实很快了, 30# G-Spider
这里关于乘法逆元用在这个帖子里不合适,事实上其局限性很大。"Quotient will only be correct if d divides a exactly."
n_limb/1_limb,不知道郭用的什么方法,gmp中mpn_divrem_1 -- mpn by limb division.用到的方法,我没有看特别明白,可能是先求导数(选取有些讲究),再乘法代替除法。
而这种方法用到的变量有些多,于是在x86 ALU asm中没有看到相关的实现,xmm和x64倒是有,如mpn\x86\p6\mmx\Divrem_1.asm。
mpn_invert_limb
按照它的说法
At 4 limbs the div is a touch faster than the mul (and of course  simpler), so start the mul from 5 limbs. (mul_by_inverse)
在vc6.0中也有关于一个常量的除法的优化方法:
如(unsigned int) n/3,优化为
  1.         mov        eax, -1431655765                        ; aaaaaaabH
  2.         mul        n
  3.         shr        edx, 1
复制代码
aaaaaaabH 对应于2/3 (规范化,有助于得到高精度的表示),于是上在的代码解释为(n*(2/3))/2.
又比如n/10
  1.         mov        eax, -858993459                                ; cccccccdH
  2.         mul        n
  3.         shr        edx, 3
复制代码
1/10=(4/5)/8 如上,cccccccdH 为4/5,然后除以8(用右移3)。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2013-6-5 23:05:15 | 显示全部楼层
19# G-Spider
上面vc做的优化是dividing a single-word number by a single-word number ,using a single-word approximate reciprocal.
这在大数除以1个limb的情况下不合适,大数中需要实现dividing a two-word number by a single-word number,using a single-word approximate reciprocal.的优化方案。
gmp有篇相关的文章Improved division by invariant integers
division-paper[1].pdf (138.97 KB, 下载次数: 1)
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-4-27 10:54 , Processed in 0.053650 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表