任意精度(bignum)整数的乘法算法

时间:2010-05-02 21:33:20

标签: c algorithm math bignum multiplication

我正在为家庭作业项目写一个小的bignum图书馆。我要实现Karatsuba乘法,但在此之前我想写一个天真的乘法例程。

我正在按照Paul Zimmerman撰写的题为“现代计算机算术”的指南,freely available online

在第4页,有一个名为BasecaseMultiply的算法的描述,该算法执行gradechool multiplication。

我理解步骤2,3,其中B ^ j是1,j次的数字移位。 但我不明白第1步和第3步,我们有A * b_j。如果还没有定义bignum乘法,那么这个乘法是如何实现的呢?

此算法中的操作“*”是否只是重复添加方法?

这是我到目前为止所写的部分。我对它们进行了单元测试,因此它们似乎在大多数情况下都是正确的:

我用于我的bignum的结构如下:

#define BIGNUM_DIGITS 2048
typedef uint32_t u_hw; // halfword
typedef uint64_t u_w; // word

typedef struct {
    unsigned int sign; // 0 or 1
    unsigned int n_digits;
    u_hw digits[BIGNUM_DIGITS];
} bn;

目前可用的例程:

bn *bn_add(bn *a, bn *b); // returns a+b as a newly allocated bn
void bn_lshift(bn *b, int d); // shifts d digits to the left, retains sign
int bn_cmp(bn *a, bn *b); // returns 1 if a>b, 0 if a=b, -1 if a<b

2 个答案:

答案 0 :(得分:2)

我刚才写了一个乘法算法,我在顶部有这个评论。如果你有两个相同大小的数字x和y(相同的n_digits)那么你会像这样乘以得到n,这将是两位数。如果两个输入的n_digits不相同,算法的部分复杂性来自于计算哪些位不会相乘。

从右边开始,n0是x0 * y0,你可以省去溢出。现在n1是x1 * y0和y1 * x0之和,前一个溢出移动了你的数字大小。如果在64位数学运算中使用32位数字,则表示n0 = low32(x0 * y0),并且输入high32(x0 * y0)作为溢出。您可以看到,如果您实际使用32位数字,则无法在不超过64位的情况下添加中心列,因此您可能使用30或31位数字。

如果每位数有30位,则表示您可以将两个8位数字组合在一起。首先编写此算法以接受两个n_digits最多为8的小缓冲区,并使用本机数学运算。然后再次实现它,采用任意大小的n_digits并使用第一个版本,以及你的shift和add方法,一次乘以8x8个数字块。

/*
    X*Y = N

                          x0     y3
                            \   /  
                             \ /   
                              X    
                      x1     /|\     y2
                        \   / | \   /  
                         \ /  |  \ /   
                          X   |   X    
                  x2     /|\  |  /|\     y1
                    \   / | \ | / | \   /  
                     \ /  |  \|/  |  \ /   
                      X   |   X   |   X    
              x3     /|\  |  /|\  |  /|\     y0
                \   / | \ | / | \ | / | \   /
                 \ /  |  \|/  |  \|/  |  \ /
                  V   |   X   |   X   |   V
                  |\  |  /|\  |  /|\  |  /|
                  | \ | / | \ | / | \ | / |
                  |  \|/  |  \|/  |  \|/  |
                  |   V   |   X   |   V   |
                  |   |\  |  /|\  |  /|   |
                  |   | \ | / | \ | / |   |
                  |   |  \|/  |  \|/  |   |
                  |   |   V   |   V   |   |
                  |   |   |\  |  /|   |   |
                  |   |   | \ | / |   |   |
                  |   |   |  \|/  |   |   |
                  |   |   |   V   |   |   |
                  |   |   |   |   |   |   |
              n7  n6  n5  n4  n3  n2  n1  n0
*/

答案 1 :(得分:1)

要做A * b_j,你需要将bignum的等级学校乘以一个数字。您最终必须将一堆两位数的产品加在一起:

bn *R = ZERO;
for(int i = 0; i < n; i++) {
  bn S = {0, 2};
  S.digits[0] = a[i] * b_j;
  S.digits[1] = (((u_w)a[i]) * b_j) >> 32;  // order depends on endianness
  bn_lshift(S, i);
  R = bn_add(R, S);
}

当然,这是非常低效的。