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

[讨论] 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.    
  18.       pc = c;
  19.       pb = b;
  20.       for (i = sb; i; i--) {
  21.          pb++;
  22.          pc++;
  23.          zaddmul(*pb, pc, a);
  24.       }
  25.    }
  26.    else {
  27.       long hsa = (sa + 1) >> 1;

  28.       if (hsa < sb) {
  29.          /* normal case */

  30.          long *T1, *T2, *T3;

  31.          /* allocate space */

  32.          T1 = stk;  stk += hsa + 2;
  33.          T2 = stk;  stk += hsa + 2;
  34.          T3 = stk;  stk += (hsa << 1) + 3;

  35.          if (stk-kmem > max_kmem) zhalt("internal error: kmem overflow");

  36.          /* compute T1 = a_lo + a_hi */

  37.          kar_fold(T1, a, hsa);

  38.          /* compute T2 = b_lo + b_hi */

  39.          kar_fold(T2, b, hsa);
  40.          
  41.          /* recursively compute T3 = T1 * T2 */

  42.          kar_mul(T3, T1, T2, stk);

  43.          /* recursively compute a_hi * b_hi into high part of c */
  44.          /* and subtract from T3 */

  45.          {
  46.             long olda, oldb;

  47.             olda = a[hsa];  a[hsa] = sa-hsa;
  48.             oldb = b[hsa];  b[hsa] = sb-hsa;

  49.             kar_mul(c + (hsa << 1), a + hsa, b + hsa, stk);
  50.             kar_sub(T3, c + (hsa << 1));

  51.             a[hsa] = olda;
  52.             b[hsa] = oldb;
  53.          }

  54.          /* recursively compute a_lo*b_lo into low part of c */
  55.          /* and subtract from T3 */

  56.          *a = hsa;
  57.          *b = hsa;

  58.          kar_mul(c, a, b, stk);
  59.          kar_sub(T3, c);

  60.          *a = sa;
  61.          *b = sb;

  62.          /* finally, add T3 * NTL_RADIX^{hsa} to c */

  63.          kar_add(c, T3, hsa);
  64.       }
  65.       else {
  66.          /* degenerate case */

  67.          long *T;
  68.          
  69.          T = stk;  stk += sb + hsa + 1;

  70.          if (stk-kmem > max_kmem) zhalt("internal error: kmem overflow");

  71.          /* recursively compute b*a_hi into high part of c */
  72.          {
  73.             long olda;

  74.             olda = a[hsa];  a[hsa] = sa-hsa;
  75.             kar_mul(c + hsa, a + hsa, b, stk);
  76.             a[hsa] = olda;
  77.          }

  78.          /* recursively compute b*a_lo into T */

  79.          *a = hsa;
  80.          kar_mul(T, a, b, stk);
  81.          *a = sa;

  82.          /* fix-up result */

  83.          kar_fix(c, T, hsa);
  84.       }
  85.    }

  86.    /* normalize result */

  87.    while (c[sc] == 0 && sc > 1) sc--;
  88.    *c = sc;
  89. }
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 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-4-28 13:15 , Processed in 0.043157 second(s), 14 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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