c语言大数相乘的算法_计算机大数乘法引发的思考 | CSDN 博文精选

cbcd3e58753c9c5744943896ca6e608d.png

作者 | dog250

责编 | 屠敏

出品 | CSDN博客

近日,看了小小的一道学而思数学作业:

计算

201×33×707+484×636321×33×707+484×6363

我知道肯定是把数字拆开,配合结合律完成一种 “巧算” ,之所以称之为“巧算”,是因为这种算法比通过竖式直接硬算要节省不少步骤。

但我一下子想不到怎么拆解,我也懒得思考,因为我在思考另一件事。

上题的答案是(各种因数分解,结合律):

原式=67×3×33×707+11×44×9×707=67×3×33×707+11×44×9×707=11×99×707=111×99×7×101=777×9999=7770000−777=7769223

本文结束了,以下皆为附录。

beb511a30c1ef0d4561eab205619daf9.png

通俗来讲,一个计算的所有步骤就是一个算法,算法的时间复杂度其实就是计算的规模和步骤数量之间的关系。

以乘法竖式为例,如果我们将一次十进制一位乘法(即99乘法表的乘法)作为一个步骤,那么两个n位乘数相乘需要n的二次方个步骤,其时间复杂度就是O(n

2) ,但是如果我们采用某种“巧算”,那么计算步骤将会大大减少。

小学,中学老师教的各种“巧算”技巧,其宗旨都是减少计算量。我们已经承蒙了12年有余的教诲,现在让我们进入计算机世界。

f3950fa34fc6488fa50ad9b70f5e41ed.png

计算机乘法和我们用竖式计算乘法没有本质区别。看看加法器,乘法器的门电路就知道了。

门电路不是我们要关注的层次,门电路实在是太快了,快到你几乎无法感知它计算2×3和24890125×98723988的差别。机器是瞬间得到结果的。

人背下下来了99乘法表,所以人只能一位一位的计算乘法,但计算机不,计算机依靠自身的硬件门电路可以轻而易举计算出其内建数据类型乘法,64位的CPU可以轻易计算 0xFFFFFFFFFFFFFFFF 范围内的任意乘法,就好像我们人类计算99乘法表的乘法一样(我们早就把这个99乘法表背下来了,深刻在了我们的大脑硬件乘法器里)。

然而,超过计算机内建类型范围,计算机便无能为力了。

32位计算机最多只能处理32位的数字,64位计算机自然只能处理64位数字,计算机处理超过内建数据类型范围的数字计算的过程称为 “大数计算” 。

以64位为例,当计算机面对超过64位的数字乘法时,就好像我们人类面对超过一位数的乘法一样,无法 “一下子” 得到结果,必须需要某种步骤来计算结果。这就是说,需要某种算法来进行生成一系列的计算步骤,而 步骤的多少决定了算法的好坏。

举一个例子,我们尝试让计算机计算下面的式子:

23567971209865125789034451795247×12345678909988776655443314719047=?

我们当然希望设计一种巧算的步骤,但在此之前,我们先设计一种 按部就班的算法,类似我们手算竖式一样:

21c82c7984dae93a0d7176c028710ace.png

人就是这么算的,老老实实地按照十进制99乘法表,一个数字一个数字地进行计算,计算过程中处理进位。

手工算竖式人人都会,说这些也无益,上周三下班的班车上,顺手撸了一个代码,感觉还好,发了个朋友圈就想分享出来,本周就休息一天,赶早起来就写下了这篇文章。

模拟竖式计算的大数乘法C代码如下:

// mul.c
// gcc mul.c -o mul
#include
#include
#include
void inlinecarry_add(char *tmp, char num, int index)
{
char tmp_num = 0;
char carry;
tmp_num = tmp[index] + num;
if (tmp_num > 9) {
carry = tmp_num / 10;
tmp[index] = tmp_num%10;
carry_add(tmp, carry, index-1); // 递归进位到底
//tmp[index - 1] += carry; // 当次进位不能保证tmp[index - 1]+'0'是一个字符
} else {
tmp[index] = tmp_num;
}
}
intmul(char *mul1, char *mul2, char *result)
{
int i, j, mid;
int len1, len2, len, pos = 0;
char *tmp;
len1 = strlen(mul1);
len2 = strlen(mul2);
len = len1 + len2;
tmp = (char *)calloc(len, 1);
for (i = 0; i < len2; i++) {
for (j = 0; j < len1; j++) {
int idx = len - j - i - 1;
mid = (mul2[len2 - i - 1] - '0') * (mul1[len1 - j - 1] - '0');
// 这里我是在计算过程中直接递归处理进位的,而不是在一轮乘法后再用一个for循环处理。
carry_add(tmp, mid, idx);
}
// 我不需要在这里用for循环统一处理进位。
// Nothing todo!
}
i = 0;
while(tmp[i++] == 0) pos++;
len = len - pos;
memcpy(result, tmp + pos, len);
free (tmp);
for (i = 0; i < len; i++) {
result[i] += '0';
}
return 0;
}
intmain(int argc, char **argv)
{
int len1, len2, i, count;
char *m1, *m2, *result;
m1 = argv[1];
m2 = argv[2];
count = atoi(argv[3]);
len1 = strlen(m1);
len2 = strlen(m2);
result = calloc(len1 + len2, 1);
// 为了比较速度,这里循环执行count次。
for (i = 0; i < count; i++) {
memset(result, 0, len1 + len2);
mul(m1, m2, result);
}
printf("%s
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值