numpy更有效的子矩阵

时间:2013-04-09 15:50:21

标签: python numpy

目前我使用下面的代码来删除第i行和第j列的子矩阵,但在分析我的代码之后,它似乎是我代码中的主要瓶颈之一。有更有效的方法吗?

def submatrix(A, i, j):
    logger.debug('submatrix(%r, %r, %r)', A, i, j)
    B = empty(shape=tuple(x - 1 for x in A.shape), dtype=int)
    B[:i, :j] = A[:i, :j]
    B[i:, :j] = A[i+1:, :j]
    B[:i, j:] = A[:i, j+1:]
    B[i:, j:] = A[i+1:, j+1:]
    return B

         25015049 function calls (24599369 primitive calls) in 44.587 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  3983040   15.541    0.000   20.719    0.000 defmatrix.py:301(__getitem__)
   415680   10.216    0.000   33.069    0.000 hill.py:127(submatrix)
 415686/6    3.232    0.000   44.578    7.430 hill.py:112(det)

编辑:Jaime提供了一种使用常规逆和行列式逼近模块化逆的好方法,但是对于大基数(在我的情况下是模256),不准确性足以使整个事情变得没有意义。主时间接收器似乎实际上是 getitem numpy,但我相信这是由这些线引起的:

    B[:i, :j] = A[:i, :j]
    B[i:, :j] = A[i+1:, :j]
    B[:i, j:] = A[:i, j+1:]
    B[i:, j:] = A[i+1:, j+1:]

瓶颈可能不是在内存中复制矩阵,而是矩阵输入访问。

3 个答案:

答案 0 :(得分:1)

嗯...你只是在复制矩阵,所以它可能很难加速,但你可以尝试的一件事是验证A是否在一个连续的内存块中,这可以加快C的访问速度码。请看numpy.ascontiguousarray()

答案 1 :(得分:1)

据我所知,submatrix只会删除i行和j列。您可以使用np.delete

执行此操作
i = 3
j = 4
a = np.arange(100).reshape(10,10)
b = np.delete(np.delete(a, i, 0), j, 1)

但是,对于rason @Jaime引用,这实际上是较慢:-/

timeit submatrix(a, i, j)
#10000 loops, best of 3: 23.2 us per loop

timeit subdel(a, i, j)
#10000 loops, best of 3: 42.6 us per loop

但我现在暂时离开这里。

答案 2 :(得分:1)

使用决定因素计算矩阵的逆矩阵是一种非常慢的方法,无论您如何处理子矩阵。让我们举一个愚蠢的例子:

a = np.array([[3, 0, 2],
              [2, 0, -2],
              [0, 1, 1]])

您可以快速计算逆转:

>>> np.linalg.inv(a)
array([[ 0.2,  0.2,  0. ],
       [-0.2,  0.3,  1. ],
       [ 0.2, -0.3, -0. ]])

但是要计算模逆,你需要将它作为整数矩阵除以整数因子。该整数因子当然是决定因素,因此您可以执行以下操作:

>>> np.linalg.inv(a) * np.linalg.det(a)
array([[  2.,   2.,   0.],
       [ -2.,   3.,  10.],
       [  2.,  -3.,  -0.]])

a的倒数是这个整数矩阵,除以a的行列式。作为一项功能,您可以这样做:

def extended_euclidean_algorithm(a, b) :
    """
    Computes a solution to a x + b y = gcd(a,b), as well as gcd(a,b),
    using the extended Euclidean algorithm.
    """
    if b == 0 :
        return 1, 0, a
    else :
        x, y, gcd = extended_euclidean_algorithm(b, a % b)
        return y, x - y * (a // b), gcd

def mmi(a, m) :
    """
    Computes the modular multiplicative inverse of a modulo m, using the
    extended Euclidean algorithm.
    """
    x, y, gcd = extended_euclidean_algorithm(a, m)
    if gcd == 1 :
        return x % m
    else :
        return None

def modular_inv(a, m):
    det_a = np.linalg.det(a)
    inv_a = np.linalg.inv(a) * det_a
    det_a = np.rint(det_a).astype(int)
    inv_a = np.rint(inv_a).astype(int)
    return ((inv_a % m) * mmi(det_a, m)) % m

现在:

>>> a = np.random.randint(10, size=(10, 10))
>>> b = modular_inv(a, 7)
>>> a.dot(b) % 7
array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]])