我已经使用以下代码在C:
中执行存储在char *中的2个任意数字之间的乘法运算#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void mult(char *n1, char *n2)
{
char *res;
int mul, i, j;
res = malloc(sizeof(*res) * (strlen(n1) + strlen(n2) + 1));
memset(res, '0', strlen(n1) + strlen(n2));
res[strlen(n1) + strlen(n2)] = 0;
for (i = strlen(n1) - 1; i >= 0; i--)
{
for (j = strlen(n2) - 1; j >= 0; j--)
{
mul = (n1[i] - '0') * (n2[j] - '0');
res[i + j] += ((res[i + j + 1] + mul - '0') / 10);
res[i + j + 1] = ((res[i + j + 1] + mul - '0') % 10) + '0';
}
}
printf("%s\n", res);
free(res);
}
我使用-O3
标志编译它,但对于86 k位的大数字,它需要大约30秒。
我怎样才能让它更快?
答案 0 :(得分:2)
总结评论:
也许使用专用struct(dyn数组+维护大小信息)会比string更好。长度和+ / - &#39; 0&#39;只有在需要数字的I / O时才会执行。
答案 1 :(得分:0)
以下是一个稍微优化的版本,使用了很多评论&#39;建议:
void mult(char *n1, char *n2)
{
char *res, *pres, *p1, *p2;
int mul, i, j, l1= strlen(n1), l2= strlen(n2);
res = malloc(sizeof(*res) * (l1 + l2 + 1));
memset(res, 0, l1 + l2);
res[l1 + l2] = 0;
for (i=0, p1=n1; i<l1; i++) *p1++ -= '0';
for (j=0, p2=n2; j<l2; j++) *p2++ -= '0';
p1= n1+l1-1;
for (i= l1 - 1; i >= 0; i--, p1--)
{
p2= n2+l2-1;
pres= res+i;
for (j = l2 - 1; j >= 0; j--, p2--)
{
mul = *p1 * *p2;
pres[j] += ((pres[j + 1] + mul) / 10);
pres[j + 1] = ((pres[j + 1] + mul) % 10);
}
}
for (i=0, p1=res; i<l1+l2; i++) *p1++ += '0';
printf("%s\n", res);
free(res);
}
答案 2 :(得分:0)
避免重复调用strlen()
。在for (j = strlen(n2) ...
循环中@Michael Walz为for (i ...
。
不是重复减去/添加'0'
,而是在开头和结尾处执行一次。对于很长的字符串来说这是值得的,对于短字符串则是如此。 @Michael Walz @Eugene Sh.
不要触摸内循环中的2个产品元素(一次添加到产品中,一次用于携带),请执行一次并形成进位。
利用前导零。
一些未经测试的代码。一定要释放释放返回的指针。编码为清晰与代码打高尔夫。
#include <stdlib.h>
#include <string.h>
static void string_offset(char *s, size_t sz, int offset) {
while (sz > 0) {
sz--;
s[sz] += offset;
}
}
char * string_string_mult(char *a, char *b) {
while (*a == '0') a++;
size_t alen = strlen(a);
size_t asz = alen + 1;
string_offset(a, alen, -'0');
while (*b == '0') b++;
size_t blen = strlen(b);
size_t bsz = blen + 1;
if (a != b) { // TBD to fully detect a,b, overlap
string_offset(b, blen, -'0');
}
size_t psz = asz + bsz;
char *product = calloc(psz, sizeof *product); // account for potential carry and \0
if (product == NULL) return NULL;
for (size_t ai = alen; ai > 0;) {
ai--;
char *p = product + ai + blen + 1;
int acc = 0;
for (size_t bi = blen; bi > 0;) {
bi--;
acc = *p + a[ai] * b[bi] + acc;
*p = acc %= 10;
p--;
acc /= 10;
}
*p += acc;
}
string_offset(product, psz - 1, +'0');
// test for an unneeded leading zero
if (product[0] == '0' && (alen + blen > 0)) {
memmove(product, product + 1, psz - 1);
}
string_offset(a, alen, +'0');
if (a != b) { // TBD to fully detect a,b, overlap
string_offset(b, blen, +'0');
}
return product;
}
详细
a
和b
可能指向相同的字符串。 size_t
是用于数组索引的最佳类型。 答案 3 :(得分:0)
我检查了大多数建议的优化(LutzL&amp; 10000基础10000计划除外)并且它们没有显着差异。由于您已经确定bc
拥有正确的内容,为什么不简单地使用它而不是尝试重新实现它:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BUFFER_SIZE 1024
void mult(const char *n1, const char *n2) {
FILE *pipe = popen("/usr/bin/bc", "r+");
fprintf(pipe, "%s * %s\nquit\n", n1, n2);
if (ferror(pipe)) {
fprintf(stderr, "Output to pipe failed.\n");
exit(EXIT_FAILURE);
}
char *buffer = malloc(BUFFER_SIZE); // bc long lines are limited to 70 characters
char *result = fgets(buffer, BUFFER_SIZE, pipe);
while (result != NULL) {
char *s = rindex(buffer, '\\'); // find and remove line continuation marker
if (s != NULL) {
*s = '\0';
}
fputs(buffer, stdout);
result = fgets(buffer, BUFFER_SIZE, pipe);
}
(void) pclose(pipe);
}
在我的系统上,这会在一秒钟内将两个86K的数字相乘。
请注意,我的系统(OSX)具有双向popen()
,这在许多系统上是单向的,因此您需要使用建议的解决方案来进行双向通信。
上面的代码可以使用更多错误检查,您可以使用bc
环境变量BC_LINE_LENGTH
来生成更长的行以提高效率,并且在某些系统上将其设置为零以避免换行。< / p>