La forma más fácil de realizar la inversión de matriz modular con Python?
inverse matrix algorithm c++ (5)
Me gustaría tomar la inversa modular de una matriz como [[1,2], [3,4]] mod 7 en Python. He observado numpy (que hace inversión de matriz pero no inversión de matriz modular) y vi algunos paquetes de teoría de números en línea, pero nada que parezca hacer este procedimiento relativamente común (al menos, me parece relativamente común).
Por cierto, el inverso de la matriz anterior es [[5,1], [5,3]] (mod 7). Aunque me gustaría que Python lo haga por mí.
Este pequeño fragmento de código parece hacerlo: enlace
Tenga en cuenta el comentario a continuación para una pequeña mejora. Parece que hace el álgebra lineal correcta hasta donde puedo ver. Nunca he encontrado ninguna opción en los paquetes regulares, por lo que probablemente tomar un fragmento de código de la web (hay muchos más disponibles) es el enfoque más fácil.
De acuerdo ... para los que les importa, resolví mi propio problema. Me tomó un tiempo, pero creo que esto funciona. Probablemente no sea el más elegante, y debería incluir un poco más de manejo de errores, pero funciona:
import numpy
import math
from numpy import matrix
from numpy import linalg
def modMatInv(A,p): # Finds the inverse of matrix A mod p
n=len(A)
A=matrix(A)
adj=numpy.zeros(shape=(n,n))
for i in range(0,n):
for j in range(0,n):
adj[i][j]=((-1)**(i+j)*int(round(linalg.det(minor(A,j,i)))))%p
return (modInv(int(round(linalg.det(A))),p)*adj)%p
def modInv(a,p): # Finds the inverse of a mod p, if it exists
for i in range(1,p):
if (i*a)%p==1:
return i
raise ValueError(str(a)+" has no inverse mod "+str(p))
def minor(A,i,j): # Return matrix A with the ith row and jth column deleted
A=numpy.array(A)
minor=numpy.zeros(shape=(len(A)-1,len(A)-1))
p=0
for s in range(0,len(minor)):
if p==i:
p=p+1
q=0
for t in range(0,len(minor)):
if q==j:
q=q+1
minor[s][t]=A[p][q]
q=q+1
p=p+1
return minor
Desafortunadamente numpy no tiene implementaciones aritméticas modulares. Siempre puede codificar el algoritmo propuesto usando la reducción de fila o los determinantes como se demuestra aquí . Un inverso modular parece ser bastante útil para la criptografía.
Se puede calcular usando Sage ( www.sagemath.org ) como
Matriz (IntegerModRing (7), [[1, 2], [3,4]]) inversa ()
Aunque Sage es enorme de instalar y tienes que usar la versión de python que viene con él, que es un problema.
Un truco de hack que funciona cuando los errores de redondeo no son un problema:
- encontrar el inverso regular (puede tener entradas no enteras) y el determinante (un entero), ambos implementados en numpy
- multiplica el inverso por el determinante y redondea a enteros (hacky)
- ahora multiplica todo por la inversa multiplicativa del determinante (módulo tu módulo, código debajo)
- hacer mod de entrada por su módulo
Una forma menos hackosa es implementar realmente la eliminación gaussiana. Aquí está mi código usando la eliminación de Gauss, que escribí para mis propios fines (los errores de redondeo fueron un problema para mí). q es el módulo, que no es necesariamente primo.
def generalizedEuclidianAlgorithm(a, b):
if b > a:
return generalizedEuclidianAlgorithm(b,a);
elif b == 0:
return (1, 0);
else:
(x, y) = generalizedEuclidianAlgorithm(b, a % b);
return (y, x - (a / b) * y)
def inversemodp(a, p):
a = a % p
if (a == 0):
print "a is 0 mod p"
return None
if a > 1 and p % a == 0:
return None
(x,y) = generalizedEuclidianAlgorithm(p, a % p);
inv = y % p
assert (inv * a) % p == 1
return inv
def identitymatrix(n):
return [[long(x == y) for x in range(0, n)] for y in range(0, n)]
def inversematrix(matrix, q):
n = len(matrix)
A = np.matrix([[ matrix[j, i] for i in range(0,n)] for j in range(0, n)], dtype = long)
Ainv = np.matrix(identitymatrix(n), dtype = long)
for i in range(0, n):
factor = inversemodp(A[i,i], q)
if factor is None:
raise ValueError("TODO: deal with this case")
A[i] = A[i] * factor % q
Ainv[i] = Ainv[i] * factor % q
for j in range(0, n):
if (i != j):
factor = A[j, i]
A[j] = (A[j] - factor * A[i]) % q
Ainv[j] = (Ainv[j] - factor * Ainv[i]) % q
return Ainv
EDITAR: como señalan los comentaristas, hay algunos casos en que este algoritmo falla. No es trivial repararlo, y no tengo tiempo hoy en día. En aquel entonces funcionaba para matrices aleatorias en mi caso (los módulos eran productos de primos grandes). Básicamente, la primera entrada distinta de cero podría no ser relativamente primo para el módulo. El caso principal es fácil ya que puede buscar una fila diferente y cambiar. En el caso no principal, creo que podría ser que todas las entradas principales no son relativamente primarias, por lo que debe combinarlas