因为对于大质数,我需要计算 a ^ (p-2)
,通常是无法计算的。
你需要模幂运算,所以使用 平方法 IVlad提到的,你只需要进行 Θ(log p)
模乘运算,数字大小最多为 p-1
。中间结果受到 p^2
的限制,因此尽管对于大质数而言 a^(p-2)
不可计算,但 (a ^ (p-2)) % p
通常是可以计算的。该方法易于实现:
unsigned long long invert_mod(unsigned long long a, unsigned long long p) {
unsigned long long ex = p-2, result = 1;
while (ex > 0) {
if (ex % 2 == 1) {
result = (result*a) % p;
}
a = (a*a) % p;
ex /= 2;
}
return result;
}
但是也有一些缺点。
(p-1)^2
必须在所使用的类型中可表示(除非使用任意精度整数,否则会出现问题[尤其是对于
巨大的
p
]),否则由于溢出而得到无效结果,并且它总是使用至少
log (p-2)/log 2
模乘法。
扩展欧几里德算法,正如
用户448810建议的,或者等价的连分数方法,从不产生比
p
更大的中间值,因此如果
p
可以表示,则避免了所有溢出问题,通常需要更少的除法。此外,它不仅计算素数的模反演,还计算任何两个互质数的模反演。
unsigned long long invert_mod(unsigned long long a, unsigned long long p) {
unsigned long long new = 1, old = 0, q = p, r, h;
int pos = 0;
while (a > 0) {
r = q%a;
q = q/a;
h = q*new + old;
old = new;
new = h;
q = a;
a = r;
pos = !pos;
}
return pos ? old : (p - old);
}
代码略长,但优化编译器应该能将其编译为短循环,每次迭代仅使用一次除法。