C/C++高精度运算(大整数运算)详细讲解

目录
  • 前言
  • 什么是大整数
  • 大整数的表示
  • 大整数的运算
    • 1、高精度加法
    • 2、高精度减法
    • 3、高精度乘以低精度
    • 4、高精度除以低精度
  • 大整数的表示
  • 补充:使用示例
  • 总结

前言

高精度的运算在算法题尤为常见,在处理一些大型数据的项目中也需要用到。虽然Boost库中有处理高精度问题的模板,但是标准库中并没有。为了方便学习,本文提供了高精度运算的模板,供大家参考。

什么是大整数

众所周知,最大的整型long long的范围是【-9223372036854775808~9223372036854775807】,即使是无符号位的unsigned long long最大值也只是扩大一倍,那当题目需要用到或需要输出比long long类型的范围还要大的数字时,我们是不是就不能用常规办法去接收这些数字了。这个时候使用大整数就可以解决上述问题——也就是解决接收超出整型范围数字的问题

大整数的表示

其实大整数的表示方法也不难,我们使用数组就可以了(当然C++的vector容器也是可以的,原理都一样,但为了照顾C语言的小伙伴,本文用数组表示)。

假设现在有一个int类型数组d[1000],那么我们可以用数组中的每一位元素表示大整数中的每一位数字,比如有整数123456789,那么我们可以用d[0]表示亿位上的【1】,用d[1]表示千万位上的【2】...以此类推,我们就可以用一个长度为9的数组表示这个大整数了。

当然为了契合我们后面四则运算的思维,我们将数组元素的顺序翻转一次,也就是在数组靠前的元素表示低位,而靠后的元素表示高位(原因后面会讲到),也就是如下图所示:

而为了方便我们获取当前大整数的长度,我们可以使用一个结构体(或者一个类,与C语言的结构体不同,在C++中的结构体也可以定义成员函数)来表示。

//struct of bign(big number)
struct bign {
    int d[1000];
    int len;
	bign()//构造函数
	{
		this->len = 0;
		memset(d, 0, sizeof(d));
	}
};

当然,一般输入大整数时,都是先用字符串读入,然后再把字符串另存为至bign结构体

bign change(const string str)//将整数转换为begin c语言用const char*
{
	bign a;
	a.len = str.size();//bign的长度就是字符串的长度 c语言用strlen()函数
	for (int i = 0; i < a.len; i++)
	{
		a.d[i] = str[a.len - i - 1] - '0';
	}
	return a;
}

大整数的运算

对于大整数的四则运算,我们需要模拟在小学期间学习四则运算的思路和过程,也就是把我们在草稿纸上运算的过程,抽象成代码的逻辑。

1、高精度加法

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

bign add(bign a, bign b)
{
	bign c;
	int carry = 0;	//carry是进位标志
	for (int i = 0; i < a.len || i < b.len; i++) //以较长的为界限
	{
		int temp = a.d[i] + b.d[i] + carry;		//两个对应位与进位相加
		c.d[c.len++] = temp % 10;		//取个位数为该位的结果
		carry = temp / 10;	//取十位数为新的进位
	}
	if (carry) //如果最后的进位不为0,则直接赋给结果的最高位
	{
		c.d[c.len++] = carry;
	}
	return c;
}

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

2、高精度减法

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

bign sub(bign a, bign b) //a - b
{
	bign c;
	for (int i = 0; i < a.len || i < b.len; i++) //以较长的为界限
	{
		if (a.d[i] < b.d[i]) //不够减
		{
			a.d[i + 1]--;	//向高位借位
			a.d[i] += 10; 	//向前位借10
		}
		c.d[c.len++] = a.d[i] - b.d[i];		//减法结果为当前位
	}
	while (c.len >= 2 && c.d[c.len - 1] == 0)    //剩余的位数不小于十位,并且最高位是0
	{
		c.len--;	//去除高位的0,同时至少保留一位最低位
	}
	return c;
}

3、高精度乘以低精度

所谓高精度乘以低精度,就是bign*int的运算。

对某一位来说是这样的步骤:取bign的某位与int型整体相乘,再与进位相加,所得结果的个位数作为该结果,高位部分作为新的进位。对于a、b异号的情况只需要一个标志位变量记录,在输出的时候加上负号就行了。

bign multi(bign a, int b)
{
	bign c;
	int carry = 0;	//进位
	for (int i = 0; i < a.len; i++)
	{
		int temp = a.d[i] * b + carry;
		c.d[c.len++] = temp % 10;		//个位作为该结果
		carry = temp / 10;	//高位部分作为新的进位
	}

	while (carry != 0) //和加法不一样,乘法的进位可能不止一位,因此用while
	{
		c.d[c.len++] = carry % 10;
		carry /= 10;
	}
	return c;
}

4、高精度除以低精度

高精度除以低精度,就是bign/int的运算。考虑到有时还需要知道计算之后的余数,于是就把余数写成引用的形式传入,当然也可以把余数设成全局变量。

对于某一位来说:上一步的余数乘以10加上该步的位,得到该步当前的被除数,将其与除数比较;如果不够除,则该位的商为0;如果够除,则商即为对应的商,余数即为对应的余数。和其他运算一样,要注意结果可能有多余的0,要去掉它们,但也要保证结果至少有一位数。

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

如果大家对于上述的逻辑还不清楚的话,可以自己在稿纸上举例几个简单的数算算,实际思路和我们运算时的思路是一样的哈。

大整数的表示

最后,当我们要打印出大整数时则要注意了,在上文中,我们为了方便运算,将数组中的位序翻转了一次,所以打印时就是需要从后往前输出。

而如果我们输入的数据是【04】,那么输出的结果就不是单纯的【4】了,一般来说这不是我们想要的结果是吧。那么为了解决这个问题,我们可以在打印的时候加上一个标志位来判断。

void print(bign ans)
{
	int flag = 0;
	for (int i = ans.len - 1; i >= 0; i--)
	{
		if (ans.d[i] != 0)    //标志位如果首位是0 则不输出
		{
			flag = 1;
		}
		if (flag)
		{
			cout << ans.d[i];
		}
	}
	if (!flag)
		cout << 0;    //包含输出0的情况
}

补充:使用示例

例题1(贪心+大整数)

题目:洛谷P1080 国王游戏

分析

此题需要先贪心找到排序规律为按照每个人的左右手乘积非降序排列,在计算最小值时累乘的过程中可能数字溢出,所以要使用大整数,但是根据题目描述大整数开100001位是不够的需要开的更大,但是实际测试数据并没有这么大。

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 10001;
pair<int,int> nums[maxn];	//first 右手,second左手

struct bign{	//存123时: d={3,2,1}llen =3;
	int d[maxn];
	int len;
	bign(){
		memset(d,0,sizeof(d));
		len = 0;}
}res,sum,ans;	//res最终结果,sum累乘结果,ans比较变量

bign multi(bign a,int b){	//高精度大整数与低精度的乘法
	bign c;
	int carry = 0; //进位
	for(int i=0;i<a.len;i++){
		int temp = a.d[i]*b + carry;
		//printf("%d*%d+%d\n",a.d[i],b,carry);
		c.d[c.len++] = temp%10;
		carry = temp / 10;
	}
	while(carry!=0){
		c.d[c.len++] = carry%10;
		carry/=10;
	}
	return c;
}

bign divide(bign a,int b,int& r){	//高精度除以低精度,r为余数
	bign c;
	c.len = a.len;
	r=0;
	for(int i=a.len-1;i>=0;i--){
		r = r*10 + a.d[i];
		if(r<b) c.d[i] = 0;
		else{
			c.d[i] = r/b;
			r %= b;
		}
	}
	while(c.len-1>=1&&c.d[c.len-1]==0){
		c.len--;
	}
	return c;
}

int compare(bign a,bign b){	//比较a和b的大小,a>b返回1,相等返回0,a<b返回-1
	if(a.len>b.len) return 1;
	if(a.len<b.len) return -1;
	for(int i=a.len-1;i>=0;i--){
		if(a.d[i]>b.d[i]) return 1;
		if(a.d[i]<b.d[i]) return -1;
	}
	return 0;
}

void print(bign a){	//输出大整数
	for(int i=a.len-1;i>=0;i--)
		printf("%d",a.d[i]);
	return;
}

int cmp(pair<int,int> a,pair<int,int> b){
	return a.first*a.second<b.first*b.second;
}

int main(){
	int n;
	scanf("%d",&n);
	scanf("%d %d",&nums[0].first,&nums[0].second);
	for(int i=1;i<=n;i++)
		scanf("%d %d",&nums[i].first,&nums[i].second);
	sort(nums+1,nums+1+n,cmp);
	sum.d[0] = 1; sum.len = 1;
	for(int i=1;i<=n;i++){
		int r = 0;	//存余数
		sum = multi(sum,nums[i-1].first);
		ans = divide(sum,nums[i].second,r);
		if(compare(ans,res)==1)
			res = ans;
	}
	print(res);
	return 0;
}

总结

到此这篇关于C/C++高精度运算(大整数运算)的文章就介绍到这了,更多相关C/C++高精度运算内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++实现大整数乘法

    算法竞赛入门经典 这本书并没有对大数乘法实现,所以自己补充了一下,乘法的实现很简单,就是再其数据结构基础上把每宽为8位的十进制数看成多项式的系数,vector的下标看成多项式的指数,然后再对应相乘相加就可以了,注意系数超过8位 将超八位的补分进位. 我这里是笛卡尔相乘.一般来说是够用的. 但其实多项式乘法算法还有很多更高效的. #include <iostream> #include <vector> #include <cstring> #include <cs

  • C语言实现大整数加减运算详解

    前言 我们知道,在数学中,数值的大小是没有上限的,但是在计算机中,由于字长的限制,计算机所能表示的范围是有限的,当我们对比较小的数进行运算时,如:1234+5678,这样的数值并没有超出计算机的表示范围,所以可以运算.但是当我们在实际的应用中进行大量的数据处理时,会发现参与运算的数往往超过计算机的基本数据类型的表示范围,比如说,在天文学上,如果一个星球距离我们为100万光年,那么我们将其化简为公里,或者是米的时候,我们会发现这是一个很大的数.这样计算机将无法对其进行直接计算. 可能我们认为实际应

  • C语言实现高精度加减法

    本文实例为大家分享了C语言实现高精度加减法的具体代码,供大家参考,具体内容如下 首先,我们来看一下C语言中各类型的最值: unsigned int 0-4294967295 int -2147483648-2147483647 unsigned long 0-4294967295 long -2147483648-2147483647 long long的最大值:9223372036854775807 long long的最小值:-9223372036854775808 unsigned lon

  • C++实现大整数乘法(字符串乘法)

    本文实例为大家分享了C++实现大整数乘法的具体代码,供大家参考,具体内容如下 #include<iostream> #include<algorithm> #include<string> using namespace std; string add(string a,string b) { if(a.length()==0) return b; if(b.length()==0) return a; a.length()<b.length()?a.swap(b

  • C语言实现高精度加法

    本篇为高精度加法的计算,接下来我还会去写高精度乘法的计算. 一.高精度运算的概念 高精度运算其实就是参与运算的数完全超出基本数据类型所能表示的范围的运算(例如int型范围就是 - 2147483648 ~+ 2147483647) 所以如果想求较大数的话就要创建新的数据类型 二.数据类型的选取 我选择的是int型数组,这样比较便于计算和理解 三.代码 其中a和b我是通过随机数来赋值 //author summer_awn //date 2017/6/20 #include<iostream>

  • C/C++高精度算法的实现

    做ACM题的时候,经常遇到大数的加减乘除,乘幂,阶乘的计算,这时给定的数据类型往往不够表示最后结果,这时就需要用到高精度算法.高精度算法的本质是把大数拆成若干固定长度的块,然后对每一块进行相应的运算.这里以考虑4位数字为一块为例,且输入的大数均为正整数(也可以考虑其他位,但要注意在每一块进行相应运算时不能超出数据类型的数值范围:有负整数的话读入时判断一下正负号在决定运算). 1. 高精度加法 以3479957928375817 + 897259321544245为例: 3479 9579 283

  • c++实现高精度加法

    最近遇到一个c++实现高精度加法的问题,高精度问题往往十复杂但发现其中的规律后发现并没有那么复杂,这里我实现了一个整数的高精度加法,主要需要注意以下几点: 1:将所需输入的数据以字符数组的形式输入,建立字符数组,建立相应的整数数组,然后一一映射,以此来实现数据的输入,需要注意的是,当实现字符向数字映射时,应该减去相应的ASCII偏移值,即48. 2:为了模拟我们在纸上手算的进位模拟运算,我们将字符数组反向填入整数数组,上图的后几行代码实现了这个操作. 3:实现进位加法,这是整个代码的核心部分,需

  • C语言实现高精度的加法

    本文实例为大家分享了C语言实现高精度的加法,供大家参考,具体内容如下 由键盘输入两个位数很长的整数(一行一个,最多不超过80位),试计算并输出这两个数的和. 输入样例 1234567890123456789353534532453453453434534 987654321098765324534534534534532 输出样例 1234567891111111110452299856987987987969066 解题思路: 由于一个普通的变量不能保存十多位长的整数,所以通过数组表示最后的运

  • c++加法高精度算法的简单实现

    c++高精度算法,对于新手来说还是一大挑战,只要克服它,你就开启了编程的新篇章,算法. 我发的这个代码并不是很好,占用内存很多而且运行时间很长(不超过1秒),但是很好理解,很适合新手 高精算法的本质就是把数组编程字符串,然后将字符串像竖式一样加起来: a+b高精度算法 #include <iostream> #include <cmath> #include <cstring> using namespace std; int main() { char a[10001

  • C/C++高精度运算(大整数运算)详细讲解

    目录 前言 什么是大整数 大整数的表示 大整数的运算 1.高精度加法 2.高精度减法 3.高精度乘以低精度 4.高精度除以低精度 大整数的表示 补充:使用示例 总结 前言 高精度的运算在算法题尤为常见,在处理一些大型数据的项目中也需要用到.虽然Boost库中有处理高精度问题的模板,但是标准库中并没有.为了方便学习,本文提供了高精度运算的模板,供大家参考. 什么是大整数 众所周知,最大的整型long long的范围是[-9223372036854775808~9223372036854775807

  • Java 高精度的大数字运算方式

    目录 Java 高精度的大数字运算 高精度整数BigInteger 高精度浮点数BigDecimal (1)BigInteger和BigDecimal都是不可变(immutable) (2)BigDecimal有4个够造方法 (3)equals()方法认为0.1和0.1是相等的 (4)另外还有一些情形 java超长数据高精度计算(仅支持整数) Java 高精度的大数字运算 为了解决Java基本数据类型在运算时会出现的溢出和计算不精确的问题.Java 提供了两个类BigInteger和BigDec

  • Java大数字运算之BigInteger 原创

    在 Java 中,有许多数字处理的类,比如 Integer 类.但是Integer 类有一定的局限性,下面我们就来看看比 Integer 类更厉害的一个,BigInteger类. BigInteger类型的数字范围较 Integer 类型的数字范围要大得多.我们都知道 Integer 是 Int 的包装类,int 的最大值为 231-1,如果要计算更大的数字,使用Integer 数据类型就无法实现了,所以 Java 中提供了BigInteger 类来处理更大的数字. BigInteger 支持任

  • Java基础教程之整数运算

    目录 引言 溢出 自增/自减 移位运算 位运算 运算优先级 类型的自动提升与强制转型 练习 小结 总结 引言 Java的整数运算遵循四则运算规则,可以使用任意嵌套的小括号.四则运算规则和初等数学一致.例如: public class Main { public static void main(String[] args) { int i=(100+200)*(99-88);//3300 int n=7*(5+(i-9));//23072 System.out.println(i); Syste

  • Python 实现大整数乘法算法的示例代码

    我们平时接触的长乘法,按位相乘,是一种时间复杂度为 O(n ^ 2) 的算法.今天,我们来介绍一种时间复杂度为 O (n ^ log 3) 的大整数乘法(log 表示以 2 为底的对数). 介绍原理 karatsuba 算法要求乘数与被乘数要满足以下几个条件,第一,乘数与被乘数的位数相同:第二,乘数与被乘数的位数应为  2 次幂,即为 2 ^ 2,  2 ^ 3, 2 ^ 4, 2 ^ n 等数值. 下面我们先来看几个简单的例子,并以此来了解 karatsuba 算法的使用方法. 两位数相乘 我

  • python里大整数相乘相关技巧指南

    问题 大整数相乘 思路说明 对于大整数计算,一般都要用某种方法转化,否则会溢出.但是python无此担忧了. Python支持"无限精度"的整数,一般情况下不用考虑整数溢出的问题,而且Python Int类型与任意精度的Long整数类可以无缝转换,超过Int 范围的情况都将转换成Long类型. 例如: >>> 2899887676637907866*1788778992788348277389943 5187258157415700236034169791337062

  • Java 超详细讲解十大排序算法面试无忧

    目录 排序算法的稳定性: 一.选择排序 二.冒泡排序 三.插入排序 四.希尔排序 五.堆排序 六.归并排序 七.快速排序 八.鸽巢排序 九.计数排序 十.基数排序 排序算法的稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,如果排序以后,保证这些记录的相对次序保持不变,即在原序列中,a[i]=a[j],且 a[i] 在 a[j] 之前,排序后保证 a[i] 仍在 a[j] 之前,则称这种排序算法是稳定的:否则称为不稳定的. 一.选择排序 每次从待排序的元素中选择最小的元素,依次

  • Java超详细讲解如何生成随机整数

    目录 1. java.util.Random 2. 数学.随机 3. Java 8 Random.ints 1. java.util.Random 这Random().nextInt(int bound)会生成一个从 0(包括)到 bound(不包括)的随机整数. (1)代码片段 对于getRandomNumberInRange(5, 10),这将生成一个介于 5(含)和 10(含)之间的随机整数. private static int getRandomNumberInRange(int mi

  • Python图像运算之顶帽运算和底帽运算详解

    目录 一.图像顶帽运算 二.图像底帽运算 三.总结 一.图像顶帽运算 图像顶帽运算(top-hat transformation)又称为图像礼帽运算,它是用原始图像减去图像开运算后的结果,常用于解决由于光照不均匀图像分割出错的问题.其公式定义如下: 图像顶帽运算是用一个结构元通过开运算从一幅图像中删除物体,顶帽运算用于暗背景上的亮物体,它的一个重要用途是校正不均匀光照的影响.其效果图如图1所示. 在Python中,图像顶帽运算主要调用morphologyEx()实现,其中参数cv2.MORPH_

随机推荐