我在想一个用p prime来解决同余ax = 1 mod p
的算法。
我在考虑使用费马定理。因为我知道
a ^ (p-1) = 1 mod p
那个
a ^ (p-1) = a * (a ^ (p-2))
这意味着a ^ (p-2) mod p
是解决方案。不幸的是,这个解决方案虽然在数学上是正确的,但对计算机并不好,因为对于大素数我不得不做a ^ (p-2)
这通常是不可计算的。
哪种算法适合计算机科学?
答案 0 :(得分:10)
因为对于大素数我必须做
a ^ (p-2)
,这通常是不可计算的。
您需要使用模幂运算,因此使用exponentiation by squaring mentioned by 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
除外),或者由于溢出而导致结果无效,它总是使用至少p
模乘。
扩展的欧几里得算法as suggested by user448810或等效的连续分数方法,永远不会产生大于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);
}
可表示的所有溢出问题,并且通常需要分歧越来越少。此外,它不仅计算质数,而且计算任何两个互质数字的模数逆。
{{1}}
代码稍长,但优化编译器应该将其编译为一个短循环,每次迭代只使用一个分区。
答案 1 :(得分:6)
计算模逆的正常方法是通过扩展的欧几里德算法:
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"
答案 2 :(得分:1)
没有理由这对于计算机来说不是一个好的算法,你只需要小心实现,我猜这不是一件容易的事,但也不难。
只需使用exponentiation by squaring,那么很可能无关紧要p
。
a^n = a^(n / 2) * a^(n / 2) for n even
= a*a^(n - 1) for n odd