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

大数运算-减法

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

问题分析
  • 和加法一样,首先我们先给定一组数据,来辅助说明整个计算过程,在减法中,可能会出现一个小的数字减去一个大的数字形成负数的情况,我们先不考虑那样的情况,只考虑式 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 的时候,我们使用的是字符串进行保存,在这里说明一下我们该如果通过字符串来比较数字的大小:

    1. 先比较两个字符串的长度,长度长的数字大;
    2. 如果长度一样,比较第一位字符的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;
}
  • 这里也有很多无用的计算过程,优化方法同上一篇中所写。
  • 23
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
以下是一个简单的 C++ 大数实现,它可以实现大数的加减运算: ``` #include <iostream> #include <string> #include <algorithm> using namespace std; class BigInt { public: string num; BigInt() {} BigInt(string s) { num = s; } BigInt operator+(BigInt b) { BigInt res; int carry = 0; int len1 = num.length(), len2 = b.num.length(); int len = max(len1, len2); for (int i = 0; i < len; i++) { int a = i < len1 ? num[len1 - 1 - i] - '0' : 0; int b = i < len2 ? b.num[len2 - 1 - i] - '0' : 0; int sum = a + b + carry; carry = sum / 10; sum = sum % 10; res.num.insert(0, to_string(sum)); } if (carry) { res.num.insert(0, to_string(carry)); } return res; } BigInt operator-(BigInt b) { BigInt res; int borrow = 0; int len1 = num.length(), len2 = b.num.length(); int len = max(len1, len2); for (int i = 0; i < len; i++) { int a = i < len1 ? num[len1 - 1 - i] - '0' : 0; int b = i < len2 ? b.num[len2 - 1 - i] - '0' : 0; int diff = a - b - borrow; if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } res.num.insert(0, to_string(diff)); } while (res.num.length() > 1 && res.num[0] == '0') { res.num.erase(0, 1); } return res; } void print() { cout << num << endl; } }; int main() { BigInt a("123456789"), b("987654321"); BigInt c = a + b; BigInt d = b - a; c.print(); // 输出 1111111110 d.print(); // 输出 864197532 return 0; } ``` 以上代码实现了 BigInt 类,这个类包含了 num 字符串,用来存储大数。它的构造函数可以接受一个字符串作为参数,用于初始化大数。 类中的 `operator+` 和 `operator-` 分别实现大数的加法和减法。其中,加法的实现比较简单,使用了竖式加法的思想,从低位开始逐位相加,最后将结果逆序输出即可。减法实现稍微复杂一些,需要考虑借位的情况。 最后,我们在 `main` 函数中创建了两个大数 a 和 b,分别进行了加法和减法运算,并将结果打印出来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值