转自:https://blog.csdn.net/shayuanshayuan_/article/details/75676127
在很多情况下,c/c++所提供的基本数据类型已经不能满足我们的需求了,所以我们需要一种方法来解决一些大数的运算,在小学进行加法运算的时候,无论数据是什么,有多少位,都通通采取列竖式的方法进行计算并得出结果,本文将使用数组模拟列竖式计算来解决大数的加法运算。
首先我们先给定一组数据,来辅助说明整个计算过程
a = 987654321
b = 56789
计算 a + b 的结果
将两个数字的每一位分别存入两个数组:因为所给定的数据不是很大,这里就开辟长度为10的数组进行说明,a数组(a[])用来存储数字a的每一位,b数组(b[])用来存储数字b的每一位,c数组(c[])用来存储计算的结果(在程序中开辟一个新的数组,数组的每一位都是随机数,所以需要将数组初始化,把数组的每一位都赋值为0)。
数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
b[] | 5 | 6 | 7 | 8 | 9 | 0 | 0 | 0 | 0 | 0 |
c[] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
我们通过某种方式将其存入了数组中,从左往右观察上面这个表格,虽然我们想计算的是987654321 + 56789,但是存入数组中后我们发现,现在已经无法分辨 a 和 b 的值了。
a 的值变成了9876543210?
b 的值变成了5678900000?
我们可以记录下数字 a 最后一位所在数组的位置,数字 b 最后一位所在数组的位置,这样虽然不会出现上面的问题了,但是在实际的计算过程中会很繁琐,并伴随着各种问题。接下来,我们换一种思路来将数字存入数组(逆序存入),存入的结果如下表所示:
数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
b[] | 8 | 8 | 7 | 6 | 5 | 0 | 0 | 0 | 0 | 0 |
c[] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
此时我们从右往左看这个表:
a = 0987654321 = 987654321
b = 0000056789 = 56789
这样的结果才是我们所需要的,接下来我们继续来模仿列竖式的方法进行计算,c[n] = a[n] + b[n] + c[n], 如果结果大于等于10,我们就向前进一位。
c[0] = a[0] + b[0] + c[0] = 1 + 9 + 0 = 10,结果等于10, 向前进一位,进位之后c[1] = 1,c[0] = 0;
c[1] = a[1] + b[1] + c[1] = 2 + 8 + 1 = 11,结果大于10,向前进一位,进位之后c[2] = 1, c[1] = 1;
c[2] = a[2] + b[2] + c[2] = 3 + 7 + 1 = 11,结果大于10,向前进一位,进位之后c[3] = 1, c[2] = 1;
重复这样的操作,直到数组的最后一个元素。
执行完上述操作后,表格变成如下所示:
数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
b[] | 9 | 8 | 7 | 6 | 5 | 0 | 0 | 0 | 0 | 0 |
c[] | 0 | 1 | 1 | 1 | 1 | 7 | 7 | 8 | 9 | 0 |
现在我们从右向左观察数组c(c[]),每一位分别是0987711110,如果忽略前置0,结果就是98771110,这便是987654321 + 56789的结果。
以上就是大数加法的基本步骤了,实际上,我们并不需要数组 c 进行辅助运算,前文中使用原因只是为了方便说明,如果不利用c数组,那么初始的表将会变成如下样子:
数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
b[] | 9 | 8 | 7 | 6 | 5 | 0 | 0 | 0 | 0 | 0 |
只需要将数组a中的每一个元素加到数组b中,即b[n] = a[n] + b[n],再根据结果决定是否需要进位。
b[0] = a[0] + b[0] = 1 + 9 = 10,结果等于10,向前进一位,进位之后b[1] = 9, b[0] = 0;
b[1] = a[1] + b[1] = 2 + 9 = 11,结果大于10,向前进一位,进位之后b[2] = 8, b[1] = 1;
b[2] = a[2] + b[2] = 3 + 8 = 11,结果大于10,向前进一位,进位之后b[3] = 7, b[2] = 1;
重复这样的操作,直到数组的最后一个元素。
执行完上述操作后,我们可以得到这样一个表:
数组 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
b[] | 0 | 1 | 1 | 1 | 1 | 7 | 7 | 8 | 9 | 0 |
结果同样是987711110
首先是如何逆序将数据存入数组,我们可以在输入数据的时候将 a 和 b 分别存入两个字符串,然后获取字符串的长度,从字符串的最后一个元素开始依次添加到数组中,添加的时候只需要把字符减去字符0。
//这里str1是字符串,a是数组
//逆序存入str1中的元素到a数组
for(int i = strlen(str1) - 1, j = 0; i >= 0; i--)
a[j++] = str1[i] - '0';
//这里str2是字符串,b是数组
//逆序存入str2中的元素到b数组
for(int i = strlen(str2) - 1, j = 0; i >= 0; i--)
b[j++] = str2[i] - '0';
//执行完这两个循环之后就完成了逆序存入的操作
2.相加和进位:两个数组的每一位都分别相加,再通过相加的结果判断是否需要进位,maxn为数组的长度,但是这样的话,会有很多没用意义的计算,就比如上文中的例子,a[9] + b[9]就是没有意义的,后面会说到这个该如何优化。
//这里的maxn是数组的长度
for(int i = 0; i < maxn; i++)
{
b[i] += a[i];
if(b[i] >= 10)
{
b[i + 1] += b[i] / 10;
b[i] %= 10;
}
}
3.结果的输出:因为输出的时候需要从数组的最后一位向前输出,也就是说会有很多前置的零,上文中的例子,如果直接输出的话结果就是0987711110,但是我们并不需要前置0,所以在输出结果的时候应该过滤掉前置0,也就是说在输出的时候从第一个不为0的位置开始输出,实现这个的方式有很多,我是用以下的方式实现的输出:
//这里的maxn同上
int i;
//注意:这个for循环有一个分号
for(i = maxn - 1; i >= 0 && b[i] == 0; i--);
if(i >= 0)
for( ; i >= 0; i--)
cout << b[i];
else
cout << 0;
cout << endl;
下面是完整的代码:
#include
#include
#include
using namespace std;
const int maxn = 1000;
char str1[maxn], str2[maxn];
int a[maxn], b[maxn];
void sum()
{
//逆序存入
for(int i = strlen(str1) - 1, j = 0; i >= 0; i--)
a[j++] = str1[i] - '0';
for(int i = strlen(str2) - 1, j = 0; i >= 0; i--)
b[j++] = str2[i] - '0';
//相加和进位
for(int i = 0; i < maxn; i++)
{
b[i] += a[i];
//进位操作
if(b[i] >= 10)
{
b[i + 1] += b[i] / 10;
b[i] %= 10;
}
}
//输出结果
int i;
//过滤掉前置0
for(i = maxn - 1; i >= 0 && b[i] == 0; i--);
if(i >= 0)
for( ; i >= 0; i--)
cout << b[i];
//如果保存结果的数组中全部为0,上面的操作会过滤掉所有的0,不会有输出,所以这里输出一个0
else
cout << 0;
cout << endl;
}
int main()
{
//清空数组和字符数组
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(str1, 0, sizeof(str1));
memset(str2, 0, sizeof(str2));
//输入
cin >> str1 >> str2;
//执行加法计算
sum();
system("pause");
return 0;
}
maxn是在程序中定义的一个常量,但是在计算的过程中如果直接使用,会运行很多没有意义的计算过程,我们可以这样做一个优化,定义一个变量MAX,令MAX的值等于最长的字符串长度 + 1,即:
MAX = max(strlen(str1), strlen(str2)) + 1;
加1的原因:n位+n位的结果可能会是n+1位,举个例子来说,如果计算999+111,实际的结果应该是1110,如果不加1,那么输出的结果就是110。
用MAX替换sum()函数中的一些边界值即可。