找到以素数为模的数的倒数

时间:2012-12-30 18:34:34

标签: algorithm

我在想一个用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)这通常是不可计算的。

哪种算法适合计算机科学?

3 个答案:

答案 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