在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. Intellij IDEA如何在一个窗口同时打开多个Maven项目

    建立父目录,比如fatherProject,并将多个项目放入该父目录fatherProject下 File-Open...打开父目录fatherProject 引入pom.xml,打开Maven Pr ...

  2. s21day25 python笔记

    s21day25 python笔记 正则表达式 1.定义 定义:正则表达式是一种规则匹配字符串的规则 re模块本身只是用来操作正则表达式的,和正则本身没关系 为什么要有正则表达式? 匹配字符串 一个人 ...

  3. How to extract msu/msp/msi/exe files from the command line

    http://www.windowswiki.info/2009/02/19/how-to-extract-msumspmsiexe-files-from-the-command-line/ Micr ...

  4. [JAVA] Tcp客户端和服务器简单代码

    服务器: import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; im ...

  5. Java ActiveMQ 讲解(一)理解JMS 和 ActiveMQ基本使用

    最近的项目中用到了mq,之前自己一直在码农一样的照葫芦画瓢.最近几天研究了下,把自己所有看下来的文档和了解总结一下. 一. 认识JMS 1.概述 对于JMS,百度百科,是这样介绍的:JMS即Java消 ...

  6. 窗口间传送数据wsprintf,WM_SETTEXT,SendMessage的理解

    对wsprintf  API函数的理解: int wsprintf ( LPTSTR lpOut, // pointer to buffer for output  LPCTSTR lpFmt, // ...

  7. window下使用MyCat实现简单的读写分离

    参考文档 MyCat权威指南 MyCat项目主页 学会数据库读写分离.分表分库--用Mycat,这一篇就够了! MyCat安装 Java SDK下载(必须JDK7或更高版本) MYSQL下载 (MyC ...

  8. css细节复习笔记——浮动

    CSS除了能够改变字体.背景和所有其他属性,还能够完成基本布局任务. div+css通过浮动和定位.盒子模型等技术应用,是最常用的布局方式. 定位的基本思想很简单,它允许定义元素框相对于其正常位置应该 ...

  9. java—在dbutils中处理事务与不确定条件的查询(46)

    在dbutils中处理事务        事务是指用户的一次操作.这一次操作有可能是一个表,也有可能是多个表,也有可能是对一个表的多次操作. 只要是: 1:对数据数据库进行多次操作. 2:多个表,还是 ...

  10. “全栈2019”Java第八十七章:类中嵌套接口的应用场景(拔高题)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...