我正在表演(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;
}
但如果没有这种转变,这将无法奏效。有人请解释答案正在谈论的位移。
答案 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 * k
和a2 * 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 >> 1
,al
= 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) % mod
,x + y
,x < mod - y
,我们可以避免溢出。