数据结构与算法入门指南 - 高精度

959 阅读3分钟

数据结构与算法入门指南

我们都知道int可以表示-2147483648 2147483647long long可以表示-92233720368547758089223372036854775807,但要是想表达更大的数字怎么办呢?例如这个数字有一百位、一千位甚至一万位。

这个时候就需要用到高精度算法了,其实本质上是用数组将每一位都储存起来,进行加减运算的时候手动模拟运算的过程,最终得到结果。

需要注意的是:存储的时候为了方便运算一般会倒着存,例如:12345会存成{5, 4, 3, 2, 1}

加法模板

回想一下我们平时计算加法的步骤,是不是跟下面一样呢?

image-20201130112316176

写加法高精度算法的时候就是模拟上一过程,只不过由于我们存储的方式是倒着存的,所以不需要从右往左开始加,顺次加即可,记录每一次的进位情况,最后计算出结果。

P1601 A+B Problem(高精) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<bits/stdc++.h>
using namespace std;

vector<int> add(vector<int> A, vector<int> B)
{
    int t = 0; vector<int> C;
    for (int i = 0; i < A.size() || i < B.size(); i++)
    {
        if (i < A.size()) t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10); t /= 10; //进位
    }

    if (t) C.push_back(1); //如果最后还能进位,就进位

    return C;
}

int main()
{
    string a, b; cin >> a >> b;
    vector<int> A, B, C;
    for (int i = a.length() - 1; i >= 0; i--) //倒着存
        A.push_back(a[i] - '0'); //字符转数字类型,减去'0'即可。
    for (int i = b.length() - 1; i >= 0; i--)
        B.push_back(b[i] - '0');

    C = add(A, B);

    for (int i = C.size() - 1; i >= 0; i--) //倒着输出
        cout << C[i];

    return 0;
}

减法模板

减法需要注意的是,如果是小数a减大数b,那么会得到负数,我们会先用大数b减去小数a,再补上负号即可。

P2142 高精度减法 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<bits/stdc++.h>
using namespace std;

bool cmp(vector<int> &A, vector<int> &B) //用来判断A与B谁大
{
    if (A.size() != B.size()) return A.size() > B.size();
    for (int i = A.size(); i >= 0; i--)
        if (A[i] != B[i])
            return A[i] > B[i];
    return true;
}


vector<int> sub(vector<int> &A, vector<int> &B)
{
    if (!cmp(A, B)) return sub(B, A); //如果是小数减大数就返回大数减小数的结果
    int t = 0; vector<int> C;
    for (int i = 0; i < A.size() || i < B.size(); i++)
    {
        t = A[i] - t;
        if (i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);
        t = (t < 0 ? 1 : 0);
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}

int main()
{
    string a, b; cin >> a >> b;
    vector<int> A, B, C;
    for (int i = a.length() - 1; i >= 0; i--)
        A.push_back(a[i] - '0');
    for (int i = b.length() - 1; i >= 0; i--)
        B.push_back(b[i] - '0');

    C = sub(A, B);
    if (!cmp(A, B)) cout << '-'; //如果是小数减大数,应该补上负号。

    for (int i = C.size() - 1; i >= 0; i--)
        cout << C[i];

    return 0;
}

乘法模板

高精度乘int

与上述加法模板差不多。

vector<int> mul(vector<int> &A, int b)
{
    vector<int> C; int t = 0;
    for (int i = 0; i < A.size() || t; i++)
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10); t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back(); //去除前置0

    return C;
}

高精度乘高精度

再开一个C数组存下最终处理出来的结果,把中间的每次乘法算出来的结果直接加到最终结果上,再对最终结果进行进位处理。

image-20201130111739916

P1303 A*B Problem - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

vector<int> mul(vector<int> &A, vector<int> &B)
{
    int len = A.size() + B.size(); vector<int> C(len);

    for (int i = 0; i < A.size(); i++)
        for (int j = 0; j < B.size(); j++)
            C[i + j] += A[i] * B[j]; //把每次乘的结果直接累加到最终结果上

    for (int i = 0; i < len; i++)
        if (C[i] > 9) //能进位就进位
        {
            C[i + 1] += C[i] / 10;
            C[i] %= 10;
        }

    while (C.size() > 1 && C.back() == 0) C.pop_back(); //去除前置0

    return C;
}

这两个模板的用法具体要看题目的要求选用。

除法模板

除法不同的是,不是无限的去计算后续位数,而是返回商和余数。

高精度除int,函数返回值为商,r为传出的余数。

vector<int> div(vector<int> &A, int b, int &r)
{
    vector<int> C; r = 0;
    //回想一下除法,我们运算的时候是顺次的,由于这里是倒着存,所以也要倒着遍历。
    for (int i = A.size() - 1; i >= 0; i--) 
    {
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }

    reverse(C.begin(), C.end()); //翻转数组
    while (C.size() > 1 && C.back() == 0) C.pop_back(); //去前置0

    return C;
}

题目

待增加

P1009 阶乘之和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)