如果模数接近您可以使用的最大整数类型的限制,事情就会变得有些复杂。如果您不能使用实现大整数运算的库,您可以通过将因子拆分为低阶和高阶部分来自己滚动模乘法。
如果模数m
大到2*(m-1)
溢出,事情就变得很麻烦,但如果2*(m-1)
不溢出,那是可以忍受的。
让我们假设您拥有并使用 64 位无符号整数类型。
您可以通过将因子拆分为低 32 位和高 32 位来计算模积,然后将积拆分为
a = a1 + (a2 << 32) // 0 <= a1, a2 < (1 << 32)
b = b1 + (b2 << 32) // 0 <= b1, b2 < (1 << 32)
a*b = a1*b1 + (a1*b2 << 32) + (a2*b1 << 32) + (a2*b2 << 64)
计算a*b (mod m)
,m <= (1 << 63)
减少四个产品中的每一个模m
,
p1 = (a1*b1) % m;
p2 = (a1*b2) % m;
p3 = (a2*b1) % m;
p4 = (a2*b2) % m;
合并这些变化的最简单方法是
for(i = 0; i < 32; ++i) {
p2 *= 2;
if (p2 >= m) p2 -= m;
}
p3
和 64 次迭代相同p4
。然后
s = p1+p2;
if (s >= m) s -= m;
s += p3;
if (s >= m) s -= m;
s += p4;
if (s >= m) s -= m;
return s;
这种方式不是很快,但是对于这里需要的少数乘法,它可能已经足够快了。应通过减少班次数来获得小幅加速;首先计算(p4 << 32) % m
,
for(i = 0; i < 32; ++i) {
p4 *= 2;
if (p4 >= m) p4 -= m;
}
那么所有的p2
,p3
的当前值p4
需要乘以 2 32模m
,
p4 += p3;
if (p4 >= m) p4 -= m;
p4 += p2;
if (p4 >= m) p4 -= m;
for(i = 0; i < 32; ++i) {
p4 *= 2;
if (p4 >= m) p4 -= m;
}
s = p4+p1;
if (s >= m) s -= m;
return s;