1.题目
? ? ? ?编写两个任意位数的大数相乘的程序,给出计算结果。
2.题目分析
? ? ? ?该题相继被ACM、华为、腾讯等选作笔试、面试题,若无准备要写出这种程序,还是要花一定的时间的。故,觉得有必要深入研究一下。搜索了网上的大多数该类程序和算法,发现,大数乘法主要有模拟手工计算的普通大数乘法,分治算法和FFT算法。其中普通大数乘法占据了90%以上,其优点是空间复杂度低,实现简单,时间复杂度为O(N2),分治算法虽然时间复杂度降低为,??
? ? ? ?但其实现需要配 合字符串模拟加减法操作,实现较为复杂,
? ??参考博客1http://cnn237111.blog.51cto.com/2359144/1201901?
? ? FFT算法则更为复杂,较少适用,有兴趣
? ??参考博客2?http://blog.csdn.net/hondely/article/details/6938497
? ? ? ??和博客3http://blog.csdn.net/jackyguo1992/article/details/12613287。
??????? 普通大数乘法算法,主要有逐位相乘处理进位法、移位进位法,下面对其进行介绍并优化。
3.题目解答
3.1 逐位相乘处理进位法
? ? ? ? 参考博客4的思路
? ? ??? 乘积是逐位相乘,也就是aibj,结果加入到积C的第i+j位,最后处理进位即可,例如:A =17 = 1*10 + 7 = (7,1)最后是十进制的幂表示法,幂次是从低位到高位,以下同。B=25 = 2*10 + 5 = (5,2);C?=?A?*?B?=?(7?*?5,?1?*?5?+?2?*?7,?1?*?2)?=?(35,?19,?2)?=?(5,?22,?2.?4)=425。
原博客的思路为: (1)转换并反转,字符串转换为数字并将字序反转;
(2)逐位相乘,结果存放在result_num[i+j]中;
(3)处理进位,消除多余的0;
(4)转换并反转,将计算结果转换为字符串并反转。
? ? ?原博客中采用指针参数传递,字符串长度有限制,改为通过string传参数,按原思路编程如下:
头文件和数据结构:
- #include?<iostream>??
- #include?<string>??
- #include?<vector>??
- #include?<stdlib.h>??
- using?namespace?std;??
- struct?bigcheng??
- {??
- ????vector<int>?a;??
- ????vector<int>?b;??
- ????string?result_str;??
- };??
- void?chartonum(string?a,string?b,bigcheng?&tempcheng);??
- void?multiply(bigcheng?&tempchengh,vector<int>?&result_num);??
- void?numtochar(bigcheng?&tempcheng,0); background-color:inherit">//将计算结果转换为字符串并反转??
(1)转换并反转,字符串转换为数字并将字序反转;
{??
- ????int?size_a=a.size();??
- ????int?size_b=b.size();??
- ????for?(int?i=size_a-1;i>=0;i--)??
- ????{??
- ????????tempcheng.a.push_back(a[i]-'0');??
- ????}??
- int?i=size_b-1;i>=0;i--)??
- ????????tempcheng.b.push_back(b[i]-'0');??
- }??
(3)处理进位,消除多余的0;代码为:
void?multiply(bigcheng?&tempcheng,87); font-weight:bold; background-color:inherit">int>?&result_num)??
- for?(unsigned?int?i=0;i<tempcheng.a.size();i++)??
- ????????int?j=0;j<tempcheng.b.size();j++)??
- ????????{??
- ????????????result_num[i+j]+=(tempcheng.a[i])*(tempcheng.b[j]);??
- ????????}??
- ????}??
- ????int?i=result_num.size()-1;i>=0;i--)??
- ????{??
- ????????if?(result_num[i]!=0)??
- ????????{??
- ????????????break;??
- ????????}??
- else??
- ????????????result_num.pop_back();??
- int?c=0;??
- int?i=0;i<result_num.size();i++)??
- ????????result_num[i]+=c;??
- ????????c=result_num[i]/10;??
- ????????result_num[i]=result_num[i]%10;??
- if?(c!=0)??
- ????????result_num.push_back(c);??
- }??
(4)转换并反转,将计算结果转换为字符串并反转。
{???int?size=result_num.size();??
- int?i=0;i<result_num.size();i++)??
- ????????tempcheng.result_str.push_back(char(result_num[size-1-i]+'0'));??
-
主函数为:
int?main()??
- ???????bigcheng?tempcheng;??
- ????string?a,b;??
- ????cin>>a>>b;??
- ????chartonum(a,b,tempcheng);??
- int>?resultnum(a.size()+b.size(),0);??
- ????multiply(tempcheng,resultnum);??
- ????numtochar(tempcheng,resultnum);??
- ????cout<<tempcheng.result_str<<endl;??
- ????system("pause");??
- return?0;??
- }??
?????上面的思路还是很清晰的,但代码有些过长,考虑优化如下:
(1)上述思路是先转换反转,其实无需先将全部字符串转换为数字的,可即用即转,节约空间;
(2)无需等到逐位相乘都结束,才处理进位,可即乘即进;
(3)无需等到所有结果出来后,将结果转换为字符,可即乘即转。
? ? ?优化后时间复杂度不变,但节省了空间,代码更简洁。如下:
#include?<assert.h>??
- namespace?std;??
- struct?bigcheng2??
- ????string?a;??
- ????string?b;??
- ????string?result_str;??
- };??
- void?reverse_data(?string?&data);??
- void?multiply2(bigcheng2?&tempcheng2);??
字符串反转:
void?reverse_data(?string?&data)??
- char?temp?=?'0';??
- int?start=0;??
- int?end=data.size()-1;??
- ????assert(?data.size()&&?start?<=?end?);??
- while?(?start?<?end?)??
- ????????temp?=?data[start];??
- ????????data[start++]?=?data[end];??
- ????????data[end--]?=?temp;??
-
两数相乘:
void ?multiply2(bigcheng2?&tempcheng2)??
- ????reverse_data(tempcheng2.a);
- ????string?temp(tempcheng2.a.size()+tempcheng2.b.size(),'0');??
- int?i=0;i<tempcheng2.a.size();i++)??
- ????????unsigned?int?j;??
- for?(j=0;j<tempcheng2.b.size();j++)??
- ????????????c+=temp[i+j]-'0'+(tempcheng2.a[i]-'0')*(tempcheng2.b[j]-'0');??
- ????????????temp[i+j]=(c%10)+'0';??
- ????????????c=c/10;??
- while(c)??
- ????????????temp[i+j++]+=c%10;??
- ????????????c=c/10;??
- int?i=temp.size()-1;i>=0;i--)??
- if?(temp[i]!='0')??
- ????????????break;??
- ????????????temp.pop_back();??
- ????reverse_data(temp);??
- ????tempcheng2.result_str=temp;??
- 主函数:
???????bigcheng2?tempcheng2;??
- ???????string?a,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> ???????cin>>a>>b;??
- ???????tempcheng2.a=a;??
- ???????tempcheng2.b=b;??
- ???????multiply2(tempcheng2);??
- ???????cout<<tempcheng2.result_str<<endl;??
- ???????system("pause");??
- ???????return?0;??
- }??
3.2 移位进位法
? ? ? ?移位进位法也是普通的大数相乘算法,其时间复杂度也为O(N2)其基本思路参考博客5,简述如下:
按照乘法的计算过程来模拟计算:
? 1 2
× 3 6
---------- ---- 其中,上标数字为进位数值。
71?2 ?--- 在这个计算过程中,2×6=12。本位保留2,进位为1.这里是一个简单的计算过程,如果在高位也需要进位的情况下,如何处理?
3 6
? -----------
413 ?2
? ? ? ? 其代码优化如下:
void ?reverse_data(?string?&data);?compute_value(?string?lhs,string?rhs,string?&result?);??
}??
????reverse_data(lhs);??
????reverse_data(rhs);??
int?i?=?0,?j?=?0,?res_i?=?0;??
int?tmp_i?=?0;??
int?carry?=?0;??
??
for?(?i?=?0;?i!=lhs.size();?++i,?++tmp_i?)??
????????res_i?=?tmp_i;????
for?(?j?=?0;?j!=?rhs.size();?++j?)??
????????????carry?+=?(?result[res_i]?-?'0'?)+(lhs[i]?-?'0')?*?(rhs[j]?-?'0');??
????????????result[res_i++]?=?(?carry?%?10?+?'0'?);??
????????????carry?/=?10;??
while?(carry)??
????????????result[res_i++]?=?(carry?%?10?+?'0');??
????????????carry?/=?10;??
int?i=result.size()-1;i>=0;i--)??
if?(result[i]!='0')??
else??
????????????result.pop_back();??
????????reverse_data(result);??
int?main()??
????string?result(a.size()+b.size(),'0');??
????compute_value(a,result);??
????cout<<result<<endl;??
3.3大数相乘优化
3.2 移位进位法中的反转字符串其实不必要的,只需从数组的后面开始计算存储即可,下面实现代码:
char*?bigcheng1(char?*p1,char?*p2)??
- {??
- if?(check(p1)||check(p2))??
- throw?exception("Invalid?input!");??
- int?index1=strlen(p1)-1,index2=strlen(p2)-1,index3,carry=0;??
- char?*p3=new?char[index1+index2+3];??
- ????memset(p3,'0',index1+index2+2);??
- ????p3[index1+index2+2]=' ';??
- for?(;index2>=0;--index2)??
- ????{??
- for?(index1=strlen(p1)-1;index1>=0;--index1)??
- ????????{??
- ????????????int?num=p3[index1+index2+1]-'0'+(p1[index1]-'0')*(p2[index2]-'0')+carry;??
- ????????????p3[index1+index2+1]=num%10+'0';??
- ????????????carry=num/10;??
- ????????}??
- ????????int?i=0;??
- while(carry)??
- ????????{??
- ????????????p3[index1+index2+1+(i--)]+=(carry%10);??
- ????????????carry/=10;??
- ????????index3=index1+index2+1+i;??
- ????}??
- while(index3>=0)??
- ????????p3[index3--]='0';??
- return?p3;??
- }??
或者下面这样,其实是一样的,下面的相加、相减、相除一样的思路啦
char*?bigcheng(char?*p1,87); font-weight:bold; background-color:inherit">char?*p2)??
if?(check(p1)||check(p2))??
throw?exception("Invalid?input!");??
int?index1=strlen(p1)-1,carry=0;??
char?*p3=new?char[index1+index2+3];??
????p3[index1+index2+2]=' ';??
for?(;index2>=0;--index2)??
for?(index1=strlen(p1)-1;index1>=0;--index1)??
int?num=p3[index1+index2+1]-'0'+(p1[index1]-'0')*(p2[index2]-'0')+carry;
if?(num>=10)??
????????????{??
????????????????carry=num/10;??
????????????????num%=10;??
????????????}??
else?carry=0;??
????????????p3[index1+index2+1]=num+'0';??
????????int?i=0;??
while(carry)??
????????????p3[index1+index2+1+(i--)]+=(carry%10);??
????????????carry/=10;??
????????index3=index1+index2+1+i;??
while(index3>=0)??
????????p3[index3--]='0';??
return?p3;??
}??
3.4运行结果
? ? ? ? 运行结果如图1、图2所示
? ?
??????????????????? 图1 ? ?
?
????????????????????????????????????????????????????????????????????图2
3.5 大数相加
check合法性校验,校验字符串中是否有非数字。
bool?check(char?*p)??
- if?(!p)??
- return?1;??
- int?i=0;??
- while(p[i]!=' ')???
- ????{??
- if?(p[i]<'0'||p[i]>'9')??
- return?1;??
- ????????}??
- else?++i;??
- ????}??
- return?0;??
- }??
char*?bigadd(char?*p2)??
- if?(check(p1)||check(p2))??
- throw?exception("Invalid?input!");??
- int?len1=strlen(p1);??
- int?len2=strlen(p2);??
- int?len3=max(len1,len2)+1;??
- char[len3+1];??
- ????p3[len3]=' ';??
- int?index1=len1-1,index2=len2-1,index3=len3-1;??
- int?carry=0;??
- while(index1>=0&&index2>=0)??
- int?num=p1[index1--]-'0'+p2[index2--]-'0'+carry;??
- if?(num>=10)??
- ????????????carry=1;??
- ????????????num-=10;??
- else??
- ????????????carry=0;??
- ????????p3[index3--]=num+'0';??
- while(index1>=0)??
- int?num=p1[index1--]-'0'+carry;??
- while(index2>=0)??
- int?num=p1[index2--]-'0'+carry;??
- ????p3[index3]=carry?'1':'0';??
- return?p3;??
- }??
3.6大数相减
char*?bigminus(char?*p2,87); font-weight:bold; background-color:inherit">bool?&flag)??
- ????flag=0;??
- if?(strlen(p1)<strlen(p2))??
- ????????flag=1;??
- char?*tmp=p1;??
- ??????????????p1=p2;??
- ??????????????p2=tmp;??
- else?if?(strlen(p1)==strlen(p2))??
- if?(strcmp(p1,p2)<0)??
- ????????????flag=1;??
- ????????????char?*tmp=p1;??
- ????????????p1=p2;??
- ????????????p2=tmp;??
- char[strlen(p1)+2];??
- ????p3[index3+1]=' ';??
- int?carry=0;??
- while(index1>=0&&index2>=0)??
- int?num=p1[index1--]-p2[index2--]-carry;??
- if?(num<0)??
- ????????????carry=1;??
- ????????????num+=10;??
- else?carry=0;??
- int?num=p1[index1--]-'0'-carry;??
- if?(num<0)??
- ????????????num+=10;??
- else?carry=0;??
- ????????p3[index3--]=num+'0'?;??
- int?i=0;??
- while(p3[i]=='0')?++i;??
- if?(flag)??
- ??????????p3[i-1]='-';??
- 3.7大数相除
char*?bigchu(char?*p2)??
- bool?flag=0;??
- char?*tmp1=char[strlen(p2)-strlen(p2)+1];??
- char?*tmp0=tmp1,*p3,*p4;??
- ????memset(tmp1,strlen(p2)-strlen(p2));??
- ????tmp1[strlen(p2)-strlen(p2)]=' ';??
- char?*tmp2=bigminus(p1,p2,flag);??
- ??????????p1=tmp2;???
- while(!flag)??
- ????{???
- ??
- ????????p3=bigadd(tmp0,"1");??
- ????????tmp1=tmp0;??
- ????????tmp0=p3;??????????
- delete?[]tmp1;??
- ??
- ????????tmp2=bigminus(p1,flag);??
- ????????p4=p1;??
- ????????p1=tmp2;???
- delete?[]p4;??
- return?tmp0;??
- ??????
- }??
3.8主函数测试
int?_tmain(int?argc,?_TCHAR*?argv[])??
- ????string?a,b;??
- while(1)??
- ????????cin>>a>>b;??
- char?*p1=const_cast<char?*>(a.c_str());??
- char?*p2=char?*>(b.c_str());??
- char?*p3=bigadd(p1,p2);??
- char?*p4=bigminus(p1,flag);??
- char?*p5=bigcheng1(p1,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> ??????????
- //cout<<p3<<endl<<endl<<p4<<endl<<endl<<p5<<endl<<endl;??
- ????????cout<<p1<<endl<<"bigadd"<<endl<<p2<<endl<<"equal:?";??
- ????????printnum(p3);??
- ????????cout<<endl;??
- ????????cout<<p1<<endl<<"bigminus"<<endl<<p2<<endl<<"equal:?";??
- ????????printnum(p4);??
- ????????cout<<endl;??
- ????????cout<<p1<<endl<<"bigcheng1"<<endl<<p2<<endl<<"equal:?";??
- ????????printnum(p5);??
- ????system("pause");??
- return?0;??
- }??
测试结果如下:
欢迎各位交流批评指正^_^····
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|