高精度算法
什么是高精度算法?
在我们的计算中,我们可能有长达几百位或者更多的数字进行加减乘除运算,我们在这里把这些整数称为高精度数,高精度算法就是用计算机对这些超大数据进行模拟加减乘除等运算。
高精度算法有什么用?
我们学完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写法
本文为原创,供大家一起学习交流,转载请注明出处。
来源:CSDN
作者:Linda_yezi_coder
链接:https://blog.csdn.net/yezi_coder/article/details/103643783