c ++中大数字的模乘

时间:2014-01-07 12:40:26

标签: c++ algorithm multiplication modular

我有三个整数 A,B (小于10 ^ 12)和 C (小于10 ^ 15)。我想计算(A * B)%C 。我知道

(A * B) % C = ((A % C) * (B % C)) % C

但是如果 A = B = 10 ^ 11 ,那么上面的表达式将导致整数溢出。对于上述情况是否有任何简单的解决方案,或者我必须使用快速乘法算法。

如果我必须使用快速乘法算法,那么我应该使用哪种算法。

编辑:我在C++尝试了上述问题(这不会导致溢出,不确定原因),但答案不应该是

提前致谢。

5 个答案:

答案 0 :(得分:14)

您可以使用 Schrage的方法解决此问题。这样,您就可以将两个带有特定模数a带符号数字zm相乘,而不会生成大于该值的中间数。

它基于模数的近似因子分解m

m = aq + r 

q = [m / a]

r = m mod a

其中[]表示整数部分。如果r < q0 < z < m − 1,则a(z mod q)r[z / q]都位于0,...,m − 1

范围内
az mod m = a(z mod q) − r[z / q]

如果这是否定的,请添加m

[这种技术经常用于线性同余随机数发生器]。

答案 1 :(得分:5)

鉴于您的公式和以下变体:

(A + B) mod C = ((A mod C) + (B mod C)) mod C 

您可以使用分而治之的方法来开发既简单又快速的算法:

#include <iostream>

long bigMod(long  a, long  b, long c) {
    if (a == 0 || b == 0) {
        return 0;
    }
    if (a == 1) {
        return b;
    }
    if (b == 1) {
        return a;
    } 

    // Returns: (a * b/2) mod c
    long a2 = bigMod(a, b / 2, c);

    // Even factor
    if ((b & 1) == 0) {
        // [((a * b/2) mod c) + ((a * b/2) mod c)] mod c
        return (a2 + a2) % c;
    } else {
        // Odd exponent
        // [(a mod c) + ((a * b/2) mod c) + ((a * b/2) mod c)] mod c
        return ((a % c) + (a2 + a2)) % c;
    }
}

int main() { 
    // Use the min(a, b) as the second parameter
    // This prints: 27
    std::cout << bigMod(64545, 58971, 144) << std::endl;
    return 0;
}

哪个是O(log N)

答案 2 :(得分:3)

更新:设置了a % c的高位时修复了错误。 (帽子提示:Kevin Hopps)

如果您正在寻找简单而不是快速,那么您可以使用以下内容:

typedef unsigned long long u64;

u64 multiplyModulo(u64 a, u64 b, u64 c)
{
    u64 result = 0;
    a %= c;
    b %= c;
    while(b) {
        if(b & 0x1) {
            result += a;
            result %= c;
        }
        b >>= 1;
        if(a < c - a) {
            a <<= 1;
        } else {
            a -= (c - a);
        }
    }
    return result;
}

答案 3 :(得分:0)

很抱歉,当变量&#34; a&#34;时,godel9算法会产生错误的结果。保存具有高位设置的值。这是因为&#34; a&lt;&lt; = 1&#34;丢失信息。这是一个修正的算法,适用于任何整数类型,有符号或无符号。

&#13;
&#13;
template <typename IntType>
IntType add(IntType a, IntType b, IntType c)
    {
    assert(c > 0 && 0 <= a && a < c && 0 <= b && b < c);
    IntType room = (c - 1) - a;
    if (b <= room)
        a += b;
    else
        a = b - room - 1;
    return a;
    }

template <typename IntType>
IntType mod(IntType a, IntType c)
    {
    assert(c > 0);
    IntType q = a / c; // q may be negative
    a -= q * c; // now -c < a && a < c
    if (a < 0)
        a += c;
    return a;
    }

template <typename IntType>
IntType multiplyModulo(IntType a, IntType b, IntType c)
    {
    IntType result = 0;
    a = mod(a, c);
    b = mod(b, c);
    if (b > a)
        std::swap(a, b);
    while (b)
        {
        if (b & 0x1)
            result = add(result, a, c);
        a = add(a, a, c);
        b >>= 1;
        }
    return result;
    }
&#13;
&#13;
&#13;

答案 4 :(得分:0)

在这种情况下,A和B是40位数,C是50位数,这在64位模式下不是问题,如果你有一个内在的或者可以写汇编代码来使用64位的64位乘法产生128位结果(乘积实际为80位),之后将128位被除数除以50位除数,产生50位余数(模数)。

取决于处理器,通过乘以81位(或更小)常数来实现除以50位常数可能更快。假设64位处理器,它将需要4次乘法和一些加法,然后移位4乘法乘积的高位以产生商。然后乘法乘以50位模数和减法(从80位乘积)用于产生50位余数。