当前位置:主页 > c/c++教程 > C/C++高精度运算

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

发布:2023-03-13 14:00:01 59


给网友朋友们带来一篇相关的编程文章,网友魏梓萱根据主题投稿了本篇教程内容,涉及到c++、高精度计算、c++高精度加法、c语言大整数运算、C/C++高精度运算相关内容,已被101网友关注,下面的电子资料对本篇知识点有更加详尽的解释。

C/C++高精度运算

前言

高精度的运算在算法题尤为常见,在处理一些大型数据的项目中也需要用到。虽然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
#include
#include
using namespace std;

const int maxn = 10001;
pair 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=0;i--){
		r = r*10 + a.d[i];
		if(r=1&&c.d[c.len-1]==0){
		c.len--;
	}
	return c;
}

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

int cmp(pair a,pair b){
	return a.first*a.second

总结

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


参考资料

相关文章

  • Java C++题解leetcode1620网络信号最好的坐标

    发布:2023-03-13

    这篇文章主要为大家介绍了Java C++题解leetcode1620网络信号最好的坐标示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪


  • C/C++中时间库函数的使用详解

    发布:2023-03-13

    这篇文章主要为大家详细介绍了C/C++中的时间相关知识总结,例如时间库函数的使用以及获取本地时间的不同方法,文中的示例代码讲解详细,需要的可以参考一下


  • 详解C++中的析构函数

    发布:2022-12-12

    给大家整理一篇关于C++的教程,这篇文章主要介绍了C++中的析构函数的相关知识,文中讲解非常详细,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下


  • c++只保留float型的小数点后两位问题

    发布:2023-03-12

    这篇文章主要介绍了c++只保留float型的小数点后两位问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教


  • C++ Boost Algorithm算法超详细精讲

    发布:2023-03-07

    Boost.Algorithm 提供了补充标准库算法的算法。与 Boost.Range 不同,Boost.Algorithm 没有引入新概念。 Boost.Algorithm 定义的算法类似于标准库中的算法


  • C++AVL树4种旋转详讲(左单旋、右单旋、左右双旋、右左双旋)

    发布:2023-03-11

    AVL树即平衡二叉搜索树,平衡因子bf=右子树的高度-左子树的高度,bf为0,-1,1时,此树即平衡,下面这篇文章主要给大家介绍了关于C++AVL树4种旋转(左单旋、右单旋、左右双旋、右左双旋)的相关资料,需要的朋友可以参考下


  • C++使用回溯法解决黄金矿工问题

    发布:2023-03-03

    在矩阵中考察回溯算法,分为任意起点、左上角开始等情况。从而有不同的模板,其实区别就是直接开始还是每个坐标都去尝试,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧


  • C++实现会员管理程序的具体方案

    发布:2021-06-01

    这篇文章主要为大家详细介绍了C++实现会员管理程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下


网友讨论