2011-01-25 44 views
57

¿Algún módulo Python estándar contiene una función para calcular modular multiplicative inverse de un número, es decir, un número y = invmod(x, p) tal que x*y == 1 (mod p)? Google no parece dar buenos consejos sobre esto.Función multiplicativa multiplicativa modular en Python

Por supuesto, uno puede inventar un trazador de líneas de 10 de fabricación casera de extended Euclidean algorithm, pero ¿por qué reinventar la rueda?

Por ejemplo, Java BigInteger tiene modInverse método. ¿Python no tiene algo similar?

Respuesta

33

Si el módulo es primo (que llaman p), entonces puede simplemente calcular:

y = x**(p-2) mod p # Pseudocode 

O en Python adecuado:

y = pow(x, p-2, p) 

Aquí es alguien que ha implementado algunas capacidades de la teoría de números en Python: http://userpages.umbc.edu/~rcampbel/Computers/Python/numbthy.html

Aquí hay un ejemplo hecho en el indicador:

 
m = 1000000007 
x = 1234567 
y = pow(x,m-2,m) 
y 
989145189L 
x*y 
1221166008548163L 
x*y % m 
1L 
+1

exponenciación Naive no es una opción debido tiempo (y memoria) límite para cualquier razonablemente grande valor de p como decir 1000000007. – dorserg

+13

exponenciación modular se hace con a lo sumo N * 2 multiplicaciones donde N es el número de bits en el exponente. usando un módulo de 2 ** 63-1, el inverso se puede calcular en el prompt y devuelve un resultado inmediatamente. – phkahler

+0

Guau, increíble. Soy consciente de la exponenciación rápida, simplemente no sabía que la función pow() puede tomar un tercer argumento que lo convierte en exponenciación modular. – dorserg

15

Es posible que también desee ver el módulo gmpy. Es una interfaz entre Python y la biblioteca de precisión múltiple de GMP. gmpy proporciona una función invertido que hace exactamente lo que necesita:

>>> import gmpy 
>>> gmpy.invert(1234567, 1000000007) 
mpz(989145189) 

Actualizado respuesta

Como señaló @hyh, los gmpy.invert() devuelve 0 si no existe la inversa. Eso coincide con el comportamiento de la función mpz_invert() de GMP. gmpy.divm(a, b, m) proporciona una solución general a a=bx (mod m).

>>> gmpy.divm(1, 1234567, 1000000007) 
mpz(989145189) 
>>> gmpy.divm(1, 0, 5) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ZeroDivisionError: not invertible 
>>> gmpy.divm(1, 4, 8) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ZeroDivisionError: not invertible 
>>> gmpy.divm(1, 4, 9) 
mpz(7) 

divm() devolverá una solución cuando gcd(b,m) == 1 y lanza una excepción cuando no existe el inverso multiplicativo.

Descargo de responsabilidad: Soy el actual mantenedor de la biblioteca gmpy.

actualizada Respuesta 2

gmpy2 ahora plantea adecuadamente una excepción cuando la inversa no existe:

>>> import gmpy2 

>>> gmpy2.invert(0,5) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ZeroDivisionError: invert() no inverse exists 
+0

Esto es genial hasta que encontré 'gmpy.invert (0,5) = mpz (0)' en lugar de generar un error ... –

+0

@hyh ¿Puede informar esto como un problema en la página de inicio de gmpy ? Siempre se aprecia si se informan problemas. – casevh

+0

https://code.google.com/p/gmpy/issues/detail?id=72 Espero que esto funcione. –

1

de averiguar el inverso multiplicativo modular le recomiendo usar el extendido de Euclides Algoritmo de la siguiente manera:

def multiplicative_inverse(a, b): 
    origA = a 
    X = 0 
    prevX = 1 
    Y = 1 
    prevY = 0 
    while b != 0: 
     temp = b 
     quotient = a/b 
     b = a%b 
     a = temp 
     temp = X 
     a = prevX - quotient * X 
     prevX = temp 
     temp = Y 
     Y = prevY - quotient * Y 
     prevY = temp 

    return origA + prevY 
+0

Parece haber un error en este código: 'a = prevX - cociente * X' debe ser' X = prevX - cociente * X', y debe devolver 'prevX'. FWIW, esta implementación es similar a la del enlace de [Qaz] (http://anh.cs.luc.edu/331/notes/xgcd.pdf) en el comentario a la respuesta de Märt Bakhoff. –

78

Quizás alguien lo encuentre útil (de wikibooks):

def egcd(a, b): 
    if a == 0: 
     return (b, 0, 1) 
    else: 
     g, y, x = egcd(b % a, a) 
     return (g, x - (b // a) * y, y) 

def modinv(a, m): 
    g, x, y = egcd(a, m) 
    if g != 1: 
     raise Exception('modular inverse does not exist') 
    else: 
     return x % m 
+1

Estaba teniendo problemas con números negativos usando este algoritmo. modinv (-3, 11) no funcionó. Lo arreglé reemplazando egcd con la implementación en la página dos de este pdf: http: //anh.cs.luc.edu/331/notes/xgcd.pdf ¡Espero que ayude! – Qaz

+0

@Qaz También puede simplemente reducir -3 módulo 11 para hacerlo positivo, en este caso modinv (-3, 11) == modinv (-3 + 11, 11) == modinv (8, 11). Eso es probablemente lo que hace el algoritmo en su PDF en algún momento. – Thomas

+0

Si está usando 'sympy', entonces' x, _, g = sympy.numbers.igcdex (a, m) 'hace el truco. – Lynn

2

Aquí está mi código, podría ser descuidado pero parece funcionar para mí de todos modos.

# a is the number you want the inverse for 
# b is the modulus 

def mod_inverse(a, b): 
    r = -1 
    B = b 
    A = a 
    eq_set = [] 
    full_set = [] 
    mod_set = [] 

    #euclid's algorithm 
    while r!=1 and r!=0: 
     r = b%a 
     q = b//a 
     eq_set = [r, b, a, q*-1] 
     b = a 
     a = r 
     full_set.append(eq_set) 

    for i in range(0, 4): 
     mod_set.append(full_set[-1][i]) 

    mod_set.insert(2, 1) 
    counter = 0 

    #extended euclid's algorithm 
    for i in range(1, len(full_set)): 
     if counter%2 == 0: 
      mod_set[2] = full_set[-1*(i+1)][3]*mod_set[4]+mod_set[2] 
      mod_set[3] = full_set[-1*(i+1)][1] 

     elif counter%2 != 0: 
      mod_set[4] = full_set[-1*(i+1)][3]*mod_set[2]+mod_set[4] 
      mod_set[1] = full_set[-1*(i+1)][1] 

     counter += 1 

    if mod_set[3] == B: 
     return mod_set[2]%B 
    return mod_set[4]%B 
5

Aquí es una sola línea para CodeFights; es una de las soluciones más cortas:

MMI = lambda A, n,s=1,t=0,N=0: (n < 2 and t%N or MMI(n, A%n, t, s-A//n*t, N or n),-1)[n<1] 

Se volverá -1 si A no tiene inverso multiplicativo en n.

Uso:

MMI(23, 99) # returns 56 
MMI(18, 24) # return -1 

La solución utiliza la Extended Euclidean Algorithm.

0

Bueno, no tengo una función en Python, pero tengo una función en C que se puede convertir fácilmente a pitón, en el siguiente función c algoritmo de Euclides extendido se utiliza para calcular mod inversa

int imod(int a,int n){ 
int c,i=1; 
while(1){ 
    c = n * i + 1; 
    if(c%a==0){ 
     c = c/a; 
     break; 
    } 
    i++; 
} 
return c;} 

función de Python

def imod(a,n): 
    i=1 
    while True: 
    c = n * i + 1; 
    if(c%a==0): 
     c = c/a 
     break; 
    i = i+1 

    return c 

referencia a la función C anterior es tomada desde el siguiente enlace C program to find Modular Multiplicative Inverse of two Relatively Prime Numbers