高精度大数的加减乘除算法解析

高精度加减乘除算法

什么是高精度?

高精度算法(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 普及组] 阶乘之和题目解析

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值