Python :模运算符给出不正确的结果

Python : Modulus operator giving incorrect result

我是 Python 的新手,我正在尝试将 Hill Cipher 作为一个小项目来实现。我想我已经弄清楚了逻辑并且加密部分可以正常工作,但是,在解密部分,我遇到了麻烦。特别是对于模数运算符。在下面的解密函数中,倒数第二行是造成麻烦的地方。

import numpy as np
import fractions as fr

def decrypt(matrix, words):
    matrix=np.asmatrix(matrix)
    length = len(matrix)
    det_matrix=int(round((np.linalg.det(matrix))%26))
    mult_inv=(abs(det_matrix*26)/fr.gcd(det_matrix,26))/26
    matrix_inv=(((np.linalg.inv(matrix)*np.linalg.det(matrix))%26)*mult_inv)%26
    words = words.lower()
    arr = np.array([ord(i) - ord('a') for i in words], dtype=int)
    decrypt_matrix=(np.asmatrix(matrix_inv)*(np.asmatrix(arr).transpose()))%26
    return decrypt_matrix

我对解密函数的输入是:

>>> matrix
[[6, 24, 1], [13, 16, 10], [20, 17, 15]]
>>> words
'poh'

并且在 det_matrix 的计算之后,mult_inv 变量的值为 25。因此计算 matrix_inv 的代码行将具有以下值,(这绝对是正确):

>>> matrix_inv
array([[  8.,   5.,  10.],
  [ 21.,   8.,  21.],
  [ 21.,  12.,   8.]])

数组 arr 的值为:

>>> arr
array([15, 14,  7])

现在的问题是下一行代码,在对表达式的结果执行取模之前

matrix_inv*(np.asmatrix(arr).transpose())

是:

matrix([[ 260.],
   [ 574.],
   [ 539.]])

现在,如果我对上述矩阵执行模数 26,我应该得到

的输出
([[0.],[2.],[19.]]) 

但是,下面是我执行表达式时得到的结果

>>> (np.asmatrix(matrix_inv)*(np.asmatrix(arr).transpose()))%26
matrix([[ 26.],
     [  2.],
     [ 19.]])

不明白为什么第一个元素计算错了(260%26是0不是26)!但是,其余两个元素已正确计算!

非常感谢任何帮助!!

P.S : 我试过 运行 2.7.11 和 3.6.1 版本的代码。两者都不起作用。

在 Python 3.5、Spyder Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] on win32

中工作正常
matrix_inv = np.array([[  8.,   5.,  10.],
   [ 21.,   8.,  21.],
   [ 21.,  12.,   8.]])

matrix_inv
Out[182]: 
array([[  8.,   5.,  10.],
       [ 21.,   8.,  21.],
       [ 21.,  12.,   8.]])

arr = np.array([15, 14,  7])

arr
Out[184]: array([15, 14,  7])

(np.asmatrix(matrix_inv)*(np.asmatrix(arr).transpose()))%26
Out[185]: 
matrix([[  0.],
        [  2.],
        [ 19.]])
print(np.__version__)
1.11.1

问题是 detnumpy.float64。你得到的可能是这样的:

round(259.6 % 26) # -> round(25.600000000000023) -> 26.0

这个有效:

round(259.6) % 26  # 0

在你的解密结果中你有:

dec = decrypt(matrix, words)
dec[0,0]      # 25.999999999989768
dec[0,0] % 26 # 25.999999999989768

它只会显示为26.


因为我对 3x3 矩阵的模逆很感兴趣,所以我写了一些代码...也许这对您有用...

import numpy as np
from itertools import product, cycle


def gcd_xy(a, b):
    '''
    extended euclidean algo: return (g, x, y): g = gcd(a, b); a*x + b*y = d.
    '''
    q, r = divmod(a, b)
    x, y, x1, y1 = 0, 1, 1, 0
    while r != 0:
        x1, y1, x, y = x, y, x1 - q*x, y1 - q*y
        b, (q, r) = r, divmod(b, r)
    return b, x, y


def mod_inv(e, n):
    '''
    return d == 1/e mod n or raise ValueError if e and n are not co-prime.
    '''
    g, d, _ = gcd_xy(e, n)
    if g != 1:
        msg = '{} has no inverse mod {}'.format(e, n)
        raise ValueError(msg)
    d %= n
    return d


def mod_inv_matrix(matrix, n):
    '''
    modular inverse of 3x3 matrix 
    '''

    inv = np.zeros((3, 3), dtype=int)

    det = round(np.linalg.det(matrix))
    det_inv = mod_inv(det, n)

    matrixT = matrix.T
    for (i, j), sign in zip(product(range(3), repeat=2), cycle((1, -1))):
        m = np.delete(np.delete(matrixT, i, axis=0), j, axis=1)
        inv[i, j] = sign * det_inv * round(np.linalg.det(m)) % n
    return inv


def hill_decrypt(matrix, words):

    matrix_inv = mod_inv_matrix(matrix, n=26)

    words = words.lower()
    arr = np.array([ord(i) - ord('a') for i in words], dtype=int)

    plain = (matrix_inv @ arr) % 26

    return plain

matrix = np.array([[6, 24, 1], [13, 16, 10], [20, 17, 15]], dtype=int)
words = 'poh'
dec = hill_decrypt(matrix, words)
print(dec)

对于模逆你也可以只使用 gmpy module

import gmpy
gmpy.invert(7, 26)