大整数乘法——分治

分治——大整数的乘法

话不多说进入正题:
两个整数 NUM1*NUM2,由于整数范围的限制,数值太大的话在NUM1 NUM2或者结果的表示时会产生溢出,因此我们用字符串来进行运算:
基本处理

  • 用字符串存储NUM1 NUM2
  • 所有数字字符串都在第一位带上符号’+’ / ‘-’ (为了处理的方便)

算法思想:
1.把原问题划分为规模大致相同的子问题:

  • 取n为字符串NUM1,NUM2两者中较短的长度值,
  • NUM1前L1-n/2为A,后n/2为B
    NUM2前L2-n/2为C,后n/2为D
  • NUM1xNUNM2= (A *10(n/2)+B) ( C *10(n/2)+D)= AC *10n+(AD+BC) *10(n/2)+BD=AC *10n+((A-B)(D-C) + AC +BD) *10(n/2) +BD
  • 最终分解为规模较小的相同类型子问题:AC BD (A-B)(D-C) 三次乘法运算

2.合并子问题的解得出原问题的解

  • ① AC *10n ② BD ③ (A-B)(C-D)*10n三个结果相加

3.基本子算法:

  • 当两个乘数的规模在10^3数量级的时候,直接用整数相乘得到结果
  • 协助算法:求两个字符串数的和、差

代码:

//求两个包含符号在内的字符串数的乘积(两数的数量级大致相同)
string LargeIntegerMulti( string s1, string s2 ){
    char sign='+';
    if(s1[0]!=s2[0])
        sign='+';
    int l1=s1.length(), l2=s2.length();
    if(l1<=3||l2<=3){
        stringstream ss;
        ss<<s1<<" "<<s2;
        int num1,num2;
        ss>>num1>>num2;
        int result=num1*num2;
        ss.clear();
        ss<<result;
        string sresult;
        ss>>sresult;
        if(result>0)
            sresult="+"+sresult;
        return sresult;
    }

    int n=l1>l2?(l2-1):(l1-1);
    n=n/2;
    string A(s1,1,l1-n-1), B(s1,l1-n,n),C(s2,1,l2-n-1),D(s2,l2-n,n);
    string A_B=Plus("+"+A,"-"+B),
           D_C=Plus("+"+D,"-"+C);
    string AC=LargeIntegerMulti("+"+A,"+"+C),
           BD=LargeIntegerMulti("+"+B,"+"+D),
           A_BD_C=LargeIntegerMulti(A_B,D_C);

    A_BD_C=Plus(Plus(AC,BD),A_BD_C);
    AC+=string(2*n,'0');
    A_BD_C+=string(n,'0');

   string result=Plus(Plus(AC,BD),A_BD_C);
   if(result[0]!=sign) result[0]='-';
   return result;
}
 
  • 协助算法
//求两个带符号的字符串数的和
char temp[100];
string Plus(string s1,string s2){

	char sign1=s1[0],sign2=s2[0],rsign,c1,c2,c;
	string str1(s1.rbegin(),s1.rend()),str2(s2.rbegin(),s2.rend());
	int index=0,l1=s1.length(),l2=s2.length(),carry=0;
	//减法
	if(sign1!=sign2){
		if(Greater(string(s1,1,l1-1),string(s2,1,l2-1))){
			rsign=(sign1=='+'?'+':'-');
		}
		else{
			rsign=(sign2=='+'?'+':'-');
			swap(str1,str2);
		}

		while(index<str1.length()-1){
			c1=str1[index];
			if(index<str2.length()-1)
				c2=str2[index];
			else
				c2='0';
			c=c1-c2-carry+'0';
			if(c<'0'){
				c+=10;
				carry=1;
			}
			else
				carry=0;
			temp[index++]=c;
		}

	}
	else{//加法
	   rsign=(sign1=='+'?'+':'-');
	   if(!Greater(string(s1,1,l1-1),string(s2,1,l2-1)))
	   		swap(str1,str2);
	   	while(index<str1.length()-1){
	   		c1=str1[index];
	   		if(index<str2.length()-1)
	   			c2=str2[index];
	   		else
	   			c2='0';
	   		c=c1+c2+carry-'0';
	   		if(c>'9'){
	   			c-=10;carry=1;
			}
			else
				carry=0;
			temp[index++]=c;
		}
		if(carry!=0)
			temp[index++]='1';

	}
	string s(temp,temp+index);
	return rsign+string(s.rbegin(),s.rend())	;

}
  • 测试(主函数)
int main()
{
   string s1,s2;
   cin>>s1>>s2;
   if(s1[0]!='-')s1="+"+s1;
   if(s2[0]!='-')s2="+"+s2;
   stringstream ss;
   string result=LargeIntegerMulti(s1,s2);
   if(result[0]=='+')
        result.erase(0,1);
   cout<<result;
   return 0;
}

Note
1.在字符串和整数的转换中巧妙地用到了字符串流,但是要注意多次使用的时候,要在使用前clear()
2.注意在每次运算里面字符串都带有符号,每次运算的结果也不要丢了正数的正号
3.字符串的加法运算不能自作聪明地转化为整数进行运算,因为当乘法运算的结果比较大的时候,整数会溢出,必须包装字符串加减法的函数。
4.这个算法在两个乘数都比较大的时候会更有效率,当只有一个乘数较大的时候,建议包装一个类似于字符串数加法的函数,从低位到高位进行运算

发布了27 篇原创文章 · 获赞 1 · 访问量 1297

猜你喜欢

转载自blog.csdn.net/qq_43496675/article/details/104576434