大整数乘法
1.算法原理
如果我们想要计算两个较大的数字相乘的时候,由于计算机硬件限制可能无法计算,因此我们可以将每个乘数用加法和乘法做分解,当分解到每个因子只是一位数的时候,乘法就很简单了,这也是一种分治法。
(1)分解:首先将2个大整数a(n位)、b(m位)分解为两部分:
ah | al | bh | bl |
然后,
其中ah、al为n/2位,bh、bl为m/2位。两个大整数a、b相乘转换成了4个乘法运算,而乘数的位数变为了原来的一半。
(2)求解子问题。继续分解每个乘法运算,直到分解有一个乘数为1位数时停止分解,进行乘法运算并记录结果。
(3)合并。将计算出的结果相加并回溯,求出最终结果。
2.算法设计
(1)数据结构。将两个大数以字符串的形式输入,然后定义结构体Node,其中s[]数组用于存储大数,注意是倒序存储!(因为乘法加法运算中有可能出现进位,倒序存储时可以让进位存储在数组的末尾)。l表示长度,c表示幂次。两个大叔的初始次幂是0.如下所示:
char sa[1000];char sb[1000];typedef struct _Node{ int s[M];//数组,倒序存储大数 int l; int c;}Node, *pNode;
(2)划分函数。其中,cp()函数用于将一个n位的数分成两个n/2的数并存储,记录它的次幂和长度。
void cp(pNode src, pNode des, int st, int l){ //src表示待分解的数的结点,des表示分解后得到的数结点 //st表示从src结点数组中取数的开始位置,l表示取数长度 int i, j; for (i = st, j = 0; i<st + l; i++, j++) { des->s[j] = src->s[i];//从src节点数组中st位置开始取l个数 } des->l = l; des->c = st + src->c;//des次幂等于开始取数的位置加上src次幂。}
(3)乘法运算。定义的mul()函数用于将两个数进行相乘,不断进行分解,直到有一个乘数为1位时停止,让这两个数相乘,并记录结果回溯。
ma = pa->l/2;//表示a长度的一半mb = pb->l/2;//表示b长度的一半if(!ma || !mb)//如果!ma说明ma=0,即a的长度为1,该乘数为1位数,同理对mb也是{ if(!ma)//!ma说明a为1位数,a/b交换,保证a的长度大于b的长度 { temp=pa; pa=pb; pb=temp;//交换后b的长度为1 } ans->c = pa->c +pb->c; w=pb->s[0];//交换后b长度为1,用w记录即可 cc=0;//初始化进位为0 for(i=0;i<pa->;i++) {//把a中的数依次取出与w相乘,记录结果和进位 ans->s[i]=(w*pa->s[i]+cc)%10;//存储相乘结果的个位,十位做进位处理 cc=(w*pa->s[i]+cc)/10;//处理进位 }}if(cc) ans->s[i++]=cc;ans->l=i;
(4)合并函数
add()函数将分解得到的数进行相加合并。
void add(pNode pa, pNode pb, pNode ans){ //程序调用时把a、b的地址传递给pa、pb参数,表示待合并的两个数 //ans记录结果 int i,cc,k,alen,blen,len; int ta,tb; pNode temp; if(pa->c <pb->c)//交换保证a的次幂大 { temp=pa; pa=pb; pb=temp; } ans->c=pb->c;//结果的次幂为两个数中较小数的次幂 cc=0;//初始化进位为0 k=pa->c-pb->c;//k为a左侧需要补零的个数 alen = pa->l + pa->c;//a数加上次幂的总长度 blen = pb->l + pb->c;//b数加上次幂的总长度 if(alen>blen) len=alen; else len=blen; len=len-pb->c;//结果的长度为a/b之中的最大值减去最低次幂,最低次幂是不进行加法运算的位数 for(i=0;i<len;i++) { if(i<k) ta=0;//k为a左侧需要补零的个数,a左侧补零 else ta=pa->s[i-k];//i=k时,补零结束,从a数组中第0位开始去数字 if(i<pb->l) tb=pb->s[i];//从b数组中第0位开始取数字 else tb=0;//b数字先取完,b右侧补零 if(i>=pa->l+k)///a数字先取完,a右侧补零 ta=0; ans->s[i]=(ta+tb+cc)%10; cc=(ta+tb+cc)/10; } if(cc) ans->s[i++]=cc;//有进位,则存入数组末位 ans->l=i;}
3.完整代码
#include<stdlib.h>#include<cstring>#include<iostream>using namespace std;//定义结构体#define M 100char sa[1000];char sb[1000];typedef struct _Node{ int s[M];//数组,倒序存储大数 int l; int c;}Node, *pNode;//划分函数void cp(pNode src, pNode des, int st, int l){ //src表示待分解的数的结点,des表示分解后得到的数结点 //st表示从src结点数组中取数的开始位置,l表示取数长度 int i, j; for (i = st, j = 0; i<st + l; i++, j++) { des->s[j] = src->s[i];//从src节点数组中st位置开始取l个数 } des->l = l; des->c = st + src->c;//des次幂等于开始取数的位置加上src次幂。}//相加void add(pNode pa, pNode pb, pNode ans){ //程序调用时把a、b的地址传递给pa、pb参数,表示待合并的两个数 //ans记录结果 int i, cc, k, palen, pblen, len; int ta, tb; pNode temp; if ((pa->c)<(pb->c))//交换保证a的次幂大 { temp = pa; pa = pb; pb = temp; } ans->c = pb->c;//结果的次幂为两个数中较小数的次幂 cc = 0;//初始化进位为0 k = pa->c - pb->c;//k为a左侧需要补零的个数 palen = pa->l + pa->c;//a数加上次幂的总长度 pblen = pb->l + pb->c;//b数加上次幂的总长度 if (palen>pblen) len = palen; else len = pblen; for (i = 0; i<len - ans->c; i++) //结果的长度最长为pa,pb之中的最大长度减去最低次幂 { if (i<k) ta = 0; else ta = pa->s[i - k];//次幂高的补0,大于低的长度后与0进行计算 if (i<pb->l) tb = pb->s[i]; else tb = 0; if (i >= pa->l + k) ta = 0; ans->s[i] = (ta + tb + cc) % 10; cc = (ta + tb + cc) / 10; } if (cc) ans->s[i++] = cc; ans->l = i;}void mul(pNode pa, pNode pb, pNode ans){ int i, cc, w; int ma = pa->l >> 1, mb = pb->l >> 1; //长度除2 Node ah, al, bh, bl; Node t1, t2, t3, t4, z; pNode temp; if (!ma || !mb) //如果其中个数为1 { if (!ma) //如果a串的长度为1,pa,pb交换,pa的长度大于等于pb的长度 { temp = pa; pa = pb; pb = temp; } ans->c = pa->c + pb->c; w = pb->s[0]; cc = 0; //此时的进位为c for (i = 0; i < pa->l; i++) { ans->s[i] = (w*pa->s[i] + cc) % 10; cc = (w*pa->s[i] + cc) / 10; } if (cc) ans->s[i++] = cc; //如果到最后还有进位,则存入结果 ans->l = i; //记录结果的长度 return; } //分治的核心 cp(pa, &ah, ma, pa->l - ma); //先分成4部分al,ah,bl,bh cp(pa, &al, 0, ma); cp(pb, &bh, mb, pb->l - mb); cp(pb, &bl, 0, mb); mul(&ah, &bh, &t1); //分成4部分相乘 mul(&ah, &bl, &t2); mul(&al, &bh, &t3); mul(&al, &bl, &t4); add(&t3, &t4, ans); add(&t2, ans, &z); add(&t1, &z, ans);}int main(){ Node ans, a, b; cout << "输入大整数 a:" << endl; cin >> sa; cout << "输入大整数 b:" << endl; cin >> sb; a.l = strlen(sa);//sa,sb以字符串进行处理 b.l = strlen(sb); int z = 0, i; for (i = a.l - 1; i >= 0; i--) a.s[z++] = sa[i] - '0'; //倒向存储 a.c = 0; z = 0; for (i = b.l - 1; i >= 0; i--) b.s[z++] = sb[i] - '0'; b.c = 0; mul(&a, &b, &ans); cout << "最终结果为:"; for (i = ans.l - 1; i >= 0; i--) cout << ans.s[i]; //ans用来存储结果,倒向存储 cout << endl; system("pause"); return 0;}
4.复杂度分析
递推可得,T(n)=4x(Tn/2x)+(2x-1)O(n)。地推最终规模为1,令n=2x,则x=logn,那么有:
T(n)=n2