我正在编写一个程序(在C中),我尝试在尽可能短的时间内计算大数字的幂。我将数字表示为数字向量,因此所有操作都必须手工编写。
如果没有中间结果的所有分配和解除分配,程序会快得多。有没有算法进行整数乘法,就地?例如,函数
void BigInt_Times(BigInt *a, const BigInt *b);
会将a
和b
的乘法结果放在a
,中,而不使用中间值。
答案 0 :(得分:2)
嗯,标准算法包括将'a'的每个数字(字)与'b'的每个数字相乘,并将它们加到结果中的适当位置。因此,第i个数字进入结果的i到i + n的每个数字。因此,为了“就地”执行此操作,您需要计算从最重要到最小的输出数字。这比从最少到大多数做起来有点棘手,但并不多......
答案 1 :(得分:2)
此处,muln()
2n (实际上,n)对于无符号整数,n = 2n 就地乘法。您可以将其调整为使用32位或64位“数字”而不是8位。为了清楚起见,模数运算符保留。
muln2()
n是n = n 就地乘法(如提示here),也是在8位“数字”上运行。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
typedef unsigned char uint8;
typedef unsigned short uint16;
#if UINT_MAX >= 0xFFFFFFFF
typedef unsigned uint32;
#else
typedef unsigned long uint32;
#endif
typedef unsigned uint;
void muln(uint8* dst/* n bytes + n extra bytes for product */,
const uint8* src/* n bytes */,
uint n)
{
uint c1, c2;
memset(dst + n, 0, n);
for (c1 = 0; c1 < n; c1++)
{
uint8 carry = 0;
for (c2 = 0; c2 < n; c2++)
{
uint16 p = dst[c1] * src[c2] + carry + dst[(c1 + n + c2) % (2 * n)];
dst[(c1 + n + c2) % (2 * n)] = (uint8)(p & 0xFF);
carry = (uint8)(p >> 8);
}
dst[c1] = carry;
}
for (c1 = 0; c1 < n; c1++)
{
uint8 t = dst[c1];
dst[c1] = dst[n + c1];
dst[n + c1] = t;
}
}
void muln2(uint8* dst/* n bytes */,
const uint8* src/* n bytes */,
uint n)
{
uint c1, c2;
if (n >= 0xFFFF) abort();
for (c1 = n - 1; c1 != ~0u; c1--)
{
uint16 s = 0;
uint32 p = 0; // p must be able to store ceil(log2(n))+2*8 bits
for (c2 = c1; c2 != ~0u; c2--)
{
p += dst[c2] * src[c1 - c2];
}
dst[c1] = (uint8)(p & 0xFF);
for (c2 = c1 + 1; c2 < n; c2++)
{
p >>= 8;
s += dst[c2] + (uint8)(p & 0xFF);
dst[c2] = (uint8)(s & 0xFF);
s >>= 8;
}
}
}
int main(void)
{
uint8 a[4] = { 0xFF, 0xFF, 0x00, 0x00 };
uint8 b[2] = { 0xFF, 0xFF };
printf("0x%02X%02X * 0x%02X%02X = ", a[1], a[0], b[1], b[0]);
muln(a, b, 2);
printf("0x%02X%02X%02X%02X\n", a[3], a[2], a[1], a[0]);
a[0] = -2; a[1] = -1;
b[0] = -3; b[1] = -1;
printf("0x%02X%02X * 0x%02X%02X = ", a[1], a[0], b[1], b[0]);
muln2(a, b, 2);
printf("0x%02X%02X\n", a[1], a[0]);
return 0;
}
输出:
0xFFFF * 0xFFFF = 0xFFFE0001
0xFFFE * 0xFFFD = 0x0006
我认为这是我们可以做到的最好的。关于muln2()
我不喜欢的一件事是,它必须积累更大的中间产品,然后传播更大的进位。
答案 2 :(得分:0)
听起来你真的不需要算法。相反,您需要更好地使用语言的功能。
为什么不创建您在答案中指明的功能?使用它,享受! (该函数最终可能会返回对 a 的引用作为结果。)
答案 3 :(得分:0)
通常,big-int表示的长度取决于所代表的值;通常,结果将比任一操作数更长。特别是,对于乘法,结果表示的大小大致是参数大小的总和。
如果您确定内存管理确实是您的特定平台的瓶颈,您可以考虑实现一个更新第三个值的乘法函数。就你上面的C风格函数原型而言:
void BigInt_Times_Update(const BigInt* a, const BigInt* b, BigInt* target);
这样,您可以像C ++ std :: vector&lt;&gt;一样处理内存管理。容器:您的更新目标只需要在现有大小太小时重新分配其堆数据。