您的位置:首页 > 编程语言 > C语言/C++

大数运算-减法(C/C++实现)

2017-07-22 22:09 253 查看

大数运算-减法

前言

上一篇中已经介绍了大数运算的加法(如果没有看过上一篇,请先看一下上一篇,再继续看关于减法的讲解),是通过模拟列竖式的方法实现的,大数运算的减法实际上也是通过模拟列竖式来进行计算的,只是把‘+’号变成了‘-’号,进位变成了借位,接下来,让我们开始吧。

问题分析

和加法一样,首先我们先给定一组数据,来辅助说明整个计算过程,在减法中,可能会出现一个小的数字减去一个大的数字形成负数的情况,我们先不考虑那样的情况,只考虑式 a - b 中 a >= b 的情况。

a = 192837465

b = 3456789

计算 a - b 的结果

数字 a 和数字 b 的值都不是很大,这里就开辟长度为10的数组进行说明,a数组(a[])用来存储数字 a 的每一位,b数组用来存储数字 b 的每一位,c 数组用来存储计算的结果,在讲述加法运算的时候,我们已经讲解过正序存储的一些问题,并采用了逆序存储避免了问题的出现,对于减法来说,同样采取逆序存储的方式。

0123456789
a[]5647382910
b[]9876543000
c[]0000000000
可以看到,当逆序将数字存入之后,会呈现这样一个表,接下来我们来模仿列竖式的计算,对这个表进行操作,我们还需要记录每次计算是否需要从前面借位,所以我们再引入一个变量 bit,当bit = 1时,说明借位了,当bit = 0时,说明并未借位。

不要因为下面这些东西看起来很复杂而不去看,实际上逻辑是很简单的。

a[0] = 5, b[0] = 9, 此时 a[0] < b[0],需要从前面借一位,因为需要借位,所以 bit 置为 1,此时a[0]经过借位之后变成 a[0] = 15,c[0] = a[0] - b[0] = 15 - 9 = 6;

因为bit = 1,所以a[1]的值并不是 6,经过借位之后变成了 5,即 a[1] = a[1] - bit,a[1] = 5,b[1] = 8,此时 a[1] < b[1],需要从前面借一位,因为需要借位,所以bit置为 1,此时a[1]经过借位之后变成了a[1] = 15,c[1] = a[1] - b[1] = 15 - 8 = 7;

因为bit = 1 ,所以a[2]的值并不是 4,经过借位之后变成了 3,即a[2] = a[2] - bit, a[2] = 3,b[2] = 7, 此时a[2] < b[2], 需要从前面借一位,因为需要借位,所以bit置为1,此时a[2]经过借位之后变成了a[2] = 13,c[2] = a[2] - b[2] = 13 - 7 = 6;

因为bit = 1 ,所以a[3]的值并不是 7,经过借位之后变成了6,即 a[3] = a[3] - bit, a[3] = 6,b[3] = 6,此时a[3] = b[3],不需要从前面借一位,因为不需要借位,所以bit置为0,c[3] = a[3] - b[3] = 0;

因为bit = 0,所以a[4]的值还是3,即a[4] = a[4] - bit,a[4] = 3, b[4] = 5,此时 a[4] < b[4],需要从前面借一位,因为需要借位,所以bit置为1,此时a[4]经过借位之后变成了a[4] = 13,c[4] = a[4] - b[4] = 13 - 5 = 9;

按照以上的步骤一直模拟到数组末尾,我们可以得到这样一个表

0123456789
a[]5647382910
b[]9876543000
c[]6760839810
结果即为189380676

虽然上面这些步骤乍一看好像乱糟糟的,但是仔细读一遍我们可以发现这其中是有规律的,而这个规律就是实现程序的关键。总结一下上面的过程:首先用上面的数减去bit(如果前一个数借了位,那么bit就为1,否则就为0),减去bit之后和下面的数进行比较,如果上面的数大于下面的数,则不需要借位(bit = 0),直接将上面的数减去下面的数的结果保存到 c 数组,如果上面的小于下面的数,则需要借位(bit = 0), 上面的数自加10,然后将自加10之后的数减去下面的数保存到 c 数组。

当然,c数组的作用和大数加法存在的理由是一样的,为了辅助说明,实际上可以不存在,如果不写c数组,那么初始的表就是这个样子的:

0123456789
a[]5647382910
b[]9876543000
经过一系列的计算,计算过程实际上就是把上面的c[] = a[] - b[]改成了b[] = a[] - b[]。我们最终把表变成了这个样子:

0123456789
a[]5647382910
b[]6760839810

代码实现

因为在大数加法中已经讲解了如何逆序存入数组和除去前置0输出结果,所以再这里就不再说明了。这里就只详细说明一下计算的过程:

//初始化为0,第一个数是肯定没有被借过位的
int bit = 0;
//循环数组的每一个元素
for(int i = 0; i < maxn; i++)
{
//减去bit,如果bit=0,相当于没有从这里借位,如果bit=1,相当于从这里借过一位
a[i] -= bit;
//比较两个数字的大小
if(a[i] < b[i])
{
//如果上面的数字较小,就借一位,也就是bit置1,自加10
a[i] += 10;
bit = 1;
}
else
//如果上面的数字大于等于下面的数字,说明不需要借位,bit置0
bit = 0;
//保存上面的数减去下面的数的结果
b[i] = a[i] - b[i];
}


完整的减法代码如下

void sub()
{
//逆序存入
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';
//计算以及借位
int bit = 0;
for(int i = 0; i < maxn; i++)
{
a[i] -= bit;
if(a[i] < b[i])
{
a[i] += 10;
bit = 1;
}
else
bit = 0;
b[i] = a[i] - b[i];
}
//去除前置0的输出
int i;
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;
}


我们在前面遗留了一个问题,默认 a - b 中的 a 是大于等于 b 的,所以说这个程序是有缺陷的,但是实际上我们只需要做一些小的改动,就可以解决这个问题,使其既能计算 a >= b 的 a - b,又能计算 a < b 的 a - b。

在输入 a 和 b 的时候,我们使用的是字符串进行保存,在这里说明一下我们该如果通过字符串来比较数字的大小:

先比较两个字符串的长度,长度长的数字大;

如果长度一样,比较第一位字符的ASCII码大小,ASCII码大的字符串大,如果第一位字符相等,依次类推,向后比较(strcmp函数)。

我们对上面的sub()函数添加两个形参,在传递参数的时候做一个比较,将较大的作为第一个参数,较小的作为第二个参数传入到sub函数中 ,这样计算出来的结果相当于是绝对值,如果输入的时候两个字符串分别为str1 , str2,而传参的时候是sub(str2, str1),那么只需要在结果前加一个负号,如果传参的时候是sub(str1, str2),那么则不需要做任何操作,或者选择在前面加一个正号。

示例代码

#include<iostream>
#include<cstring>
#include<cstdlib>
#define maxn 1000
using namespace std;
int a[maxn], b[maxn];

bool myStrcmp(const char str1[], const char str2[])
{
if(strlen(str1) > strlen(str2))
return true;
if(strlen(str1) == strlen(str2))
return strcmp(str1, str2) > 0;
return false;
}

void sub(char str1[], char str2[])
{
//逆序存入
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';
//计算
int bit = 0;
for(int i = 0; i < maxn; i++)
{
a[i] -= bit;
if(a[i] < b[i])
{
a[i] += 10;
bit = 1;
}
else
bit = 0;
b[i] = a[i] - b[i];
}
//输出
int i;
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;
}

int main()
{
char str1[maxn], str2[maxn];
//清空数组和字符数组
memset(str1, 0, sizeof(str1));
memset(str2, 0, sizeof(str2));
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
cin >> str1 >> str2;
//判断字符串大小(即判断数字大小)
if(myStrcmp(str1, str2))
{
cout << "+";
sub(str1, str2);
}
else
{
cout << "-";
sub(str2, str1);
}
system("pause");
return 0;
}


这里也有很多无用的计算过程,优化方法同上一篇中所写。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 c++ 大数运算