在C语言中,宽度最大的无符号整数类型是unsigned long long, 占8个字节。那么,如果整数超过8个字节,如何进行大数乘法呢? 例如:

$ python
Python 2.7.6 (default, Oct 26 2016, 20:32:47)
...<snip>....
>>> a = 0x123456781234567812345678
>>> b = 0x876543211234567887654321
>>> print "a * b = 0x%x" % (a * b)
a * b = 0x9a0cd057ba4c159a33a669f0a522711984e32bd70b88d78

用C语言实现大数乘法,跟十进制的多位数乘法类似,基本思路是采用分而治之的策略难点就是进位处理相对比较复杂。本文尝试给出C代码实现(基于小端),并使用Python脚本验证计算结果。

1. foo.c

 #include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef unsigned char byte; /* 1 byte */
typedef unsigned short word; /* 2 bytes */
typedef unsigned int dword; /* 4 bytes */
typedef unsigned long long qword; /* 8 bytes */ typedef struct big_number_s {
dword *data;
dword size;
} big_number_t; static void
dump(char *tag, big_number_t *p)
{
if (p == NULL)
return; printf("%s : data=%p : size=%d:\t", tag, p, p->size);
for (dword i = ; i < p->size; i++)
printf("0x%08x ", (p->data)[i]);
printf("\n");
} /*
* Add 64-bit number (8 bytes) to a[] whose element is 32-bit int (4 bytes)
*
* e.g.
* a[] = {0x12345678,0x87654321,0x0}; n = 3;
* n64 = 0xffffffff12345678
*
* The whole process of add64() looks like:
*
* 0x12345678 0x87654321 0x00000000
* + 0x12345678 0xffffffff
* -----------------------------------
* = 0x2468acf0 0x87654321 0x00000000
* + 0xffffffff
* -----------------------------------
* = 0x2468acf0 0x87654320 0x00000001
*
* Finally,
* a[] = {0x2468acf0,0x87654320,0x00000001}
*/
static void
add64(dword a[], dword n, qword n64)
{
dword carry = ; carry = n64 & 0xFFFFFFFF; /* low 32 bits of n64 */
for (dword i = ; i < n; i++) {
if (carry == 0x0)
break; qword t = (qword)a[i] + (qword)carry;
a[i] = t & 0xFFFFFFFF;
carry = (dword)(t >> ); /* next carry */
} carry = (dword)(n64 >> ); /* high 32 bits of n64 */
for (dword i = ; i < n; i++) {
if (carry == 0x0)
break; qword t = (qword)a[i] + (qword)carry;
a[i] = t & 0xFFFFFFFF;
carry = (dword)(t >> ); /* next carry */
}
} static big_number_t *
big_number_mul(big_number_t *a, big_number_t *b)
{
big_number_t *c = (big_number_t *)malloc(sizeof(big_number_t));
if (c == NULL) /* malloc error */
return NULL; c->size = a->size + b->size;
c->data = (dword *)malloc(sizeof(dword) * c->size);
if (c->data == NULL) /* malloc error */
return NULL; memset(c->data, , sizeof(dword) * c->size); dword *adp = a->data;
dword *bdp = b->data;
dword *cdp = c->data;
for (dword i = ; i < a->size; i++) {
if (adp[i] == 0x0)
continue; for (dword j = ; j < b->size; j++) {
if (bdp[j] == 0x0)
continue; qword n64 = (qword)adp[i] * (qword)bdp[j];
dword *dst = cdp + i + j;
add64(dst, c->size - (i + j), n64);
}
} return c;
} static void
free_big_number(big_number_t *p)
{
if (p == NULL)
return; if (p->data != NULL)
free(p->data); free(p);
} int
main(int argc, char *argv[])
{
dword a_data[] = {0x12345678, 0x9abcdef0, 0xffffffff, 0x9abcdefa, 0x0};
dword b_data[] = {0xfedcba98, 0x76543210, 0x76543210, 0xfedcba98, 0x0}; big_number_t a;
a.data = (dword *)a_data;
a.size = sizeof(a_data) / sizeof(dword); big_number_t b;
b.data = (dword *)b_data;
b.size = sizeof(b_data) / sizeof(dword); dump("BigNumber A", &a);
dump("BigNumber B", &b);
big_number_t *c = big_number_mul(&a, &b);
dump(" C = A * B", c);
free_big_number(c); return ;
}

2. bar.py

 #!/usr/bin/python

 import sys

 def str2hex(s):
l = s.split(' ') i = len(l)
out = ""
while i > 0:
i -= 1
e = l[i]
if e.startswith("0x"):
e = e[2:]
out += e out = "0x%s" % out
n = eval("%s * %d" % (out, 0x1))
return n def hex2str(n):
s_hex = "%x" % n
if s_hex.startswith("0x"):
s_hex = s_hex[2:] n = len(s_hex)
m = n % 8
if m != 0:
s_hex = '' * (8 - m) + s_hex
n += (8 - m)
i = n
l = []
while i >= 8:
l.append('0x' + s_hex[i-8:i])
i -= 8
return "%s" % ' '.join(l) def main(argc, argv):
if argc != 4:
sys.stderr.write("Usage: %s <a> <b> <c>\n" % argv[0])
return 1 a = argv[1]
b = argv[2]
c = argv[3]
ax = str2hex(a)
bx = str2hex(b)
cx = str2hex(c) axbx = ax * bx
if axbx != cx:
print "0x%x * 0x%x = " % (ax, bx)
print "got: 0x%x" % axbx
print "exp: 0x%x" % cx
print "res: FAIL"
return 1 print "got: %s" % hex2str(axbx)
print "exp: %s" % c
print "res: PASS"
return 0 if __name__ == '__main__':
argv = sys.argv
argc = len(argv)
sys.exit(main(argc, argv))

3. Makefile

CC        = gcc
CFLAGS = -g -Wall -m32 -std=c99 TARGETS = foo bar all: $(TARGETS) foo: foo.c
$(CC) $(CFLAGS) -o $@ $< bar: bar.py
cp $< $@ && chmod +x $@ clean:
rm -f *.o
clobber: clean
rm -f $(TARGETS)
cl: clobber

4. 编译并测试

$ make
gcc -g -Wall -m32 -std=c99 -o foo foo.c
cp bar.py bar && chmod +x bar
$ ./foo
BigNumber A : data=0xbfc2a7c8 : size=: 0x12345678 0x9abcdef0 0xffffffff 0x9abcdefa 0x00000000
BigNumber B : data=0xbfc2a7d0 : size=: 0xfedcba98 0x76543210 0x76543210 0xfedcba98 0x00000000
C = A * B : data=0x8967008 : size=: 0x35068740 0xee07360a 0x053bd8c9 0x2895f6cd 0xb973e57e 0x4e6cfe66 0x0b60b60b 0x9a0cd056 0x00000000 0x00000000
$ A="0x12345678 0x9abcdef0 0xffffffff 0x9abcdefa 0x00000000"
$ B="0xfedcba98 0x76543210 0x76543210 0xfedcba98 0x00000000"
$ C="0x35068740 0xee07360a 0x053bd8c9 0x2895f6cd 0xb973e57e 0x4e6cfe66 0x0b60b60b 0x9a0cd056 0x00000000 0x00000000"
$
$ ./bar "$A" "$B" "$C"
got: 0x35068740 0xee07360a 0x053bd8c9 0x2895f6cd 0xb973e57e 0x4e6cfe66 0x0b60b60b 0x9a0cd056
exp: 0x35068740 0xee07360a 0x053bd8c9 0x2895f6cd 0xb973e57e 0x4e6cfe66 0x0b60b60b 0x9a0cd056 0x00000000 0x00000000
res: PASS
$

结束语:

本文给出的是串行化的大数乘法实现方法。 A * B = C设定如下:

  • 大数A对应的数组长度为M, A = {a0, a1, ..., aM};
  • 大数B对应的数组长度为N, B = {b0, b1, ..., bN};
  • A*B的结果C对应的数组长度为(M+N)。
A = { a0, a1, ..., aM };
B = { b0, b1, ..., bN }; C = A * B
= a0 * b0 + a0 * b1 + ... + a0 * bN
+ a1 * b0 + a1 * b1 + ... + a1 * bN
+ ...
+ aM * b0 + aM * b1 + ... + aM * bN a[i] * b[j] will be save to memory @ c[i+j]
i = 0, 1, ..., M;
j = 0, 1, ..., N
a[i] is unsigned int (4 bytes)
b[j] is unsigned int (4 bytes)

算法的时间复杂度为O(M*N), 空间复杂度为O(1)。 为了缩短运行时间,我们也可以采用并行化的实现方法。

  • 启动M个线程同时计算, T0 = a0 * B, T1 = a1 * B, ..., TM = aM * B;
  • 接下来,只要M个线程都把活干完了,主线程就可以对T0, T1, ..., TM进行合并。

不过,在并行化的实现方法中,对每一个线程来说,时间复杂度为O(N), 空间复杂度为O(N) (至少N+3个辅助存储空间)。因为有M个线程并行计算,于是总的空间复杂度为O(M*N)。

大数乘法的C代码实现的更多相关文章

  1. 用分治法实现大数乘法,加法,减法(java实现)

    大数乘法即多项式乘法问题,求A(x)与B(x)的乘积C(x),朴素解法的复杂度O(n^2),基本思想是把多项式A(x)与B(x)写成 A(x)=a*x^m+b B(x)=c*x^m+d 其中a,b,c ...

  2. 51 Nod 1027 大数乘法【Java大数乱搞】

    1027 大数乘法 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 给出2个大整数A,B,计算A*B的结果. Input 第1行:大数A 第2行:大数B (A,B的长度  ...

  3. 51 Nod 1028 大数乘法 V2【Java大数乱搞】

    1028 大数乘法 V2 基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 给出2个大整数A,B,计算A*B的结果. Input 第1行:大数A 第2行:大数B (A ...

  4. C++算法之大数加法计算的代码

    如下代码段是关于C++算法之大数加法计算的代码,希望对大家有用. { int length; int index; int smaller; int prefix = 0; if(NULL == sr ...

  5. 大数乘法|2012年蓝桥杯B组题解析第六题-fishers

    (9')大数乘法 对于32位字长的机器,大约超过20亿,用int类型就无法表示了,我们可以选择int64类型,但无论怎样扩展,固定的整数类型总是有表达的极限!如果对超级大整数进行精确运算呢?一个简单的 ...

  6. 51nod1057-N的阶乘(大数乘法巧解)

    这道大数乘法开始我是想套板子模拟的..然后就发现2/3的例子都wa了.(惊了).然后在思考后发现n2的板子的确过不了这么多的大数.(不看题的下场).所以,我在网上发现了分块求大数的方法.%%% 思路来 ...

  7. PAT 1023 Have Fun with Numbers[大数乘法][一般]

    1023 Have Fun with Numbers (20)(20 分) Notice that the number 123456789 is a 9-digit number consistin ...

  8. ACM学习历程—51NOD1028 大数乘法V2(FFT)

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1028 题目大意就是求两个大数的乘法. 但是用普通的大数乘法,这 ...

  9. hdu-5666 Segment(俄罗斯乘法or大数乘法取模)

    题目链接: Segment Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 65536/65536 K (Java/Others) P ...

随机推荐

  1. Angularjs 通过directive实现验证两次输入是否一致的功能

    实现效果: 1> 当输入确认密码时验证: 2> 当输入密码时验证: 实现步骤: 1.页面代码: <input class="form-control" type= ...

  2. SharePoint Adventures : Using Claims with Reporting Services

    Cannot convert claims identity to a windows token.  This may be due to user not logging in using win ...

  3. This InfoPath form template is browser-compatible, but it cannot be browser-enabled on the selected site

    - all features were running on sitecollection level and at site level But here is the solution, i do ...

  4. phonegap3.0+HTMLl5 开发 ipad app 总结

    忙碌了一段时间,终于完成了手上的这个ipad上的app ,app是用phonegap+ jquery mobile 开发的,不是用原生的objective c开发的.因为app有许多chart的渲染, ...

  5. NTP服务器配置

    #/etc/ntp.conf# driftfile /var/lib/ntp/ntp.drift logfile /var/log/ntpd.log statistics loopstats peer ...

  6. iterrows() 函数对dataframe进行遍历

    for循环遍历dataframe,返回有一个元祖类型,第一个是行的索引,第二个是series,是每一行的内容.

  7. 446. Arithmetic Slices II - Subsequence

    A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...

  8. Mybatis框架 使用接口Mapper实现数据库的crud操作

    Mybatis的Mapper接口方式实现简单crud操作: 1.创建实体类 与数据库对应 我的实体类是<Student>   package com.hxzy.mybatis.pojo; ...

  9. Hibernate 干货2

    @ORM框架 对象关系映射,用于实现面向对象编程语言里不同系统的数据之间的转换 @实例public void demo01(){  User user = new User();  user.setU ...

  10. WC2019 全国模拟赛第一场 T1 题解

    由于只会T1,没法写游记,只好来写题解了... 题目链接 题目大意 给你一个数列,每次可以任取两个不相交的区间,取一次的贡献是这两个区间里所有数的最小值,求所有取法的贡献和,对 \(10^9+7\) ...