def invertMatrixZN(M, N): """Invertiert eine Matrix M in ZN mit dem Gauss-Jordan-Algorithmus, indem die Matrix zu einer Einheitsmatrix umgeformt und die entsprechenden Schritte auf einer Einheitsmatrix wiederholt werden, die dann am Schluss die Inverse darstellt. """ n = M.shape[0] # shape = (nzeilen, nspalten), also shape[0] = nzeilen M = M.copy() # nicht an der Originalmatrix rumspielen I = np.identity(n, int) # Einheitsmatrix -> wird später das Ergebnis for row in range(n): if not invertierbar(M[row, row], N): # müssen Zeilen tauschen for j in range(row+1, n): if invertierbar(M[j, row], N): tmp = M[row, :].copy() M[row, :] = M[j, :] M[j, :] = tmp tmp = I[row, :].copy() I[row, :] = I[j, :] I[j, :] = tmp break else: # hier kommen wir hin wenn die for-Schleife nicht durch ein # break beendet wurde, also keine geeignete Zeile zum Tauschen # existiert raise ValueError("Matrix nicht invertierbar") # Zeile mit dem Inversen des Pivot-Elements multiplizieren, um eine 1 # auf der Diagonalen zu erreichen faktor = invertZN(M[row, row], N) M[row, :] = (M[row, :] * faktor) % N I[row, :] = (I[row, :] * faktor) % N # Nullen unterhalb des aktuellen Pivots erzeugen for j in range(row + 1, n): if invertierbar(M[j, row], N): faktor = invertZN(M[j, row], N) M[j, :] = (M[j, :] * faktor - M[row, :]) % N I[j, :] = (I[j, :] * faktor - I[row, :]) % N elif M[j, row] != 0: # In Z_N können Nullteiler auftreten, z.B. die 8 in Z_{12}. # Um dort eine 0 zu erzeugen, müssen wir mit dem kgV der beiden # Zahlen multiplizieren. Da ggt*kgv = mn gilt, können wir dazu # den bereits implementierten ggt-Algorithmus nehmen. faktor = N * M[j, row] // krypto1.ggT(N, M[j, row]) M[j, :] = (M[j, :] * faktor) % N I[j, :] = (I[j, :] * faktor) % N # jetzt haben wir eine obere Dreiecksmatrix. Um daraus eine Diagonalmatrix # zu machen, müssen wir nun noch einmal von unten nach oben durchgehen # um die Einträge oberhalb der Diagonalen zu Nullen zu machen. for row in range(n-1, -1, -1): for j in range(row + 1, n): faktor = M[row, j] M[row, :] = (M[row, :] - faktor*M[j, :]) % N I[row, :] = (I[row, :] - faktor*I[j, :]) % N return I
def invertierbar(a, N): """Gibt an ob eine Zahl a invertierbar in Z_N ist.""" return krypto1.ggT(a, N) == 1