大数运算-减法(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 数组用来存储计算的结果,在讲述加法运算的时候,我们已经讲解过正序存储的一些问题,并采用了逆序存储避免了问题的出现,对于减法来说,同样采取逆序存储的方式。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 5 | 6 | 4 | 7 | 3 | 8 | 2 | 9 | 1 | 0 |
b[] | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 0 | 0 | 0 |
c[] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 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;
按照以上的步骤一直模拟到数组末尾,我们可以得到这样一个表
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 5 | 6 | 4 | 7 | 3 | 8 | 2 | 9 | 1 | 0 |
b[] | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 0 | 0 | 0 |
c[] | 6 | 7 | 6 | 0 | 8 | 3 | 9 | 8 | 1 | 0 |
虽然上面这些步骤乍一看好像乱糟糟的,但是仔细读一遍我们可以发现这其中是有规律的,而这个规律就是实现程序的关键。总结一下上面的过程:首先用上面的数减去bit(如果前一个数借了位,那么bit就为1,否则就为0),减去bit之后和下面的数进行比较,如果上面的数大于下面的数,则不需要借位(bit = 0),直接将上面的数减去下面的数的结果保存到 c 数组,如果上面的小于下面的数,则需要借位(bit = 0), 上面的数自加10,然后将自加10之后的数减去下面的数保存到 c 数组。
当然,c数组的作用和大数加法存在的理由是一样的,为了辅助说明,实际上可以不存在,如果不写c数组,那么初始的表就是这个样子的:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 5 | 6 | 4 | 7 | 3 | 8 | 2 | 9 | 1 | 0 |
b[] | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 0 | 0 | 0 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
a[] | 5 | 6 | 4 | 7 | 3 | 8 | 2 | 9 | 1 | 0 |
b[] | 6 | 7 | 6 | 0 | 8 | 3 | 9 | 8 | 1 | 0 |
代码实现
因为在大数加法中已经讲解了如何逆序存入数组和除去前置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++实现大数运算 加法部分。
- c++实现大数的加减乘运算(数组存放)
- c++大数、分数、模数运算实现模板
- C++之运算符的重载实现大数的运算
- 小项目之用C++实现以顺序表存储的大数运算
- C++实现大数的减法
- 用C\C++实现离散数学简单逻辑运算
- C++实现矩阵类,实现了大部分矩阵运算功能,大家可以类比matlab
- 大数加减法(C++实现)
- C++实现大数阶乘
- 21天学通C++之集合的链表实现及其运算实现
- 【Django】template中实现加减乘除数学运算[加法 减法 乘法 除法]
- 【C++】大数的+-*/四则运算
- c++实现大数乘法
- C语言实现大数运算
- JavaScript实现超大字符串运算减法
- C++实现R语言向量化运算(向量类:c 矩阵类:matrix)2015.9.11
- JavaScript实现大数的运算