(2)大整数乘法
一般我们用int、long来定义整数,但是超过了这些范围的大整数如“123456789”我们怎么表示呢,大整数的加减乘除怎么又如何计算呢?
两个n位整数a,b相乘,一般有一下几种方法:
1.用小学的竖式运算,要用到两层for循环,时间复杂度是O(n^2)
2.用分治法,把整数拆分成两部分,一般是对称拆分
a = a1* 10^(n/2)+a0 b = b1* 10^(n/2)+b0
a*b = a1*b1*10^n +(a1*b0*+a0*b1)*10^(n/2)+a0*b0
拆分后需要对n/2位数做四次乘法运算,时间复杂度可表示为
n =1 T(n) = O(1)
n>1 T(n) = 4T(n/2) +O(n)
计算以后时间复杂度还是O(n^2)
3.还需要减少乘法的次数
(1) a1*b0*+a0*b1 = (a1+a0)*(b1+b0)-a1*b1-a0*b0
(2) a1*b0*+a0*b1 = (a1-a0)*(b0-b1)+a1*b1+a0*b0
简化后需要对n/2位数做三次乘法运算,时间复杂度可表示为
n =1 T(n) = O(1)
n>1 T(n) = 3T(n/2) +O(n)
计算以后时间复杂度是O(n^log3)=O(n^1.59),(1)中两数相加可能会得到m+1位的结果,增加了复杂度,所以选第二种方案。
具体算法:
1.大整数可以用字符数组、int数组以及字符串来表示;
2.对于位数不相同的大整数乘法,用上面的第2种方法比较简单,a*b = a1*b1*10^n +(a1*b0*+a0*b1)*10^(n/2)+a0*b0,其中的几个乘法又可以考虑用拆分的方法递归求解;
3.计算乘积的递归函数终止条件是两个乘数的位数都为1,这时可直接返回相乘结果;
string mutiply(string& a, string& b)
{
int num1 = atol(a.c_str());
int num2 = atol(b.c_str());
stringstream ss;
string s;
ss << num1*num2;
ss >> s;
return s;
}
if(a.length() ==1 || b.length() == 1)
return mutiply(a, b);
4.如果没有达到递归的终止条件,就继续拆分算出各部分乘积再相加
string bigInt_mutiply(string& a, string& b)
{
if(a.length() ==1 || b.length() == 1)
return mutiply(a, b);
else{
string a1 = a.substr(0, a.length()/2);
string a0 = a.substr(a1.length());
string b1 = b.substr(0, b.length()/2);
string b0 = b.substr(b1.length());
string c0 = bigInt_mutiply(a0, b0);
string c1 = bigInt_mutiply(a1, b0);
string c2 = bigInt_mutiply(a0, b1);
string c3 = bigInt_mutiply(a1, b1);
c3 = add_zero(c3, a0.length()+b0.length());
c2 = add_zero(c2, b0.length());
c1 = add_zero(c1, a0.length());
add(c3, c2);
add(c3, c1);
add(c3, c0);
return c3;
}
}
//返回两个数相加的字符串结果
string add(string& a, string& b)
{
int len1 = a.length(), len2 = b.length(), i, j;
//a>=b
if(len1 < len2){
string t = a;
a = b;
b = t;
int t0 = len1;
len1 = len2;
len2 = t0;
}
for(i = len1-1, j = len2-1;i >= 0, j >= 0; i--, j--){
int t = a[i] - '0' + b[j] - '0' ;
if(t >= 10){
a[i] = t - 10 + '0';
if(i > 0){
a[i-1] = a[i-1] + 1;
}else{
a = '1' + a;
}
}else{
a[i] = t + '0';
}
}
if(i >= 0){
for(j = i; j>= 0; j--){
if(a[j] >= 58){
a[j] = a[j] - 10;
if(j > 0){
a[j-1] = a[j-1] + 1;
}else{
a = '1' + a;
}
}
}
}
return a;
}
//在字符串的右边加0
string add_zero(string& a, int len)
{
for(int i = 0; i < len; i++)
a = a + '0';
return a;
}
完整程序:
#include <cstdlib>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
//返回两个数相乘的字符串结果
string mutiply(string& a, string& b)
{
int num1 = atol(a.c_str());
int num2 = atol(b.c_str());
stringstream ss;
string s;
ss << num1*num2;
ss >> s;
return s;
}
//返回两个数相加的字符串结果
string add(string& a, string& b)
{
int len1 = a.length(), len2 = b.length(), i, j;
//a>=b
if(len1 < len2){
string t = a;
a = b;
b = t;
int t0 = len1;
len1 = len2;
len2 = t0;
}
for(i = len1-1, j = len2-1;i >= 0, j >= 0; i--, j--){
int t = a[i] - '0' + b[j] - '0' ;
if(t >= 10){
a[i] = t - 10 + '0';
if(i > 0){
a[i-1] = a[i-1] + 1;
}else{
a = '1' + a;
}
}else{
a[i] = t + '0';
}
}
if(i >= 0){
for(j = i; j>= 0; j--){
if(a[j] >= 58){
a[j] = a[j] - 10;
if(j > 0){
a[j-1] = a[j-1] + 1;
}else{
a = '1' + a;
}
}
}
}
return a;
}
//在字符串的右边加0
string add_zero(string& a, int len)
{
for(int i = 0; i < len; i++)
a = a + '0';
return a;
}
/*递归计算大整数乘积
a = a1* 10^a0.length()+a0
b = b1* 10^b0.length()+b0
a*b = a1*b1*10^(a0.length()+b0.length())
+a1*b0*10^a0.length()+a0*b1*10^b0.length()+a0*b0
*/
string bigInt_mutiply(string& a, string& b)
{
if(a.length() ==1 || b.length() == 1)
return mutiply(a, b);
else{
string a1 = a.substr(0, a.length()/2);
string a0 = a.substr(a1.length());
string b1 = b.substr(0, b.length()/2);
string b0 = b.substr(b1.length());
string c0 = bigInt_mutiply(a0, b0);
string c1 = bigInt_mutiply(a1, b0);
string c2 = bigInt_mutiply(a0, b1);
string c3 = bigInt_mutiply(a1, b1);
c3 = add_zero(c3, a0.length()+b0.length());
c2 = add_zero(c2, b0.length());
c1 = add_zero(c1, a0.length());
add(c3, c2);
add(c3, c1);
add(c3, c0);
return c3;
}
}
int main()
{
string a, b;
while(cin>>a>>b){
cout<<bigInt_mutiply(a, b)<<endl;
}
system("PAUSE");
return 0;
}