C++ 解决大数问题(加、减、乘、除)(含分析、代码)

也就是被同学问到怎么计算,才发现自己对这个知识点疏忽了,看来题做得还是不够全面。


问题提出

C++如何计算两个很大很大的数相加,这两个数大到连 long long 也装不下,请问如何计算他们之和?

本文不仅仅分析加法,同时一并把四则运算(加、减、乘、除)总结一下。

大数加法分析

在计算机中,数字是以二进制的形式存储的,因此对于数的存储以及计算方面具有很大的限制。比如:int 的范围是 -2147483648 ~ 2147483647,超过或者低于他的范围就会产生溢出,从而我们的计算将得到错误的结果。与之类似的缺点在浮点数的存储上也存在着。

那么一个大到连 long long 都无法存储的数应该如何计算呢?

我们的解决方法是字符数组

让我们先来分析一下加法的步骤:

在小学我们就学过两个数的加法是如何计算的,如图:

加法完成的步骤:从低位开始计算,满十进一

因此,我们不妨直接采用数组来存储每一位(个位、十位...)的字符,然后使用进位加法来计算结果。

那么具体怎么实现呢,我们按照上面的例子。

第一步,存储

这一步骤是完成输入与字符数组之间的转换。目的是方便我们后续的计算(通常我们都习惯从index为0处开始计算)。

具体来说做法不唯一,达到这个目的即可,我的两种思路如下:

  1. 正序输入,正序存储,然后调用 reverse 将字符数组逆序。
  2. 正序输入,直接采用反序存储(前提是知道多少位)。

第二步,创建ans数组

输入已经无法存储了,结果更不可能存储。我们需要建立一个以最大整数的位数 +1 为长度的字符数组,帮助我们存储进位。

第三步,遍历数组,逐位相加

从左到右遍历数组,逐位完成相加,同时要记录进位操作。

在我们的例子中,完成 index = 0 的加法,其中,将 5 存储在 index 对应位置,将 1 存储在 index + 1 的位置。

下一步,同理。计算 9 + 5 + 1(进位)。

最后一步,同理。计算3 + 8 + 1(进位)

第四步,逆序输出

同理,这里仍旧可以使用两种方法,当然直接逆序输出是最快的。也可以采用 reverse() 将结果转换成正序。

代码

//大数加法
#include<bits/stdc++.h>
using namespace std;
char a[200] = {'\0'};
char b[200] = {'\0'};
int res[205] = {0};
int carry[201]={0};
//get sum
void get_sum(char a[], char b[]){
	int index = 0; //index
	int tmp;
	int len_a = strlen(a);
	int len_b = strlen(b);
	int len_max = len_a * (len_a >= len_b) + len_b * (len_a < len_b);
	int x11[200] = {0};
	for(int i = 0;i <= len_a - 1; i++)
	{
		x11[i]=a[i] - '0';
	}
	int x22[200] = {0};
	for(int i = 0; i <= len_b - 1; i++)
	{
		x22[i]=b[i] - '0';
	}
	//不考虑进位对应位相加
	for(int i = 0; i <= len_max - 1; i++)
	{
		res[i] = x11[i] + x22[i];
		res[i + 1] = 0; //用于防止超过lenmax后的一位
	}
	//考虑进位 完成最终求和
	for(int i = 0;i <= len_max + 1; i++)
	{
		carry[i+1] = res[i] / 10;
		res[i] = res[i] % 10 + carry[i];
	}
}
int main(){
	int i;
	//input
	cin>> a >> b;
	int len_a = strlen(a);
	int len_b = strlen(b);
	int len_max = len_a * (len_a >= len_b) + len_b * (len_a < len_b);
	//reserve
	strrev(a);
	strrev(b);
	//sum
	get_sum(a, b);
	if(res[len_max] == 0){
		i = len_max - 1;
	}else{
		i = len_max;
	}
	for(; i >= 0; i--){
		cout<<res[i];
	}
	return 0;
} 

大数乘法分析

有了前面加法的分析与代码,我们对大数的乘法应该有了一定的思路吧?大数的乘法也跟我们小学的乘法一样,需要注意的是对应 index 的值存放的位置很关键,详细见如下分析过程。我们以 125 × 53 为例。

第一步 倒序输入

这里同样是倒序输入,不再展开。

第二步 逐位相乘

图中 Ans 的索引从左到右逐渐递增

首先,我们计算 125 × 3。5 × 3 = 15,产生15个1;3 × 2 = 6,产生6个10;3 × 1 = 3,产生3个100。

下一步,我们计算 125 × 5。5 × 5 = 25,产生25个10;5 × 2 = 10,产生10个100;5 × 1 = 5,产生5个1000。

总结一个规律:即一个数的第 i 位和另一个数的第 j 位相乘所得的数,一定是要累加到结果的第 i+j 位上。这里i, j 都是从右往左,从0 开始数。即:res[i+j] = a[i] × b[j]。

第三步 进位加法

乘法步骤已经完成。我们需要做的就是将对应位置的进位加法处理完成即可。

第四步 逆序输出

最后进行逆序输出即可。这里也不展开了。

代码

//大数乘法
#include<bits/stdc++.h>
using namespace std;
char a[200] = {'\0'};
char b[200] = {'\0'};
int res[205] = {0};
//get_mul
void get_mul(char a[], char b[], int len_a, int len_b, int max_len){
	int index = 0;
	int x11[200] = {0};
	for(index = 0; index < len_a; index++){
		x11[index] = a[index] - '0';
	}
	int x22[200] = {0};
	for(index = 0; index < len_b; index++){
		x22[index] = b[index] - '0';
	}
	//最关键的一步
	for(index = 0; index < len_a; index++){
		for(int i = 0; i < len_b; i++){
			res[i + index] += x11[index] * x22[i];
		}
	} 
	//处理进位
	for(index = 0; index < len_a + len_b; index++){
		if(res[index] > 9){
			res[index + 1] += res[index] / 10;
			res[index] %= 10;
		}
	} 
}
int main(){
	//input
	cin >> a >> b;
	int len_a = strlen(a);
	int len_b = strlen(b);
	int max_len = len_a * (len_a >= len_b) + len_b * (len_a < len_b);
	int i = len_a + len_b;
	//reserve
	strrev(a);
	strrev(b);
	get_mul(a, b, len_a, len_b, max_len);
	while(res[i] == 0){
		i--;
	}
	for(; i >= 0; i--){
		cout<<res[i];
	}
	return 0;
} 

 

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

猜你喜欢

转载自blog.csdn.net/weixin_41960890/article/details/104847860
今日推荐