【问题标题】:How does int(or long long) overflow in c++ affect modulus?c ++中的int(或long long)溢出如何影响模数?
【发布时间】:2015-06-14 20:50:01
【问题描述】:

假设我有两个 long long,a 和 b,我需要相乘,然后得到一些大 k 的值 mod k,这样 a、b 和 k 都在 long long 的范围内,而不是诠释。为简单起见,a, b

因此代码将是:

long long a, b, k;
cin >> a >> b >> k;
cout << (a * b)%k << "\n";

但是,由于 a 和 b 如此之大,如果你像上面那样相乘,它会溢出并变成负数,那么 mod k 将是一个负数并且不正确。

如何确保值 mod k 是正确的?

编辑:作为奖励,这在 Java 中是如何工作的?和预期的一样吗?还是需要 BigInteger?

【问题讨论】:

  • a,b
  • 尝试使用 (a * b) % k == ((a % k) * (b % k)) % k 的事实。如果 k 小于 long 这将起作用。如果没有,您将需要做一些简单的多精度。
  • 从技术上讲,当您使用有符号值时,“溢出”之后的所有内容都是未定义的行为;任何事情都可能发生,
  • @RichardCritten,请在回答之前阅读问题和 cmets。 nneonneo 已经在您的正上方的评论中指出a,b

标签: c++ long-integer modulus integer-overflow


【解决方案1】:

许多编译器都提供 128 位整数类型。例如,g++ 可以创建一个函数

static inline int64_t mulmod(int64_t x, int64_t y, int64_t m)
{
    return ( (__int128_t)x * y) % m;
}

另外:如果可以的话,在进行模运算时尽量坚持使用无符号整数类型。整数除法的舍入行为使得在涉及有符号值时使用% 非常尴尬。

【讨论】:

    【解决方案2】:

    如果您知道这些值小于ULONGLONG_MAX/2(因此加法不会溢出),您可以一次乘一位:

    unsigned long long mulmod(unsigned long long a, unsigned long unsigned long b, long long m) {
        unsigned long long rv = 0;
        a %= m;
        b %= m;
        while (b) {
            if (b&1) { rv += a; if (rv >= m) rv -= m; }
            a += a; if (a >= m) a -= m;
            b >>= 1; }
        return rv; }
    

    如果你知道你在 gcc/x86_64 上运行,你可以试试:

    unsigned long mulmod(unsigned long a, unsigned long b, unsigned long m) {
        unsigned long rv;
        asm ("mulq %2; divq %3" : "=d"(rv), "+a"(a): "S"(b), "c"(m));
        return rv;
    }
    

    最高可达ULONG_MAX

    如果你的数字变得更大,你需要去一个多精度库,比如GMP

    【讨论】:

    • 一次一位是否意味着一个额外的 log(n) 乘法因子?
    • 由于这里可能的最大大小是 63 或 127 位(取决于您的机器上 long long 的大小),渐近性能实际上并不适用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 2023-03-15
    • 1970-01-01
    • 2018-12-01
    相关资源
    最近更新 更多