高精度算法

孤者浪人 提交于 2019-12-22 03:38:03

高精度算法

什么是高精度算法?
在我们的计算中,我们可能有长达几百位或者更多的数字进行加减乘除运算,我们在这里把这些整数称为高精度数,高精度算法就是用计算机对这些超大数据进行模拟加减乘除等运算。
高精度算法有什么用?
我们学完c后发现,int表示的数据范围是-2^31 ~ 2^32-1也就是[-2147483648,2147483657]这也就10位数字,我们最长的 long long int 也不过19位数字远远达不到几百位甚至更多的要求,这里如果要对这些数进行加减乘除等运算就需要用到高精度算法。
在这里,我简单介绍一下加法、减法和乘法:
我们知道,要进行高精度的运算的数字就很大,用long long 都可能存不下去,所以,我们想到了用字符存储的方法,就是把每一位数字都看做是一个字符,存储到字符串之中,然后我们通过“字符 – 48 = 数字”的这个ASCII码规律把每一位数字存入数组的每一位之中,我们就可以模拟运算了。
代码是这样的:

	char a[1001], b[1001];
	int c[101], d[101];
	cin >> a >> b;
	int len1 = strlen(a);
	int len2 = strlen(b);
	for(int i = 0;i < len1; i++)
	c[i] = a[len1-i-1] - 48;
	for(int i = 0; i < len2; i++)
	d[i] = b[len2-i-1] - 48;

高精度加法

这里要说明的是,因为我们进行加减乘运算时都是从低位开始,到高位介绍,所以我们整型数组倒序存放我们的数据,以便运算。
我们先来看加法运算,根据我们的加法运算法则,如果超过十就往前进1,我们就很快能模拟得到加法运算的核心:

for(int i = 0;i < len;i++)
	{
		c[i] += d[i];
		c[i+1] += c[i]/10;
		c[i] %= 10; 
	}
	if(c[len])//如果这一位不是0的话就要一并计入 
	len++;//比如 99 + 11 = 110 这时候数位就多了一位 

这里的len是最长的数据有多少个数字。
在网上的很多高精运算中,把这些运算放在main函数之中,这样就有一个缺点,我们要是多次使用高精运算时,我们就要复制粘贴很多次,我们可以把它作为一个函数模块,这样就可以多次使用,而且看起来也不那么凌乱。
我们可以用一个string类来完成这个操作。

string a, b;
string _add(string a, string b)
{
	int ia[10000] = {0}, ib[10000] = {0}, sum[10000] = {0};//因为在函数里面,所以要清零一下 
	for(int i = 0; i < a.size() ; i++)// a.size()就是a的可见字符长度,与strlen的效果是一样的 
	ia[i] = a[a.size() - i - 1] - 48;
	for(int i = 0; i < b.size() ; i++)
	ib[i] = b[b.size() - i - 1] - 48;
	int len = max(a.size() , b.size() );
	for(int i = 0; i < len; i++)
	{
		sum[i] += ia[i] + ib[i];
		sum[i + 1] += sum[i] / 10;
		sum[i] %= 10;
	}
	if(sum[len])
	len++;
	string _diff;//因为我们的函数是string类型的,所以返回值也是string类型的 
	for(int i = 0; i < len; i++)
	_diff += sum[len - i - 1] + 48;
	return _diff;
}

高精度加法我们写完了,我们再来看看

高精度减法

减法有多种情况,正-负,负-负,正-正,负-正,得到的答案也不太相同,我们这里讨论一下正-正,其他情况通过与加法的结合也能做出来。
正-正我们得到的就有三种情况,负数,正数,0,我们可以把0看作是省略了+的+0。
前面的模拟是一样的,把字符串转为整型数组,我们先来讨论一下如何确定符号的问题,
我们可以这样,拥有用大数减小数,永远的得到的非负数,我们在后面就不用考虑符号了,
如何实现呢?
如果一开始我们是小数减大数,我们可以在结果的前面添加一个负号,然后把两个数互换位置,就变成了大数减小数,我们这样写:

string _minus(string a, string b)
{
	if(a.size() <= b.size() && a < b || a.size() < b.size() )
	return "-" + _minus(b, a);

这样看来,我们用string类型的优势是不是展现出来了,如果两个数的数位是一样的话,可以直接用直接将a b比较大小,这是字符串不具有的功能。
我们再来看减法的核心部分

int len = a.size() ;
	for(int i = 1; i <= len ; i++)
	{
		if(ia[i] < ib[i])
		{
			ia[i] += 10;
			ia[i + 1] --;
		}
		m[i] = ia[i] - ib[i];
	}
	while(!m[len] && len > 1)
	len --;

前面的模拟很好理解,后面while里面的东西是什么意思呢?我们知道,运算出来的结果前面的零是没有意义的,如果我们输出了000123这样就不太好,我们应该是输出123,这样才对,所以当最高位为0且最高位不是第一位时,我们就不要这个数位了。
最后我们把它还是变成string类,然后返回,给出减法子函数的全部代码:

string _minus(string a, string b)
{
	if(a.size() <= b.size() && a < b || a.size() < b.size() )
	return "-" + _minus(b, a);
	int ia[10500] = {0}, ib[10500] = {0}, m[10500] = {0};
	for(int i = 1; i <= a.size() ; i++)
	ia[i] = a[a.size() - i] - 48;
	for(int i = 1; i <= b.size() ; i++)
	ib[i] = b[b.size() - i] - 48;
	int len = a.size() ;
	for(int i = 1; i <= len ; i++)
	{
		if(ia[i] < ib[i])
		{
			ia[i] += 10;
			ia[i + 1] --;
		}
		m[i] = ia[i] - ib[i];
	}
	while(!m[len] && len > 1)
	len --;
	string diff;
	for(int i = len; i > 0; i--)
	diff += m[i] +  48;
	return diff;
}

我们最后介绍的就是

高精度乘法

同样,我们的原理是列竖式乘法,这个小学二年级学的东西,我们先看乘法的核心部分:

for(int i = 1; i <= a.size() ; i++)
	for(int j = 1; j <= b.size() ; j++)
	{
		m[i + j - 1] += ia[i] * ib[j];
	}

是不是很熟悉?我们这里只是把乘法竖式列出来了,还没有最后合并呢,我们合并一下:

for(int i = 1; i <= len; i++)
	if(m[i] > 9)
	{
		m[i + 1] += m[i] / 10;
		m[i] %= 10;
	}

然后就是转为string类来返回了,给出子函数的全部代码:

string _mult(string a, string b)
{
	int ia[10500] = {0}, ib[10500] = {0}, m[10500] = {0};
	for(int i = 1; i <= a.size() ; i++)
	ia[i] = a[a.size() - i] - 48;
	for(int i = 1; i <= b.size() ; i++)
	ib[i] = b[b.size() - i] - 48;
	for(int i = 1; i <= a.size() ; i++)
	for(int j = 1; j <= b.size() ; j++)
	{
		m[i + j - 1] += ia[i] * ib[j];
	}
	int len = a.size() + b.size();
	for(int i = 1; i <= len; i++)
	if(m[i] > 9)
	{
		m[i + 1] += m[i] / 10;
		m[i] %= 10;
	}
	while(!m[len] && len > 1)
	len--;
	string diff;
	for(int i = len; i > 0; i--)
	 diff += m[i] + 48;
	 return diff; 
}

高精度也讲了这样一大部分了,高精度其实就是我们的工具而已,就像是计算机一样,就是我们小学学的东西然后在计算机上模拟一下,最后,把三个高精度写在一起,纪念一下我新生赛的题目:三个高精在同一道题里面,代码真多。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
string a, b;
string _add(string a, string b)
{
	int ia[10000] = {0}, ib[10000] = {0}, sum[10000] = {0};//因为在函数里面,所以要清零一下 
	for(int i = 0; i < a.size() ; i++)// a.size()就是a的可见字符长度,与strlen的效果是一样的 
	ia[i] = a[a.size() - i - 1] - 48;
	for(int i = 0; i < b.size() ; i++)
	ib[i] = b[b.size() - i - 1] - 48;
	int len = max(a.size() , b.size() );
	for(int i = 0; i < len; i++)
	{
		sum[i] += ia[i] + ib[i];
		sum[i + 1] += sum[i] / 10;
		sum[i] %= 10;
	}
	if(sum[len])
	len++;
	string _diff;//因为我们的函数是string类型的,所以返回值也是string类型的 
	for(int i = 0; i < len; i++)
	_diff += sum[len - i - 1] + 48;
	return _diff;
}
string _minus(string a, string b)
{
	if(a.size() <= b.size() && a < b || a.size() < b.size() )
	return "-" + _minus(b, a);
	int ia[10500] = {0}, ib[10500] = {0}, m[10500] = {0};
	for(int i = 1; i <= a.size() ; i++)
	ia[i] = a[a.size() - i] - 48;
	for(int i = 1; i <= b.size() ; i++)
	ib[i] = b[b.size() - i] - 48;
	int len = a.size() ;
	for(int i = 1; i <= len ; i++)
	{
		if(ia[i] < ib[i])
		{
			ia[i] += 10;
			ia[i + 1] --;
		}
		m[i] = ia[i] - ib[i];
	}
	while(!m[len] && len > 1)
	len --;
	string diff;
	for(int i = len; i > 0; i--)
	diff += m[i] +  48;
	return diff;
}
string _mult(string a, string b)
{
	int ia[10500] = {0}, ib[10500] = {0}, m[10500] = {0};
	for(int i = 1; i <= a.size() ; i++)
	ia[i] = a[a.size() - i] - 48;
	for(int i = 1; i <= b.size() ; i++)
	ib[i] = b[b.size() - i] - 48;
	for(int i = 1; i <= a.size() ; i++)
	for(int j = 1; j <= b.size() ; j++)
	{
		m[i + j - 1] += ia[i] * ib[j];
	}
	int len = a.size() + b.size();
	for(int i = 1; i <= len; i++)
	if(m[i] > 9)
	{
		m[i + 1] += m[i] / 10;
		m[i] %= 10;
	}
	while(!m[len] && len > 1)
	len--;
	string diff;
	for(int i = len; i > 0; i--)
	 diff += m[i] + 48;
	 return diff; 
}
int main()
{
	cin >> a >> b;
	cout << _add(a, b) << endl;
	cout << _minus(a, b) << endl;
	cout << _mult(a, b);
	return 0;
}

感谢stone_juice石汁给我提供了用string类来写高精减法的思路,这里加上他高精减法的链接,高精度减法的OP写法

本文为原创,供大家一起学习交流,转载请注明出处。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!