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

[讨论] karatsuba乘法两种算法比较测试

[复制链接]
发表于 2008-4-28 15:00:57 | 显示全部楼层
我的做法: 两个n位的数相加,结果可能出现进位,这时可用一个变量carry 表示进位(carry=0 或者 carry=1),一个长为n的数组表示结果。 两个n位的数相减,结果可能出现借位,如果出现借位,carry=-1, 结果采用补码表示,数组的各个元素使用补码表示。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-4-28 15:05:13 | 显示全部楼层
下面贴出GMP kara乘法 的代码:来自GMP 4.22 中的 mpn\generic\mul_n.c ,GMP 4.22 可从 http://swox.com/gmp/ 下载 顺便说一下,我的代码比他的简单,而且是基于2^30进制的。
  1. /* Multiplies using 3 half-sized mults and so on recursively.
  2. * p[0..2*n-1] := product of a[0..n-1] and b[0..n-1].
  3. * No overlap of p[...] with a[...] or b[...].
  4. * ws is workspace.
  5. */
  6. void
  7. mpn_kara_mul_n (mp_ptr p, mp_srcptr a, mp_srcptr b, mp_size_t n, mp_ptr ws)
  8. {
  9. mp_limb_t w, w0, w1;
  10. mp_size_t n2;
  11. mp_srcptr x, y;
  12. mp_size_t i;
  13. int sign;
  14. n2 = n >> 1;
  15. ASSERT (n2 > 0);
  16. if ((n & 1) != 0)
  17. {
  18. /* Odd length. */
  19. mp_size_t n1, n3, nm1;
  20. n3 = n - n2;
  21. sign = 0;
  22. w = a[n2];
  23. if (w != 0)
  24. w -= mpn_sub_n (p, a, a + n3, n2);
  25. else
  26. {
  27. i = n2;
  28. do
  29. {
  30. --i;
  31. w0 = a[ i ];
  32. w1 = a[n3 + i];
  33. }
  34. while (w0 == w1 && i != 0);
  35. if (w0 < w1)
  36. {
  37. x = a + n3;
  38. y = a;
  39. sign = ~0;
  40. }
  41. else
  42. {
  43. x = a;
  44. y = a + n3;
  45. }
  46. mpn_sub_n (p, x, y, n2);
  47. }
  48. p[n2] = w;
  49. w = b[n2];
  50. if (w != 0)
  51. w -= mpn_sub_n (p + n3, b, b + n3, n2);
  52. else
  53. {
  54. i = n2;
  55. do
  56. {
  57. --i;
  58. w0 = b[ i ];
  59. w1 = b[n3 + i];
  60. }
  61. while (w0 == w1 && i != 0);
  62. if (w0 < w1)
  63. {
  64. x = b + n3;
  65. y = b;
  66. sign = ~sign;
  67. }
  68. else
  69. {
  70. x = b;
  71. y = b + n3;
  72. }
  73. mpn_sub_n (p + n3, x, y, n2);
  74. }
  75. p[n] = w;
  76. n1 = n + 1;
  77. if (n2 < MUL_KARATSUBA_THRESHOLD)
  78. {
  79. if (n3 < MUL_KARATSUBA_THRESHOLD)
  80. {
  81. mpn_mul_basecase (ws, p, n3, p + n3, n3);
  82. mpn_mul_basecase (p, a, n3, b, n3);
  83. }
  84. else
  85. {
  86. mpn_kara_mul_n (ws, p, p + n3, n3, ws + n1);
  87. mpn_kara_mul_n (p, a, b, n3, ws + n1);
  88. }
  89. mpn_mul_basecase (p + n1, a + n3, n2, b + n3, n2);
  90. }
  91. else
  92. {
  93. mpn_kara_mul_n (ws, p, p + n3, n3, ws + n1);
  94. mpn_kara_mul_n (p, a, b, n3, ws + n1);
  95. mpn_kara_mul_n (p + n1, a + n3, b + n3, n2, ws + n1);
  96. }
  97. if (sign)
  98. mpn_add_n (ws, p, ws, n1);
  99. else
  100. mpn_sub_n (ws, p, ws, n1);
  101. nm1 = n - 1;
  102. if (mpn_add_n (ws, p + n1, ws, nm1))
  103. {
  104. mp_limb_t x = (ws[nm1] + 1) & GMP_NUMB_MASK;
  105. ws[nm1] = x;
  106. if (x == 0)
  107. ws[n] = (ws[n] + 1) & GMP_NUMB_MASK;
  108. }
  109. if (mpn_add_n (p + n3, p + n3, ws, n1))
  110. {
  111. mpn_incr_u (p + n1 + n3, 1);
  112. }
  113. }
  114. else
  115. {
  116. /* Even length. */
  117. i = n2;
  118. do
  119. {
  120. --i;
  121. w0 = a[ i ];
  122. w1 = a[n2 + i];
  123. }
  124. while (w0 == w1 && i != 0);
  125. sign = 0;
  126. if (w0 < w1)
  127. {
  128. x = a + n2;
  129. y = a;
  130. sign = ~0;
  131. }
  132. else
  133. {
  134. x = a;
  135. y = a + n2;
  136. }
  137. mpn_sub_n (p, x, y, n2);
  138. i = n2;
  139. do
  140. {
  141. --i;
  142. w0 = b[ i ];
  143. w1 = b[n2 + i];
  144. }
  145. while (w0 == w1 && i != 0);
  146. if (w0 < w1)
  147. {
  148. x = b + n2;
  149. y = b;
  150. sign = ~sign;
  151. }
  152. else
  153. {
  154. x = b;
  155. y = b + n2;
  156. }
  157. mpn_sub_n (p + n2, x, y, n2);
  158. /* Pointwise products. */
  159. if (n2 < MUL_KARATSUBA_THRESHOLD)
  160. {
  161. mpn_mul_basecase (ws, p, n2, p + n2, n2);
  162. mpn_mul_basecase (p, a, n2, b, n2);
  163. mpn_mul_basecase (p + n, a + n2, n2, b + n2, n2);
  164. }
  165. else
  166. {
  167. mpn_kara_mul_n (ws, p, p + n2, n2, ws + n);
  168. mpn_kara_mul_n (p, a, b, n2, ws + n);
  169. mpn_kara_mul_n (p + n, a + n2, b + n2, n2, ws + n);
  170. }
  171. /* Interpolate. */
  172. if (sign)
  173. w = mpn_add_n (ws, p, ws, n);
  174. else
  175. w = -mpn_sub_n (ws, p, ws, n);
  176. w += mpn_add_n (ws, p + n, ws, n);
  177. w += mpn_add_n (p + n2, p + n2, ws, n);
  178. MPN_INCR_U (p + n2 + n, 2 * n - (n2 + n), w);
  179. }
  180. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-4-28 16:07:38 | 显示全部楼层
下面贴出NTL 中的kara乘法 源代码,kar_mul函数来自 WinNTL-5_4_2\src\c_lip_impl.h,WinNTL可从 http://www.shoup.net下载
  1. static
  2. void kar_mul(long *c, long *a, long *b, long *stk)
  3. {
  4. long sa, sb, sc;
  5. if (*a < *b) { long *t = a; a = b; b = t; }
  6. sa = *a;
  7. sb = *b;
  8. sc = sa + sb;
  9. if (sb < KARX) {
  10. /* classic algorithm */
  11. long *pc, i, *pb;
  12. pc = c;
  13. for (i = sc; i; i--) {
  14. pc++;
  15. *pc = 0;
  16. }
  17. pc = c;
  18. pb = b;
  19. for (i = sb; i; i--) {
  20. pb++;
  21. pc++;
  22. zaddmul(*pb, pc, a);
  23. }
  24. }
  25. else {
  26. long hsa = (sa + 1) >> 1;
  27. if (hsa < sb) {
  28. /* normal case */
  29. long *T1, *T2, *T3;
  30. /* allocate space */
  31. T1 = stk; stk += hsa + 2;
  32. T2 = stk; stk += hsa + 2;
  33. T3 = stk; stk += (hsa << 1) + 3;
  34. if (stk-kmem > max_kmem) zhalt("internal error: kmem overflow");
  35. /* compute T1 = a_lo + a_hi */
  36. kar_fold(T1, a, hsa);
  37. /* compute T2 = b_lo + b_hi */
  38. kar_fold(T2, b, hsa);
  39. /* recursively compute T3 = T1 * T2 */
  40. kar_mul(T3, T1, T2, stk);
  41. /* recursively compute a_hi * b_hi into high part of c */
  42. /* and subtract from T3 */
  43. {
  44. long olda, oldb;
  45. olda = a[hsa]; a[hsa] = sa-hsa;
  46. oldb = b[hsa]; b[hsa] = sb-hsa;
  47. kar_mul(c + (hsa << 1), a + hsa, b + hsa, stk);
  48. kar_sub(T3, c + (hsa << 1));
  49. a[hsa] = olda;
  50. b[hsa] = oldb;
  51. }
  52. /* recursively compute a_lo*b_lo into low part of c */
  53. /* and subtract from T3 */
  54. *a = hsa;
  55. *b = hsa;
  56. kar_mul(c, a, b, stk);
  57. kar_sub(T3, c);
  58. *a = sa;
  59. *b = sb;
  60. /* finally, add T3 * NTL_RADIX^{hsa} to c */
  61. kar_add(c, T3, hsa);
  62. }
  63. else {
  64. /* degenerate case */
  65. long *T;
  66. T = stk; stk += sb + hsa + 1;
  67. if (stk-kmem > max_kmem) zhalt("internal error: kmem overflow");
  68. /* recursively compute b*a_hi into high part of c */
  69. {
  70. long olda;
  71. olda = a[hsa]; a[hsa] = sa-hsa;
  72. kar_mul(c + hsa, a + hsa, b, stk);
  73. a[hsa] = olda;
  74. }
  75. /* recursively compute b*a_lo into T */
  76. *a = hsa;
  77. kar_mul(T, a, b, stk);
  78. *a = sa;
  79. /* fix-up result */
  80. kar_fix(c, T, hsa);
  81. }
  82. }
  83. /* normalize result */
  84. while (c[sc] == 0 && sc > 1) sc--;
  85. *c = sc;
  86. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-4-28 16:39:13 | 显示全部楼层
两个采取了不同的策略啊
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-4-29 10:15:45 | 显示全部楼层
原帖由 无心人 于 2008-4-28 09:12 发表 是否存在比较的必要? 考虑工作量 1、前加法2k 乘法(k+1)^2 后加法2k 总减法 2k+1 2、前减法2k 纠正2k+2 乘法k^2 后加法2k 总加法 2k+1 差异在 1、乘法(k+1)^2 2、纠正2k+2 乘法k^2 (k+1)^2 - k^2 - (2k ...
其实,前加法也并非一定会出现进位。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-4-29 10:16:51 | 显示全部楼层
应该说,直接采用楼主的公式还不是最优化的, 更好的形式可以进一步减少一次 大数的加/减法 运算。 下面的公式是从我的 HugeCalc 源代码注释中抠出的:
// ( A1*b + A0 )( B1*b + B0 ) // = (( A1*B1*b - A0*B0 ) + ( A0 + A1 )( B0 + B1 ))*b // - ( A1*B1*b - A0*B0 ) // ( A1*b + A0 )( B1*b + B0 ) // = (( A1*B1*b + A0*B0 ) - ( A0 - A1 )( B0 - B1 ))*b // + ( A1*B1*b + A0*B0 )
计算400000!(屏蔽Toom算法及卷积算法),前者需 5.919233s,后者需 6.188290s 也就是说,在 HugeCalc 上的表现,用第一种形式可比第二种至少快 4% 左右。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-4-29 10:45:31 | 显示全部楼层
我公布一下我的kara乘法的 技术数据: 1.因为我的加减法接口是这样的 int DEC_Add(DWORD *a,const DWORD *b,size_t len) 故在做加减法是可使用更少的寄存器,访问更少的内存。缺点应用到kara_mul时,需要使用内存拷贝函数. 2.若计算2个长度为 2*len的数的乘积,则在每次递归调用kara_mul时,除了乘法外,需要附加的操作有: 1. 2 * len 次整数减法。 2. len + len + 4*len 次整数复制 3. 3 * (len*2) 次整数加法 4. 2 * len * 0.5 次整数求补 (大约有50%的概率需要做求补运算) 共计:2×len 次减法,6×len 次整数加法,6×len次整数复制,len次 求补运算。 gxq可否给出你的kara乘法中,除了乘法外的其他运算的次数。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-4-29 11:01:40 | 显示全部楼层

以第1种形式分析

// ( A1*b + A0 )( B1*b + B0 ) // = (( A1*B1*b -[1] A0*B0 ) +[5] ( A0 +[3] A1 )( B0 +[4] B1 ))*b // -[6] ( A1*B1*b - A0*B0 )[2]
[1] 2*len 次整数减法 - A0*B0 [2] 3*len 次整数复制 ( A1*B1*b - A0*B0 ) [3] len 次整数加法 + A1 [4] len 次整数加法 + B1 [5] 2*len 次整数加法 + ( A0 + A1 )( B0 + B1 ) [6] 3*len 次整数减法 - ( A1*B1*b - A0*B0 ) 注:选择好适当大小的 b,可以确保上述所有减法无须修正(即结果一定为正)。 合计:加减法 9*len 单元次;复制 3*len 单元次;乘法 3*len*len 单元次;移位 无(在相关运算中直接定位即可)
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2008-4-29 11:31:54 | 显示全部楼层
GxQ 你没考虑过你的加减法和求负还有内存拷贝都不是最优的 加减存在一次处理4X32位操作的算法 求负,拷贝存在一次处理4X128位操作的算法
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2008-4-29 11:41:34 | 显示全部楼层
那需要用户CPU至少得支持 SSE2 才行。 而有许多老爷机是不具备的。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

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

GMT+8, 2024-11-22 00:33 , Processed in 0.026540 second(s), 14 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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