3

現在、以下のコードを使用して i 番目の行と j 番目の列を削除した部分行列を取得していますが、コードをプロファイリングした後、コードの主なボトルネックの 1 つと思われます。より効率的な方法はありますか?

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)

編集: ハイメは通常の逆数と行列式を使用してモジュラー逆数を近似する良い方法を提供しましたが、大きなベース (私の場合はモジュロ 256) では、不正確さは全体を無意味なものにするのに十分です. 主なタイム シンクは、実際には numpy の getitem のように見えますが、次の行が原因であると考えています

    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:]

ボトルネックがメモリ内での行列のコピーではなく、行列エントリへのアクセスである可能性があります。

4

3 に答える 3

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]])
于 2013-04-09T18:39:14.490 に答える