我正在为家庭作业项目写一个小的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
答案 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);
}
当然,这是非常低效的。