📜  在 cpp 中将两个数字相乘 <=10^18 %m - C++ (1)

📅  最后修改于: 2023-12-03 15:37:19.190000             🧑  作者: Mango

在 cpp 中将两个数字相乘 <=10^18 %m - C++

在 C++ 中,当需要将两个非常大的数字相乘时,可能会导致溢出或精度丢失的问题。因此,需要使用高精度计算来解决这个问题。

同时,当结果较大时,需要取模运算以避免超出数据类型的范围。本文将介绍如何在 C++ 中实现高精度计算和取模运算。

1. 高精度计算

高精度计算的主要思路是将每一位上的数字分别存储,然后通过模拟手算的过程进行计算。

以下是实现高精度计算的基本框架:

const int MAXN = 10005; // 最大位数
struct BigNumber {
    int num[MAXN];       // 数字数组
    int len;             // 数字长度

    BigNumber() {        // 构造函数
        memset(num, 0, sizeof(num));
        len = 1;
    }

    BigNumber(int x) {   // 构造函数
        memset(num, 0, sizeof(num));
        len = 0;
        while (x) {
            num[len++] = x % 10;
            x /= 10;
        }
        if (len == 0) len = 1;
    }

    void print() {       // 输出函数
        for (int i = len - 1; i >= 0; i--)
            printf("%d", num[i]);
    }
};

其中,num 数组用于存储每一位上的数字,len 表示数字的长度。构造函数用于将一个普通的整数转换为高精度数字。输出函数用于将高精度数字转换为普通的整数进行输出。

以下是实现高精度数字的加减乘除的代码示例:

// 高精度加法
BigNumber operator+(const BigNumber& A, const BigNumber& B) {
    BigNumber C;
    C.len = 0;
    for (int i = 0, g = 0; g || i < max(A.len, B.len); i++) {
        int x = g;
        if (i < A.len) x += A.num[i];
        if (i < B.len) x += B.num[i];
        C.num[C.len++] = x % 10;
        g = x / 10;
    }
    return C;
}

// 高精度减法
BigNumber operator-(const BigNumber& A, const BigNumber& B) {
    BigNumber C;
    C.len = 0;
    for (int i = 0, g = 0; i < A.len; i++) {
        int x = A.num[i] - g;
        if (i < B.len) x -= B.num[i];
        if (x >= 0) {
            g = 0;
        } else {
            g = 1;
            x += 10;
        }
        C.num[C.len++] = x;
    }
    while (C.len > 1 && C.num[C.len - 1] == 0)
        C.len--;
    return C;
}

// 高精度乘法
BigNumber operator*(const BigNumber& A, const int& b) {
    BigNumber C;
    C.len = 0;
    int t = 0;
    for (int i = 0; i < A.len || t; i++) {
        int x = t;
        if (i < A.len) x += A.num[i] * b;
        C.num[C.len++] = x % 10;
        t = x / 10;
    }
    return C;
}

// 高精度除法
BigNumber operator/(const BigNumber& A, const int& b) {
    BigNumber C;
    C.len = A.len;
    int r = 0;
    for (int i = A.len - 1; i >= 0; i--) {
        r = r * 10 + A.num[i];
        C.num[i] = r / b;
        r %= b;
    }
    while (C.len > 1 && C.num[C.len - 1] == 0)
        C.len--;
    return C;
}
2. 取模运算

当两个数字的乘积较大时,为了避免超出数据类型的范围,需要使用取模运算将结果缩小。以下是常见的取模方法:

2.1. 直接取模法

将每一步的结果都进行取模操作,最后再对整个结果进行一次取模运算。

const int MOD = 998244353;

// 高精度乘法取模
BigNumber operator*(const BigNumber& A, const BigNumber& B) {
    BigNumber C;
    C.len = A.len + B.len;

    for (int i = 0; i < A.len; i++) {
        int t = 0;
        for (int j = 0; j < B.len; j++) {
            t += A.num[i] * B.num[j] + C.num[i + j];
            C.num[i + j] = t % 10;
            t /= 10;
        }
        C.num[i + B.len] += t;
    }

    while (C.len > 1 && C.num[C.len - 1] == 0)
        C.len--;

    return C % MOD;
}
2.2. 模数分解法

将模数拆分成多个质数的乘积,分别对每个质数进行取模计算,最后再将结果合并。

// 高精度乘法取模(模数分解法)
const int P[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
const int M = sizeof(P) / sizeof(int);

BigNumber mod(const BigNumber& A, const int& p) {
    BigNumber C;
    C.len = 0;
    int r = 0;
    for (int i = A.len - 1; i >= 0; i--) {
        r = r * 10 + A.num[i];
        C.num[i] = r / p;
        r %= p;
    }
    while (C.len > 1 && C.num[C.len - 1] == 0)
        C.len--;
    return C;
}

BigNumber operator%(const BigNumber& A, const int& m) {
    BigNumber ans;
    for (int i = 0; i < M; i++) {
        if (m % P[i] == 0) {
            ans = (ans + mod(A, P[i])) % m;
        }
    }
    return ans;
}

BigNumber operator*(const BigNumber& A, const BigNumber& B, const int& M) {
    BigNumber C;
    C.len = A.len + B.len;

    for (int i = 0; i < A.len; i++) {
        int t = 0;
        for (int j = 0; j < B.len; j++) {
            t += A.num[i] * B.num[j] + C.num[i + j];
            C.num[i + j] = t % 10;
            t /= 10;
        }
        C.num[i + B.len] += t;
    }

    while (C.len > 1 && C.num[C.len - 1] == 0)
        C.len--;

    return C % M;
}
3. 示例代码

以下是将两个数字相乘并取模的示例代码:

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

const int MAXN = 10005;

struct BigNumber {
    int num[MAXN];
    int len;

    BigNumber() {
        memset(num, 0, sizeof(num));
        len = 1;
    }

    BigNumber(int x) {
        memset(num, 0, sizeof(num));
        len = 0;
        while (x) {
            num[len++] = x % 10;
            x /= 10;
        }
        if (len == 0) len = 1;
    }

    void print() {
        for (int i = len - 1; i >= 0; i--)
            printf("%d", num[i]);
    }
};

BigNumber operator+(const BigNumber& A, const BigNumber& B) {
    BigNumber C;
    C.len = 0;
    for (int i = 0, g = 0; g || i < max(A.len, B.len); i++) {
        int x = g;
        if (i < A.len) x += A.num[i];
        if (i < B.len) x += B.num[i];
        C.num[C.len++] = x % 10;
        g = x / 10;
    }
    return C;
}

BigNumber operator-(const BigNumber& A, const BigNumber& B) {
    BigNumber C;
    C.len = 0;
    for (int i = 0, g = 0; i < A.len; i++) {
        int x = A.num[i] - g;
        if (i < B.len) x -= B.num[i];
        if (x >= 0) {
            g = 0;
        } else {
            g = 1;
            x += 10;
        }
        C.num[C.len++] = x;
    }
    while (C.len > 1 && C.num[C.len - 1] == 0)
        C.len--;
    return C;
}

BigNumber operator*(const BigNumber& A, const int& b) {
    BigNumber C;
    C.len = 0;
    int t = 0;
    for (int i = 0; i < A.len || t; i++) {
        int x = t;
        if (i < A.len) x += A.num[i] * b;
        C.num[C.len++] = x % 10;
        t = x / 10;
    }
    return C;
}

BigNumber operator/(const BigNumber& A, const int& b) {
    BigNumber C;
    C.len = A.len;
    int r = 0;
    for (int i = A.len - 1; i >= 0; i--) {
        r = r * 10 + A.num[i];
        C.num[i] = r / b;
        r %= b;
    }
    while (C.len > 1 && C.num[C.len - 1] == 0)
        C.len--;
    return C;
}

const int MOD = 998244353;

BigNumber operator%(const BigNumber& A, const int& m) {
    BigNumber ans;
    for (int i = 2; i <= sqrt(m); i++) {
        int p = 1;
        while (m % i == 0) {
            m /= i;
            p *= i;
        }
        if (p > 1) ans = ans + (A % p) * (MOD / p) % MOD;
    }
    if (m > 1) ans = ans + A % m * (MOD / m) % MOD;
    return ans % MOD;
}

BigNumber operator*(const BigNumber& A, const BigNumber& B) {
    BigNumber C;
    C.len = A.len + B.len;

    for (int i = 0; i < A.len; i++) {
        int t = 0;
        for (int j = 0; j < B.len; j++) {
            t += A.num[i] * B.num[j] + C.num[i + j];
            C.num[i + j] = t % 10;
            t /= 10;
        }
        C.num[i + B.len] += t;
    }

    while (C.len > 1 && C.num[C.len - 1] == 0)
        C.len--;

    return C % MOD;
}

int main() {
    int a, b;
    cin >> a >> b;
    BigNumber A(a), B(b);
    (A * B).print();
    return 0;
}
4. 总结

本文介绍了如何在 C++ 中实现高精度计算和取模运算。使用高精度计算和取模运算可以解决两个非常大的数字相乘的问题,并且保证了精度和速度的性能。