[算法]大数相乘

题目:有两个用字符串表示的非常大的大整数,算出他们的乘积,也是用字符串表示。不能用系统自带的大整数类型。
大数的长度:m,n

1. 优点:最容易理解,符合草稿纸的写法;  
缺点:空间复杂度不是 m+n

摘自牛客网 https://www.nowcoder.com/test/question/done?tid=10559365&qid=111150#summary
超蓝の悟空的代码

//AC代码:
#include<stdio.h>
#include<string>
#include<iostream>
using namespace std;
const int L=11000; 
string mul(string,string);
int main(){
     string x,y;
     while (cin>>x>>y)
         cout<<mul(x,y)<<endl;
}
string mul(string a,string b) { 
     string s; 
     int na[L],nb[L],nc[L],La=a.size(),Lb=b.size(),i,j;
     fill(na,na+L,0);fill(nb,nb+L,0);fill(nc,nc+L,0);
     for (i=La-1;i>=0;i--) na[La-i]=a[i]- '0' ;
     for (i=Lb-1;i>=0;i--) nb[Lb-i]=b[i]- '0'
     for (i=1;i<=La;i++) 
         for (j=1;j<=Lb;j++) 
         nc[i+j-1]+=na[i]*nb[j];
     for (i=1;i<=La+Lb;i++) 
         nc[i+1]+=nc[i]/10,nc[i]%=10;
     if (nc[La+Lb]) s+=nc[La+Lb]+ '0' ;
     for (i=La+Lb-1;i>=1;i--) 
         s+=nc[i]+ '0' ;
     return s; 
}

2. 摘自 Jason Yang -- 博客园
http://www.cnblogs.com/jason-yang/archive/2012/04/26/2472755.html

优点:空间复杂度 m+n

由于数字无法用一个整形变量存储,很自然的想到用字符串来表示一串数字。

然后按照乘法的运算规则,用一个乘数的每一位乘以另一个乘数,

然后将所有中间结果按正确位置相加得到最终结果。可以分析得出如果乘数为A和B,

A的位数为m,B的位数为n,则乘积结果为m+n-1位(最高位无进位)或m+n位(最高位有进位)。

因此可以分配一个m+n的辅存来存储最终结果。

为了节约空间,所有的中间结果直接在m+n的辅存上进行累加。

最后为了更符合我们的乘法运算逻辑,可以讲数字逆序存储,这样数字的低位就在数组的低下标位置,进行累加时确定下标位置较容易些。

下面是我的解法。

首先是对数组逆序的函数:

复制代码
 1 void reverseOrder(char* str, int p, int q)
 2 {
 3     char temp;
 4     while(p < q)
 5     {
 6         temp = str[p];
 7         str[p] = str[q];
 8         str[q] = temp;
 9         p ++;
10         q --;
11     }
12 }
复制代码

然后是完成大数相乘的函数:

复制代码
 1 char* multiLargeNum(char* A, char* B)
 2 {
 3     int m = strlen(A);
 4     int n = strlen(B);
 5     char* result = new char[m+n+1];
 6     memset(result, '0', m+n);
 7     result[m+n] = '\0';
 8     reverseOrder(A, 0, m-1);
 9     reverseOrder(B, 0, n-1);
10 
11     int multiFlag; // 乘积进位
12     int addFlag;   // 加法进位
 
   
    // 试一遍 789*56 
13     for(int i=0; i <= n-1; i++) // B的每一位, 一共(n-1)轮
14     {
15         multiFlag = 0; // 在每一轮的初始,加法进位 乘法进位 均为0
16         addFlag = 0;
17         for(int j=0; j <= m-1; j++) // A的每一位
18         {
19             // '0' - 48 = 0
20             int temp1 = (A[j] - 48) * (B[i] - 48) + multiFlag;
21             multiFlag = temp1 / 10; // 如 6*8+5=53 产生进位5
22             temp1 = temp1 % 10;

23             int temp2 = (result[i+j] - 48) + temp1 + addFlag;
24             addFlag = temp2 / 10; 
25             result[i+j] = temp2 % 10 + 48; 
26         }
27         result[i + m] += multiFlag + addFlag; // 第i轮,最高数位是result[i+m]
28     }

29     reverseOrder(result, 0, m+n-1); // 逆序回来
30 
31     return result;
32 }
复制代码

最后是测试程序:

复制代码
 1 int main()
 2 {
 3     char A[] = "962346239843253528686293234124";
 4     char B[] = "93459382645998213649236498";
 5     char *res = multiLargeNum(A, B);
 6     if(res[0] != 48)
 7         printf("%c", res[0]);
 8     printf("%s", res+1);
 9     delete [] res;
10     return 0;
11 }
复制代码

时间复杂度分析:

3个逆序操作的时间分别为O(n)、O(m)、O(m+n),双重循环的时间复杂度为O(mn),

则总的时间复杂度为O(mn + (m+n)),通常m+n << mn,因此可近似认为为O(mn)。

而且,逆序操作只是为了思考更容易,完全可以去掉。

空间复杂度为O(m+n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值