Numpy матричная мощность / экспонента с модулем?

Можно ли использовать linalg.matrix_power numpy по модулю, чтобы элементы не превышали определенное значение?


person John Smith    schedule 15.12.2011    source источник
comment
Можете ли вы определить, что вы подразумеваете под модулем.   -  person Benjamin    schedule 15.12.2011
comment
модуль = операция остатка. Например, 10 mod 3 = 1, 24 mod 5 = 4 и т. д. linalg.matrix_power работает быстро, но я хочу иметь возможность применять модульные операции к элементам до того, как они станут слишком большими.   -  person John Smith    schedule 15.12.2011
comment
А, по модулю: en.wikipedia.org/wiki/Modulo_operation   -  person Benjamin    schedule 15.12.2011
comment
правильно, но в сочетании с возведением матрицы в степень до того, как элементы взорвутся   -  person John Smith    schedule 15.12.2011
comment
Модуль обычно относится к абсолютному значению комплексных чисел (в то время как модуль действительно используется для остатка целочисленного деления).   -  person Eric O Lebigot    schedule 15.12.2011
comment
Да, модуль — это существительное (модуль чего-то: вектора, комплексного числа и т. д., знак, умноженный на числовой формат модуля), а модуль (часто сокращаемый до «мод») — это... что-то другое (наречие, причастие?). Это поможет мне никогда больше не называть остаток модулем, но я все еще не могу найти векторизованную поэлементную мощность в NumPy, например встроенную pow(x, y, z=None, /), которая равна Equivalent to x**y (with two arguments) or x**y % z (with three arguments)   -  person Tomasz Gandor    schedule 30.11.2019


Ответы (4)


Чтобы предотвратить переполнение, вы можете использовать тот факт, что вы получите тот же результат, если сначала возьмете модуль каждого из ваших входных чисел; по факту:

(M**k) mod p = ([M mod p]**k) mod p,

для матрицы M. Это следует из следующих двух фундаментальных тождеств, действительных для целых чисел x и y:

(x+y) mod p = ([x mod p]+[y mod p]) mod p  # All additions can be done on numbers *modulo p*
(x*y) mod p = ([x mod p]*[y mod p]) mod p  # All multiplications can be done on numbers *modulo p*

Те же тождества справедливы и для матриц, поскольку сложение и умножение матриц можно выразить через скалярное сложение и умножение. При этом вы возводите в степень только небольшие числа (n mod p обычно намного меньше, чем n) и с гораздо меньшей вероятностью получите переполнение. Поэтому в NumPy вы просто делаете

((arr % p)**k) % p

чтобы получить (arr**k) mod p.

Если этого все еще недостаточно (т. е. если есть риск того, что [n mod p]**k вызовет переполнение, несмотря на то, что n mod p мало), вы можете разбить возведение в степень на несколько возведений в степень. Фундаментальные тождества выше дают

(n**[a+b]) mod p = ([{n mod p}**a mod p] * [{n mod p}**b mod p]) mod p

а также

(n**[a*b]) mod p = ([n mod p]**a mod p)**b mod p.

Таким образом, вы можете разбить силу k на a+b+… или a*b*… или любую их комбинацию. Приведенные выше тождества позволяют выполнять только возведение в степень небольших чисел с помощью малых чисел, что значительно снижает риск целочисленных переполнений.

person Eric O Lebigot    schedule 15.12.2011

Использование реализации из Numpy:

https://github.com/numpy/numpy/blob/master/numpy/matrixlib/defmatrix.py#L98

Я адаптировал его, добавив термин по модулю. ОДНАКО существует ошибка, заключающаяся в том, что при переполнении не возникает OverflowError или любое другое исключение. С этого момента решение будет неверным. Отчет об ошибке находится здесь.

Вот код. Используйте с осторожностью:

from numpy.core.numeric import concatenate, isscalar, binary_repr, identity, asanyarray, dot
from numpy.core.numerictypes import issubdtype    
def matrix_power(M, n, mod_val):
    # Implementation shadows numpy's matrix_power, but with modulo included
    M = asanyarray(M)
    if len(M.shape) != 2 or M.shape[0] != M.shape[1]:
        raise ValueError("input  must be a square array")
    if not issubdtype(type(n), int):
        raise TypeError("exponent must be an integer")

    from numpy.linalg import inv

    if n==0:
        M = M.copy()
        M[:] = identity(M.shape[0])
        return M
    elif n<0:
        M = inv(M)
        n *= -1

    result = M % mod_val
    if n <= 3:
        for _ in range(n-1):
            result = dot(result, M) % mod_val
        return result

    # binary decompositon to reduce the number of matrix
    # multiplications for n > 3
    beta = binary_repr(n)
    Z, q, t = M, 0, len(beta)
    while beta[t-q-1] == '0':
        Z = dot(Z, Z) % mod_val
        q += 1
    result = Z
    for k in range(q+1, t):
        Z = dot(Z, Z) % mod_val
        if beta[t-k-1] == '1':
            result = dot(result, Z) % mod_val
    return result % mod_val
person Unapiedra    schedule 09.10.2014

Что не так с очевидным подходом?

E.g.

import numpy as np

x = np.arange(100).reshape(10,10)
y = np.linalg.matrix_power(x, 2) % 50
person Joe Kington    schedule 15.12.2011
comment
возможно, OP использует большие показатели и получает проблемы с переполнением. например алгоритмы с возведением в степень в сочетании с модулем часто используются для больших целых чисел в криптографии. - person wim; 15.12.2011

У меня были проблемы с переполнением во всех предыдущих решениях, поэтому мне пришлось написать алгоритм, который учитывает переполнение после каждого целочисленного умножения. Вот как я это сделал:

def matrix_power_mod(x, n, modulus):
    x = np.asanyarray(x)
    if len(x.shape) != 2:
        raise ValueError("input must be a matrix")
    if x.shape[0] != x.shape[1]:
        raise ValueError("input must be a square matrix")
    if not isinstance(n, int):
        raise ValueError("power must be an integer")

    if n < 0:
        x = np.linalg.inv(x)
        n = -n
    if n == 0:
        return np.identity(x.shape[0], dtype=x.dtype)
    y = None
    while n > 1:
        if n % 2 == 1:
            y = _matrix_mul_mod_opt(x, y, modulus=modulus)
        x = _matrix_mul_mod(x, x, modulus=modulus)
        n = n // 2
    return _matrix_mul_mod_opt(x, y, modulus=modulus)


def matrix_mul_mod(a, b, modulus):
    if len(a.shape) != 2:
        raise ValueError("input a must be a matrix")
    if len(b.shape) != 2:
        raise ValueError("input b must be a matrix")
    if a.shape[1] != a.shape[0]:
        raise ValueError("input a and b must have compatible shape for multiplication")
    return _matrix_mul_mod(a, b, modulus=modulus)


def _matrix_mul_mod_opt(a, b, modulus):
    if b is None:
        return a
    return _matrix_mul_mod(a, b, modulus=modulus)


def _matrix_mul_mod(a, b, modulus):
    r = np.zeros((a.shape[0], b.shape[1]), dtype=a.dtype)
    bT = b.T
    for rowindex in range(r.shape[0]):
        x = (a[rowindex, :] * bT) % modulus
        x = np.sum(x, 1) % modulus
        r[rowindex, :] = x
    return r
person Tamas Hegedus    schedule 29.10.2019