【问题标题】:How to avoid overflow in fast modular exponentiation如何避免快速模幂运算中的溢出
【发布时间】:2013-07-29 10:00:57
【问题描述】:

我正在尝试解决关于 SPOJ 的一个需要模幂运算的问题。我正在使用以下 C 代码

long long modpow(long long a,long long b,long long mod)
{
    long long product,pseq;
    product=1
    pseq=a%mod;
    while(b>0)
    {
        if(b&1)
            product=(product*pseq)%mod;
        pseq=(pseq*pseq)%mod;
        b>>=1
    }
    return product;
}

问题是当我想计算(2^249999999997)%999999999989时,由于溢出,它给出了答案0。如何避免溢出?

【问题讨论】:

  • 除非你彻底改变实现,否则我认为这是无法避免的。
  • 更好地使用大数字库,如 gmp libmp...
  • int64*int64 的结果需要 int128
  • 请参阅math.stackexchange.com/questions/91583/…,了解计算大幂余数背后的数学原理。
  • @mahk:计算 2 到 249999999997 需要一个 249999999997 位整数,即 29 GB 整数。我不认为一个大数字图书馆会有所帮助。

标签: c c99 integer-overflow


【解决方案1】:

Untestet,但你明白了。只要2*mod 小于最大可表示的long long 值并且a、b 和mod 为正值,这应该可以工作。

long long modpow(long long a,long long b,long long mod)
{
    long long product,pseq;
    product=1;
    pseq=a%mod;
    while(b>0)
    {
        if(b&1)
            product=modmult(product,pseq,mod);
        pseq=modmult(pseq,pseq,mod);
        b>>=1;
    }
    return product;
}

long long modmult(long long a,long long b,long long mod)
{
    if (a == 0 || b < mod / a)
        return (a*b)%mod;
    long long sum;
    sum = 0;
    while(b>0)
    {
        if(b&1)
            sum = (sum + a) % mod;
        a = (2*a) % mod;
        b>>=1;
    }
    return sum;
}

【讨论】:

  • +1 我对此进行了测试,结果与 Python 中的 pow(2,249999999997, 999999999989) 相同。
  • 我怀疑这适用于某些值 - 例如modmult(3, LLONG_MAX, LLONG_MAX);(a = (2*a) % mod; 中的 2*a 溢出导致未定义的行为并可能导致意外否定)。
【解决方案2】:

另一个建议是利用 999999999989 是素数这一事实。 通过使用关系式 (a^n) = a % n(其中 n 是素数),您可以简化运算。

【讨论】:

    【解决方案3】:

    你可以用unsigned long long代替long long,它会帮助你玩更高的值,范围是0到18,446,744,073,709,551,615。

    【讨论】:

    • GCC 支持 __uint128_t。 (128位无符号整数)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-12
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    • 2020-12-06
    相关资源
    最近更新 更多