我需要计算a*a
mod n
,但a
相当大,当我对它进行调整时会导致溢出。执行((a % n)*(a % n)) % n
不起作用,因为(n-1) 2 可能会溢出。这是用C ++编写的,我正在使用int64_t
示例值:a = 821037907258,n = 800000000000,如果你将其平方后会溢出。
我正在使用DevCPP,我已经尝试过让大整数库工作无效。
不,这些数字没有模式。
答案 0 :(得分:15)
如果您不能使用大整数库,并且没有原生uint128_t
(或类似),则需要手动执行此操作。
一种选择是将a
表示为两个32位量的总和,即 a = 2 32 b + c ,其中 b 包含32个字节, c 包含32个lbs。然后,平方是一组四次交叉乘法;每个结果都保证适合64位类型。然后,在重新组合各个术语时进行模运算(仔细考虑重新调整所有内容所需的变化)。
答案 1 :(得分:2)
我知道您不再需要这个,并且有另一种解决方案,但我想添加一种替代方法来实现它。它提供了两种不同的技术:double and add algorithm和处理溢出检测mod(a + b, n)
的方法。
Double和add算法通常用于无法进行乘法或直接计算代价太高的领域(例如椭圆曲线),但我们可以采用它来处理它,以便处理溢出。
以下代码可能比接受的解决方案慢(即使你优化它),但如果速度不是很关键,你可能更喜欢它以便清晰。
unsigned addmod(unsigned x, unsigned y, unsigned n)
{
// Precondition: x<n, y<n
// If it will overflow, use alternative calculation
if (x + y <= x) x = x - (n - y);
else x = (x + y) % n;
return x;
}
unsigned sqrmod(unsigned a, unsigned n)
{
unsigned b;
unsigned sum = 0;
// Make sure original number is less than n
a = a % n;
// Use double and add algorithm to calculate a*a mod n
for (b = a; b != 0; b >>= 1) {
if (b & 1) {
sum = addmod(sum, a, n);
}
a = addmod(a, a, n);
}
return sum;
}
答案 2 :(得分:1)
这是乘法a * b % m
的双重加法实现,没有溢出,无论a,b和m的大小。
(注意,res -= m
和temp_b -= m
行依赖于64位无符号整数溢出,以便给出预期的结果。这应该没问题,因为无符号整数溢出在C和C中定义良好C ++。因此它是important to use unsigned integer types。)
uint64_t mulmod(uint64_t a, uint64_t b, uint64_t m) {
uint64_t res = 0;
uint64_t temp_b;
/* Only needed if b may be >= m */
if (b >= m) {
if (m > UINT64_MAX / 2u)
b -= m;
else
b %= m;
}
while (a != 0) {
if (a & 1) {
/* Add b to res, modulo m, without overflow */
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;
}
这是my modification的another answer to another similar question。
答案 3 :(得分:-3)
您可以自己实现乘法,每次运行时添加 n ,然后立即修改结果。
答案 4 :(得分:-5)
我真的认为((a % n)*(a % n)) % n
应该适用于正整数。为什么你认为它不起作用?你有反例吗?如果n可能为负数,则%
运算符未定义。