大整数运算(高精度运算)C/C++

简介: 大整数运算(高精度运算)C/C++

前言

这种类型,在做题过程中多为观察所给数据可能造成的大小来选择是否使用。

属于模板类型,学习者理解其格式并记住大致框架即可熟练应用。


一、什么是大整数(高精度)

想知道什么是大整数,不如换一个解释的方法:什么时候需要用到大整数?

众所周知,int类型的范围是-2147483648~2147483647。long long类型的范围是 -2^63 ~ (2^63)-1。那当题目需要用到或需要输出比long long类型的范围还要大的数字时,我们是不是就不能用常规办法去接收这些数字了。这个时候使用大整数就可以解决上述问题。即解决接收超出long long范围数字的问题。

二、大整数的存储

原理很简单,使用数组。

例如定义Int类型数组d[1000],那么这个数组中的每一位就代表存放整数的每一位。比如将数字123456存储,则有d[0] = 6,d[1] = 5,d[2] = 4,d[3] = 3,d[4] = 2,d[5] = 1。整数的高位存在数组的高位,整数的低位存储在数组的低位。(为了契合加减乘除的思维)

这时候会产生一个需要注意的问题:把整数通过%s或者itoa的方式变成字符串的时候,一开始是逆位存储的,即d[0] = 1...,因此读入的之后需要在另存为至d[i]数组的时候反转一下

为了方便随时获取大整数的长度,一般定义len来记录长度,并与d数组组合成结构体。

1. struct bign{
2. int d[1000];
3. int len;
4. };

bign是big number的缩写。

一般输入大整数时,都是先用字符串读入,然后再把字符串另存为至bign结构体。由于使用char数组进行读入时,整数高位会变成数组地位,整数的地位会变成数组的高位。因此为了让整数在bign中是顺位存储,需要让字符串倒着赋值给d[]数组。

1. bign change(char str[])//将整数转换为bign 
2. {
3.  bign a;
4.  a.len = strlen(str);//bign的长度就是字符串的长度
5.  for(int i = 0; i < a.len; i++)
6.  {
7.    a.d[i] = str[a.len-i-1] - '0';
8.  }
9.  return a;
10. }

如果要比较两个bign变量的大小,也很简单:先判断len大小,若不相等,长的为大,若相等,则从高位至低位逐个比较,直到某一位不等,则可以判断两者大小。

1. int compare(bign a,bign b)
2. {
3.  if(a.len > b.len) return 1;//a大 
4.  else if(a.len < b.len) return -1;//a小 
5.  else
6.  {
7.    for(int i = a.len - 1; i >= 0; i--)//从高位到低位开始比较 
8.    {
9.      if(a.d[i] > b.d[i]) return 1;//只要有一位a大,则a大 
10.       else if(a.d[i] < b.d[i]) return -1;//只要有一位a小,则a小 
11.     }
12.     return 0;//两位相等 
13.   } 
14. }

最后如果需要输出bign变量的值,遍历一遍就可以实现了。

1. void print(bign a) 
2. {
3.  for(int i = a.len - 1; i >= 0; i--) 
4.     {
5.    printf("%d",a.d[i]);
6.  }
7.  printf("\n"); 
8. }

三、大整数的四则运算

1、高精度加法

加法实现方式与我们以前学到的加法一样。对于某一位的运算:我们将该位上的两个数字与进位相加,得到的结果取个位数作为该结果,十位数作为新的进位。

1. bign add(bign a,bign b) 
2. {
3.  bign c;
4.  int carry = 0;  //carry是进位
5.  for(int i = 0; i < a.len || i < b.len; i++) //以较长的为界限 
6.     {    
7.    int temp = a.d[i] + b.d[i] + carry;   //两个对应位与进位相加 
8.    c.d[c.len++] = temp%10;   //个位数为该位的结果 
9.    carry = temp/10;  //十位数为新的进位 
10.   }
11.   if(carry != 0) //如果最后进位不为0,则直接赋给结果的最高位  
12.     {
13.     c.d[c.len++] = carry;   
14.   }
15.   return c; 
16. }

这里有一点需要注意,这样写法的条件是两个对象都是非负整数。如果有一方是负的,可以在转换到数组这一步时去掉符号,再使用高精度减法;如果两个都是负的,都去掉负号后采用高精度加法,最后负号加回去即可。

2、 高精度减法

通过对减法步骤的拆分可以得到一个简练的步骤:对某一位,比较被减位和减位,如果不够减,则令被减位的高位减1,被减位加10再进行减法;如果够减,直接减。最后需要注意减法后高位可能有多余的0,要去除他们,但也要保证结果至少有一位数

1. bign sub(bign a,bign b) //a - b
2. {
3.  bign c;
4.  for(int i = 0; i < a.len || i < b.len; i++) //以较长的为界限
5.     {    
6.    if(a.d[i] < b.d[i]) //不够减 
7.         {    
8.      a.d[i + 1]--; //向高位借位 
9.      a.d[i] += 10;   //向前位借10  
10.     }
11.     c.d[c.len++] = a.d[i] - b.d[i];   //减法结果为当前位 
12.   }
13.   while(c.len - 1 >= 1 && c.d[c.len - 1] == 0) 
14.     {
15.     c.len--;  //去除高位的0,同时至少保留一位最低位 
16.   } 
17.   return c; 
18. }

最后需要指出,使用sub函数前要比较两个数的大小,如果被减数小于减数,需要交换两个变量,然后输出负号,再用sub函数。

3、高精度与低精度的乘法

这里所谓的低精度就是基本数据类型存储的数据,如int。这里就是bign与int的乘法。

对某一位来说是这样的步骤:取bign的某位与int型整体相乘,再与进位相加,所得结果的个位数作为该结果,高位部分作为新的进位。

1. bign multi(bign a,int b) 
2. {
3.  bign c;
4.  int carry = 0;  //进位
5.  for(int i = 0; i < a.len; i++) 
6.     {
7.    int temp = a.d[i] * b + carry;
8.    c.d[c.len++] = temp % 10;   //个位作为该结果
9.    carry = temp / 10;  //高位部分作为新的进位 
10.   } 
11. 
12.   while(carry != 0) //和加法不一样,乘法的进位可能不止一位,因此用while 
13.     { 
14.     c.d[c.len++] = carry % 10;
15.     carry /= 10;
16.   } 
17.   return c; 
18. }

另外,如果a和b中存在负数,需要先记录下负号,然后取他们的绝对值带入函数。

4、高精度与低精度的除法

归纳其中一位的步骤:上一步的余数乘以10加上该步的位,得到该步临时的被除数,将其与除数比较;如果不够除,则该位的商为0;如果够除,则商即为对应的商,余数即为对应的余数。最后一步要注意减法后高位可能有多余的0,要去除他们,但也要保证结果至少有一位数。

1. bign divide(bign a,int b,int& r) //r为余数 
2. {  
3.  bign c;
4.  c.len = a.len;//被除数的每一位和商的每一位是一一对应的,因此先令长度相等 
5.  for(int i = a.len - 1; i >= 0; i--)  //从高位开始 
6.     {   
7.    r = r * 10 + a.d[i];    //和上一位遗留的余数组合
8.    if(r < b) c.d[i] = 0;   //不够除,该位为0 
9. else //够除 
10.         {   
11.       c.d[i] = r / b; //商
12.       r = r % b;    //获得新的余数 
13.     }
14.   }
15.   while(c.len - 1 >= 1 && c.d[c.len - 1] == 0) 
16.     { 
17.     c.len--; //去除高位的0,同时至少保留一位最低位 
18.   }
19.   return c; 
20. }

考虑到很多题目会要求得到余数,因此把余数写成引用的形式直接作为参数传入,或者也可以把余数设成全局变量。

相关文章
|
16天前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
132 77
|
16天前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
37 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
16天前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
39 12
|
16天前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
39 9
|
16天前
|
存储 算法 测试技术
【C++数据结构——线性表】求集合的并、交和差运算(头歌实践教学平台习题)【合集】
本任务要求编写程序求两个集合的并集、交集和差集。主要内容包括: 1. **单链表表示集合**:使用单链表存储集合元素,确保元素唯一且无序。 2. **求并集**:遍历两个集合,将所有不同元素加入新链表。 3. **求交集**:遍历集合A,检查元素是否在集合B中存在,若存在则加入结果链表。 4. **求差集**:遍历集合A,检查元素是否不在集合B中,若满足条件则加入结果链表。 通过C++代码实现上述操作,并提供测试用例验证结果。测试输入为两个集合的元素,输出为有序集合A、B,以及它们的并集、交集和差集。 示例测试输入: ``` a c e f a b d e h i ``` 预期输出:
39 7
|
16天前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
31 5
|
16天前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】顺序表的基本运算(头歌实践教学平台习题)【合集】
本文档介绍了线性表的基本运算任务,涵盖顺序表和链表的初始化、销毁、判定是否为空、求长度、输出、查找元素、插入和删除元素等内容。通过C++代码示例详细展示了每一步骤的具体实现方法,并提供了测试说明和通关代码。 主要内容包括: - **任务描述**:实现顺序表的基本运算。 - **相关知识**:介绍线性表的基本概念及操作,如初始化、销毁、判定是否为空表等。 - **具体操作**:详述顺序表和链表的初始化、求长度、输出、查找、插入和删除元素的方法,并附有代码示例。 - **测试说明**:提供测试输入和预期输出,确保代码正确性。 - **通关代码**:给出完整的C++代码实现,帮助完成任务。 文档
31 5
|
8月前
|
C++
C++程序中的关系运算
C++程序中的关系运算
70 1
|
7月前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第二章 构造/析构/赋值运算 笔记
|
8月前
|
算法 C++
c++算法学习笔记 (4)高精度运算
c++算法学习笔记 (4)高精度运算