我正在考虑一种算法来解决ax = 1 mod p
与 p 素数的一致性。我正在考虑使用费马定理。既然我知道
a ^ (p-1) = 1 mod p
然后
a ^ (p-1) = a * (a ^ (p-2))
这意味着这a ^ (p-2) mod p
就是解决方案。不幸的是,这个解决方案虽然在数学上是正确的,但对计算机来说并不好,因为对于大素数我必须这样做a ^ (p-2)
,这通常是不可计算的。
哪种算法适合计算机科学?
我正在考虑一种算法来解决ax = 1 mod p
与 p 素数的一致性。我正在考虑使用费马定理。既然我知道
a ^ (p-1) = 1 mod p
然后
a ^ (p-1) = a * (a ^ (p-2))
这意味着这a ^ (p-2) mod p
就是解决方案。不幸的是,这个解决方案虽然在数学上是正确的,但对计算机来说并不好,因为对于大素数我必须这样做a ^ (p-2)
,这通常是不可计算的。
哪种算法适合计算机科学?
因为对于大素数我必须做
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
模乘。
如user448810 所建议的扩展欧几里得算法,或等效的连分数法,永远不会产生大于 的中间值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);
}
代码稍长,但优化编译器应该将其编译为一个短循环,每次迭代只使用一个除法。
计算模逆的通常方法是通过扩展欧几里得算法:
function inverse(x, m)
a, b, u := 0, m, 1
while x > 0
q, r := divide(b, x)
x, a, b, u := b % x, u, x, a - q * u
if b == 1 return a % m
error "must be coprime"
没有理由这对计算机来说不是一个好的算法,你只需要小心实现,我猜这并不是微不足道的,但也不难。
只需通过平方使用取幂,那么它的大小很可能无关紧要p
。
a^n = a^(n / 2) * a^(n / 2) for n even
= a*a^(n - 1) for n odd