大数相乘算法

关于C++中double类型到底可以表示多大的数字,按照我查询的结果是IEEE的标准可以参考。这个改日再说。但是不论值怎么大,都有一个范围。因此针对超过这个范围的值,如果需要做计算,那么就会溢出。

因此有专门的大数乘法计算逻辑,我参考了网上的一些资料,都是按照我们小学学的乘法逻辑来做的计算。 

先看看乘法逻辑:

  1820   ----------------用变量a表示

   ×     29   ----------------用变量b表示

------------------

      16380

      3460

------------------

      52780

 

为了充分体现乘法逻辑,我专门写了个较大的数字乘法。因此我直接总结出乘法逻辑:

  1. b的个位乘以a的每一位。
  2. 个位数相乘,查看是否有进位值(carry)。
  3. 十位数相乘,相乘的结果加上进位值,然后再检查是否可以向百位进位。
  4. b的十位乘以a的每一位,然后循环2-3步。

因此我们的代码如下:

 1 #include <iostream>
 2 #include <string>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <cmath>
 6 
 7 using namespace std;
 8 
 9 string multiply(string a,string b);
10 int main()
11 {
12     string x = "1820";
13     string y = "29";
14     string result = multiply(x,y);
15     cout << result << endl;
16     return 0;
17 }
18 
19 string multiply(string a,string b)
20 {
21     int a_length = a.size();
22     int b_length = b.size();
23     int *iresult = new int[a_length+b_length+1];
24     memset(iresult,0,sizeof(int)*(a_length+b_length));
25     int avalue=0;
26     int bvalue=0;
27     int carry=0;
28     int temp = 0;
29     int i,j;
30     for(j=b_length-1;j>=0;j--)
31     {
32         bvalue = b[j]-48;
33         for(i=a_length-1;i>=0;i--)
34         {
35             avalue = a[i]-48;
36             temp = avalue*bvalue+carry + iresult[i+j+2];
37             iresult[i+j+2] = temp%10;
38             carry=temp/10;
39         }
40         if(carry!=0)
41         {
42             iresult[i+j+2] = carry;
43             carry = 0;
44         }
45     }
46     if(carry!=0)
47     {
48         iresult[i+j+2] = carry;
49         carry = 0;
50     }
51 
52     string result="";
53     int pos=0;
54     for(pos=0;pos<a_length+b_length+1;pos++)
55     {
56         if(iresult[pos]!=0)break;
57     }
58 
59     for(;pos<a_length+b_length+1;pos++)
60     {
61         result = result + (char)(iresult[pos]+48);
62     }
63     return result;
64 }

 

 

代码解析:

第9行:string multiply(string a,string b); 就是大数相乘函数。因为数字太大会溢出,所以用string类型来表示数字。

第21,22 行分别算出两个数字的位数。

第23行用一个整型数组来记录大数相乘后的结果iresult。

第24行先初始化这个数组iresult,用0替换数组每个元素。注意,这个必须有。如果不是0的话,会导致第一次相乘后的结果。此处注意memset的用法。memset 是按照字节来初始化的。因此需要乘sizeof(int)。

第25,26行声明变量来表示两个大数的每一位数字。

第27行声明carry变量用来记录每一次乘法的进位值。

第28行声明temp变量来记录每一次做乘法的结果。

第29行声明i,j变量用来遍历大数的每一位。

第30行开始做乘法:

1) 这里我们需要从后向前遍历,因为个位数在最后。其他很多资料都是说,需要将大数做一个reverse,这样可以从前向后遍历。但我觉得这种方式不符合我们做乘法的习惯。

2) 我们要计算a*b,那么我这里在外循环遍历b,用b的每一位乘以a的每一位。

第32行我用方式解释:

char x = '5';

int y = x - 48。

y的结果是5。

也就是说字符'5' 和数字5 之间相差48. 这个是ASCII编码。其他数字也一样。

下面就到了关键点:

第36 行就是b的每一位数字和a的每一位数字相乘。同时加上从低位进位来的数字,并且加上上一轮相乘的结果值。

比如1820×29。 1820×9=16380. 1820×2的时候,我们不断需要记录从低位的进位值,还要考虑对应位置加上16380 对应的值。理解了这一步。后面就完事大吉了。

第37,38 行分别用除10,%10 来计算该位应该保留的值,以及进位值。

第40行,是因为存在最高位进位,但是最高位也是循环边界,所以需要单独在内循环外来单独计算。最高位进位后,需要把进位值清零。

第46行同理。

52行以后,我们从数组下标0开始,找到真正的数字最高位,最高位之前都为0,所以我们需要跳过。然后从最高位开始将这些数字填充到字符串中。至此,计算结束。

我忽然发现里面其实还有一点错误....就是数组没有释放...

posted on 2017-08-21 14:39  ^~~^  阅读(898)  评论(0编辑  收藏  举报

导航