(a * b)%m分割操作数

时间:2015-08-02 17:10:37

标签: c++ algorithm

我正在表演(a * b)%m,但所有a,b和m的顺序为10 ^ 18。

不能将a * b乘以或乘以(a%m * b%m),因为m也是10 ^ 18的阶数。

我认为没有必要将它们转换为字符串然后将它们作为字符串相乘,取出mod然后返回long long int。

我遇到了this问题,接受的解决办法是分割我的操作数。 (请参阅链接的帖子)。但是我并不理解他提到的位移的解释。

我写了一个函数来计算a和b modulo mod的乘积。

/*
   a=a1+k*a2
   b=b1+k*b2
   (a1+k*a2)*(b1+k*b2) % c = a1*b1 % c + k*a1*b2 % c + k*a2*b1 % c + k*k*a2*b2 % c
*/

ull MAM(ull a,ull b,ull mod)//multiply and mod;ull: unsigned long long
{
   ull a1,a2,b1,b2;
   ull k=4294967296; //2^32
   a1=a%k;
   a2=a/k;
   b1=b%k;
   b2=b/k;
   ull ans = (a1*b1)%mod + (((a1*k)%mod)*b2) %mod + (((k*a2)%mod)*b1)%mod + (((k*k)%mod)*((a2*b2)%mod))%mod;
   return ans;
}

但如果没有这种转变,这将无法奏效。有人请解释答案正在谈论的位移。

4 个答案:

答案 0 :(得分:2)

使用:

(((a1 * k) % mod) * b2) % mod

mod可能大于2 ** 32,因此(((a1 * k) % mod) * b2)可能会溢出。

k == 2 ** 32因此(k * a1 * b2) % mod((a1 * b2) % mod) * (k % mod) % mod(外部乘法可能仍会溢出,因此在k中拆分2*2*...*2

所以

(((((a1 * b2) % mod) * 2) % mod) * 2) % mod)...
     ^^^^^^^^^^^^^^
     named x
  • 如果x < 2 ** 63,则2 * x不会溢出,我们可以进行迭代(2 * x) % mod
  • 如果x >= 2 ** 63然后2*x溢出,我们就会2 ** 63 <= mod(我们有x < 2 ** 64),因此(2 * x) % mod可以计算为2 * x - mod }或写成没有溢出x - (mod - x)

所以代码变成了

const std::uint64 limit = 0x1000000000000000;
std::uint64_t x = (a1 * b2) % mod;
for (int i = 0; i != 32; ++i) {
    if (x < limit) {
        x = (2 * x) % mod; // No overflow
    } else {
        x -= mod - x;      // Manage overflow
    }
}

同样适用于a2 * b1 * ka2 * b2 * k * k

我希望现在更清楚了。

答案 1 :(得分:1)

与此相当的位移是a1 = a&amp; 0xffffffffull; a2 = a&gt;&gt; 32;或a1 =(a <&lt; 32)&gt; 32; a2 = a&gt;&gt; 32;

然而,示例代码存在问题:k * k = 0(溢出),第2和第3项:(((a1 * k)%mod)* b2)和(((a2 * k) %mod)* b1)也可以溢出(mod可能是2 ^ 64-1)。似乎乘法的实现方式类似于之前发布的答案之一,但在这种情况下,不需要拆分操作数。

uint64_t mulmod(uint64_t a, uint64_t b, uint64_t m) {
    uint64_t res = 0;
    uint64_t temp_b;

    if (a >= m)
        a %= m;
    if (b >= m)
        b %= m;
    while (a != 0) {
        if (a & 1) {
            if (b >= m - res) /* Equiv to if (res + b >= m), without overflow */
                res -= m;
            res += b;
        }
        a >>= 1;
        /* Double b, modulo m */
        temp_b = b;
        if (b >= m - b)       /* Equiv to if (2 * b >= m), without overflow */
            temp_b -= m;
        b += temp_b;
    }
    return res;
}

答案 2 :(得分:0)

让我解释A * B%k -

    Let us assume A = a1a2a3.......an 
    and           B = b1b2b3.......bn
    where ai & bi are numeric digits

    Then 
A*B%k = A*(b1*(pow(2,n-1))%k + A*(b2*(pow(2,n-2))%k + .......... A*(bn*(pow(2,n-n)%k.
OR 
A*B%k = B*(a1*(pow(2,n-1))%k + B*(a2*(pow(2,n-2))%k + .......... B*(an*(pow(2,n-n)%k.

在模数之前,右侧的每个术语都不会超过2 ^ 63。所以这样做是安全的,不会有任何溢出

答案 3 :(得分:0)

ah = a >> 1al = a & 1,然后a = (ah << 1) + al = 2*ah + al,{ {1}} = a*b = 2*ah*b + al*b

因此,我们可以递归计算ah*b + ah*b + al*b,然后将每个后续a*b % mod右移一,直到a为零:

a

这里我们只需要处理加法模ull mulMod(ull a, ull b, ull mod) { // assuming a < mod and b < mod if (a == 0) return 0; ull ah = a >> 1; ull al = a & 1; ull ahb = mulMod(ah, b, mod); ull ahb2 = ahb < mod - ahb ? ahb + ahb : ahb - (mod - ahb); ull alb = al * b; return alb < mod - ahb2 ? ahb2 + alb : ahb2 - (mod - alb); } 。如果我们注意到mod只有(x + y) % modx + yx < mod - y,我们可以避免溢出。