高精度加减乘除算法
什么是高精度?
高精度算法(High Accuracy Algorithm)是处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。
一般情况下我们会用一段连续空间,如线性表,顺序表,数组等来拆分存储大数的每一位。如4578可以在数组array里拆分成array[0] = 8,array[1] = 7,array[2] = 5,array[3] = 4来存储,至于在数组array[0]上放高位还是低位要取决于代码的编写者和环境需求,这并不是固定的。
高精度加法
A+B=C(A和B都是高精度)
模拟加法的前提是我们得知道加法是如何计算的。
那么7145+129就是
C0=(5+9+0)%10=4
t=(5+9+0)/10=1
C1=(4+2+1)%10=7
t=(4+2+1)/10=0
C2=(1+1+0)%10=2
t=(1+1+0)/10=0
C3=(7+0+0)%10=7
t=(7+0+0)/10=0
代码模拟上述步骤
样例代码(C++)
在这里插入代vector<int> add(vector<int>& a, vector<int>& b)
{
vector<int> c;
int temp = 0; //存储进位
//a,b里只要有字符那么就得计数,进位有效时也需要进行一次循环否则会被舍弃进位导致缺少最高位
for (int i = 0; i < a.size() || i < b.size() || temp; i++)
{
if (i < a.size())
temp += a[i];
if (i < b.size())
temp += b[i];
//必须取余,例如9+8 = 17,个位放17%10=7
c.push_back(temp % 10);
//考虑进位 17/10 = 1
temp /= 10;
}
//上面的例子9+8后循环直接结束如果temp非零得加入所有循环在temp非零时还得走
return c;
}码片
高精度减法
A-B=C(A和B都是高精度)
模拟减法的前提也是我们得知道加法是如何计算的。
C0=(5-9-0+10)%10=6
因为(5-9-0)<0所以t=1
C1=(4-2-1+10)%10=1
因为(4-2-1)>0所以t=0
C2=(1-1-0+10)%10=0
因为(1-1-0)>0所以t=0
C3=(7-0-0+10)%10=7
因为(7-0-0)>0所以t=0
到这里就会出现一个问题,那就是如果是A<B怎么办?会产生负数,这该怎么办,众所周知,产生负数就是A不够减去B所以欠下的债,也就是B比A大多少那么A当被B减去的时候就会欠下多少,所以当B大于A的时候从B减去A然后加个负号即可。
代码模拟上述规则
样例代码(C++)
#include <iostream>
#include <vector>
#include <utility>
using namespace std;
pair<char, vector<int>> sub(vector<int>& A, vector<int>& B)
{
//存储负号和数字
pair<char, vector<int>> t;
bool flag = false; //标记是否是负数
if (A.size() < B.size() || A.back() < B.back())
{
//负数标记
flag = true;
//互换减数和被减数
A.swap(B);
}
vector<int> C;
for (int i = 0, t = 0; i < A.size(); i++)
{
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);//若t<0,借位加10,取余10不影响,若>=0,加10再余10还是t
if (t < 0) t = 1; //小于0时需要借位
else t = 0;
}
while (C.size() > 1 && C.back() == 0)
C.pop_back();
//去多余的0,例如100-97=3,防止输出03
//是否有负号
if (flag)
t.first = '-';
else
t.first = '+';
t.second = C;
return t;
}
int main()
{
vector<int> a, b, t;
pair<char, vector<int>> c;
//初始化
//a是7145,b是129
a.push_back(5);
a.push_back(4);
a.push_back(1);
a.push_back(7);
b.push_back(9);
b.push_back(2);
b.push_back(1);
//b.push_back(8); //解开这句可以测试负数运算
c = sub(a, b);
t = c.second;
//因为数字高位在容器的低位所以逆序输出
//如果有负号那么打印出来
if (c.first == '-')
cout << c.first;
for (auto it = t.rbegin(); it != t.rend(); it++)
cout << *it;
return 0;
}
高精度乘法
A*b=C,A是高精度的,b是低精度的(位数小,可以用int)
当然模拟乘法的前提还是得知道乘法是如何进行的
C0=(5x5+0)%10=5
t=(5x5+0)/10=2
C1=(2x5+2)%10=2
t=(2x5+2)/10=1
C2=(1x5+1)%10=1
t=(1x5+1)/10=0
#include <iostream>
#include <vector>
using namespace std;
//C=A*b
vector<int> mul(vector<int>& A, int b)
{
vector<int> C;
int t = 0;//初始化时进位为0
//注意A有位数时循环进行进位数t有效时循环也需要进行否则会出错,会缺少最高位
for (int i = 0; i < A.size() || t; i++)
{
if (i < A.size()) t += A[i] * b;//将b与A[i]相乘,A[i]的每一位和b进行相乘
C.push_back(t % 10); //相乘后只取最后一位的数
t /= 10;//考虑进位
}
return C;
}
int main()
{
vector<int> a, c;
int b;
//初始化
//a是125,b是5
a.push_back(5);
a.push_back(2);
a.push_back(1);
b = 5;
c = mul(a, b);
//因为数字高位在容器的低位所以逆序输出
for (auto it = c.rbegin(); it != c.rend(); it++)
cout << *it;
return 0;
}
高精度除法
A/b=C,A是高精度的,b是低精度的(位数小,可以用int)
我们在分析中余数是相减出来的比如图中26-25=1余1但是在代码中我可以直接用取余(%)来求余即可。
用的代码实现上述模拟
样例代码(C++)
#include <iostream>
#include <vector>
using namespace std;
//A/b,商是C,余数是r
vector<int> div(vector<int>& A, int b)
{
vector<int> C;
int r = 0;
for (int i = A.size() - 1; i >= 0; i--)
{
r = r * 10 + A[i];
C.push_back(r / b);
//求余
r %= b;
}
//因为除法是从高位开始的所以有没有前置0得看提前前面有没有0
while (C.size() > 1 && C.front() == 0)
C.erase(C.begin()); //擦除最前面的一位
//去掉多余的0,比如15/3得到的是5,而不是05
return C;
}
int main()
{
vector<int> a, c;
int b;
//初始化
//a是125,b是5
a.push_back(5);
a.push_back(2);
a.push_back(1);
b = 5;
c = div(a, b);
//因为除法的高位也在容器的高位所以这次没必要逆序输出
for (auto it = c.begin(); it != c.end(); it++)
cout << *it;
return 0;
}
实际应用
题目:【入门3】循环结构 P1009 [NOIP1998 普及组] 阶乘之和
解析:【入门3】循环结构 P1009 [NOIP1998 普及组] 阶乘之和题目解析