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

时间:2017-06-16 07:21:49

标签: python arrays matrix modulus

我是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上运行代码。两者都不起作用。

2 个答案:

答案 0 :(得分:0)

在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

答案 1 :(得分:0)

问题是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)