大数乘法的C代码实现
在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代码实现的更多相关文章
- 用分治法实现大数乘法,加法,减法(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 ...
- 51 Nod 1027 大数乘法【Java大数乱搞】
1027 大数乘法 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 给出2个大整数A,B,计算A*B的结果. Input 第1行:大数A 第2行:大数B (A,B的长度 ...
- 51 Nod 1028 大数乘法 V2【Java大数乱搞】
1028 大数乘法 V2 基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 给出2个大整数A,B,计算A*B的结果. Input 第1行:大数A 第2行:大数B (A ...
- C++算法之大数加法计算的代码
如下代码段是关于C++算法之大数加法计算的代码,希望对大家有用. { int length; int index; int smaller; int prefix = 0; if(NULL == sr ...
- 大数乘法|2012年蓝桥杯B组题解析第六题-fishers
(9')大数乘法 对于32位字长的机器,大约超过20亿,用int类型就无法表示了,我们可以选择int64类型,但无论怎样扩展,固定的整数类型总是有表达的极限!如果对超级大整数进行精确运算呢?一个简单的 ...
- 51nod1057-N的阶乘(大数乘法巧解)
这道大数乘法开始我是想套板子模拟的..然后就发现2/3的例子都wa了.(惊了).然后在思考后发现n2的板子的确过不了这么多的大数.(不看题的下场).所以,我在网上发现了分块求大数的方法.%%% 思路来 ...
- PAT 1023 Have Fun with Numbers[大数乘法][一般]
1023 Have Fun with Numbers (20)(20 分) Notice that the number 123456789 is a 9-digit number consistin ...
- ACM学习历程—51NOD1028 大数乘法V2(FFT)
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1028 题目大意就是求两个大数的乘法. 但是用普通的大数乘法,这 ...
- hdu-5666 Segment(俄罗斯乘法or大数乘法取模)
题目链接: Segment Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) P ...
随机推荐
- eclipse中配置server中选择tomcat8无法进行下一步处理
在创建server的时候,选择tomcat8后,server name为空,并且无法手动输入,同时无法进行下一步操作. 解决方案如下: 1.退出eclipse. 2.找到eclipse[工作空间][当 ...
- C-sharp精华面试题
注:红色表示答案 一.选择,填空题 1. 在ADO.NET中,对于Command对象的ExecuteNonQuery()方法和ExecuteReader()方法,下面叙述错误的是(C). a) ...
- 如何为SharePoint文档库、文件夹、文件单独设置权限
在这里使用截图的方式简单描述两个问题:设置SharePoint Server文档库权限和文档库中的文件夹权限 一.设置SharePoint Server文档库权限 Figure 1 - 打开文档库后, ...
- Tempdb--TempDB Basic
1. TempDB只能运行在Simple Recovery Model下 2. 由于TempDB不需要Recovery,因此在TempDB中发生的操作不需要REDO,因此在日志记录上有别于其他数据库. ...
- 【MVC】输出HTML内容,不输出HTML标签
第一种方式: @Html.Raw("内容") 第二种方式 @(new HtmlString("<h1>abcd</h1>")) 第三种方 ...
- Java笔记(一)GC及类加载
垃圾回收器(GC) 垃圾回收器:java中有一个线程,专门负责JVM中垃圾内存的释放 垃圾:没有引用的内存节点 垃圾回收的算法 注意:垃圾回收有自己的算法,我们是不能控制垃圾回 ...
- Java50道经典习题-程序19 输入行数打印菱形图案
题目:根据用户输入的行数打印菱形图案,若用户传入的是为偶数则提示用户重新输入,例如输入数字7打印出如下菱形图案 * *** ************ ***** *** *分析:先把图形分 ...
- spring包下载方法
http://blog.csdn.net/liangtiaoxian/article/details/52780747 https://jingyan.baidu.com/article/2fb0ba ...
- Code Chef IMPO(计算几何+扫描线+积分)
题面 传送门 前置芝士 扫描线,积分求面积 题解 我怎么老是忘了积分可以求面积-- 首先,这两个投影的最小的\(x\)坐标和最大的\(x\)坐标必须相等,否则肯定无解 我们考虑一种方法,枚举\(x\) ...
- [CSS3] 各种角度的三角形绘制
#triangle-up { width:; height:; border-left: 50px solid transparent; border-right: 50px solid transp ...