我有三个整数 A,B (小于10 ^ 12)和 C (小于10 ^ 15)。我想计算(A * B)%C 。我知道
(A * B) % C = ((A % C) * (B % C)) % C
但是如果 A = B = 10 ^ 11 ,那么上面的表达式将导致整数溢出。对于上述情况是否有任何简单的解决方案,或者我必须使用快速乘法算法。
如果我必须使用快速乘法算法,那么我应该使用哪种算法。
编辑:我在C++尝试了上述问题(这不会导致溢出,不确定原因),但答案不应该是零
提前致谢。
答案 0 :(得分:14)
您可以使用 Schrage的方法解决此问题。这样,您就可以将两个带有特定模数a
的带符号数字z
和m
相乘,而不会生成大于该值的中间数。
它基于模数的近似因子分解m
,
m = aq + r
即
q = [m / a]
和
r = m mod a
其中[]
表示整数部分。如果r < q
和0 < 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;丢失信息。这是一个修正的算法,适用于任何整数类型,有符号或无符号。
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;
答案 4 :(得分:0)
在这种情况下,A和B是40位数,C是50位数,这在64位模式下不是问题,如果你有一个内在的或者可以写汇编代码来使用64位的64位乘法产生128位结果(乘积实际为80位),之后将128位被除数除以50位除数,产生50位余数(模数)。
取决于处理器,通过乘以81位(或更小)常数来实现除以50位常数可能更快。假设64位处理器,它将需要4次乘法和一些加法,然后移位4乘法乘积的高位以产生商。然后乘法乘以50位模数和减法(从80位乘积)用于产生50位余数。