对矩阵求幂N次?

时间:2018-09-25 19:48:45

标签: python python-3.x numpy matrix-multiplication exponentiation

我正在使用FOR实现矩阵的幂运算:

import numpy as np
fl=2
cl=2
fl2=fl
cl2=cl
M = random.random((fl,cl))
M2 = M
Result = np.zeros((fl,cl))
Temp = np.zeros((fl,cl))
itera = 2
print('Matriz A:\n',M)
print('Matriz AxA:\n',M2)

for i in range (0,itera):
    for a in range(0,fl):
        for b in range (0,cl):  
            Result[a,b]+=M[a,b]*M[a,b]
            temp[a,b]=Result[a,b]
            Res[a,k]=M[a,b]
print('Potencia:\n',temp)
print('Matriz:\n', Result)

错误是它在Result[a,b]+=M[a,b]*M[a,b]中的乘法效果不佳,当我将其保存在临时矩阵中以与原始矩阵相乘时,它不会在for i in range (0,itera):中进行下一个跳转

我知道我可以执行功能np.matmul 但我尝试使用FOR循环

Example

2 个答案:

答案 0 :(得分:1)

您正在寻找np.linalg.matrix_power

如果您使用的是numpy,请不要使用for循环,请使用矢量化操作。

arr = np.arange(16).reshape((4,4))

np.linalg.matrix_power(arr, 3)

array([[ 1680,  1940,  2200,  2460],
       [ 4880,  5620,  6360,  7100],
       [ 8080,  9300, 10520, 11740],
       [11280, 12980, 14680, 16380]])

与显式乘法相同:

arr @ arr @ arr

>>> np.array_equal(arr @ arr @ arr, np.linalg.matrix_power(arr, 3))
True

自从您问到

如果您真的想要使用循环的幼稚解决方案,我们可以很容易地整理各个部分。首先,我们需要一种实际上对矩阵进行多重计算的方法。有一些选项可以克服n ^ 3的复杂性,这个答案是不会做到的。这是基本的矩阵乘法函数:

def matmultiply(a, b):
  res = np.zeros(a.shape)
  size = a.shape[0]

  for i in range(size):
    for j in range(size):
      for k in range(size):
        res[i][j] += a[i][k] * b[k][j]

  return res

现在,您需要一个指数函数。此函数获取一个矩阵和一个幂,然后将矩阵提高到该幂。

def loopy_matrix_power(a, n):
  res = np.identity(a.shape[0])
  while n > 0:
    if n % 2 == 0:
      a = matmultiply(a, a)
      n /= 2
    else:
      res = matmultiply(res, a)
      n -= 1

  return res

实际情况:

loopy_matrix_power(arr, 3)

array([[ 1680.,  1940.,  2200.,  2460.],
       [ 4880.,  5620.,  6360.,  7100.],
       [ 8080.,  9300., 10520., 11740.],
       [11280., 12980., 14680., 16380.]])

答案 1 :(得分:1)

这里有一些问题:

  1. 在乘法完成后,您重置result矩阵,因此您将不断添加更多值;和
  2. 您永远不会将result分配回m来执行 next 乘法。

天真电源实现

我认为最好将矩阵乘法“封装”到一个单独的函数中,例如:

def matmul(a1, a2):
    m, ka = a1.shape
    kb, n = a2.shape
    if ka != kb:
        raise ValueError()
    res = np.zeros((m, n))
    for i in range(m):
        for j in range(n):
            d = 0.0
            for k in range(ka):
                d += a1[i,k] * a2[k,j]
            res[i, j] = d
    return res

然后我们可以使用以下公式计算该矩阵的功效:

m2 = m
for i in range(topow-1):
    m = matmul(m, m2)

请注意,我们不能在这里使用m作为唯一矩阵。由于如果我们写m = matmul(m, m),那么m现在是m 2 。但这意味着,如果我们第二次执行乘法运算,则会得到m 4 而不是m 3

这将产生预期的结果:

>>> cross = np.array([[1,0,1],[0,1,0], [1,0,1]])
>>> matmul(cross, cross)
array([[2., 0., 2.],
       [0., 1., 0.],
       [2., 0., 2.]])
>>> matmul(cross, matmul(cross, cross))
array([[4., 0., 4.],
       [0., 1., 0.],
       [4., 0., 4.]])
>>> matmul(cross, matmul(cross, matmul(cross, cross)))
array([[8., 0., 8.],
       [0., 1., 0.],
       [8., 0., 8.]])

对数乘幂

上面的代码可以在 O(n)(线性时间)中计算 M n ,但是我们可以做得更好,我们可以计算此矩阵在对数时间内:我们通过查看幂是否为1来执行此操作,如果是,则简单地返回矩阵,如果不是,则检查幂是否为偶数,如果是偶数,我们将矩阵与其自身相乘,然后计算该矩阵的幂,但幂除以二,因此 M 2 n =(M×M)< sup> n 。如果幂是 odd ,我们做的差不多相同,只是我们将其乘以 M 的原始值: M 2 n + 1 = M×(M×M) n 。喜欢:

def matpow(m, p):
    if p <= 0:
        raise ValueError()
    if p == 1:
        return m
    elif p % 2 == 0:  # even
        return matpow(matmul(m, m), p // 2)
    else:             # odd
        return matmul(m, matpow(matmul(m, m), p // 2))

以上内容可以写得很优美,但我将其保留为练习:)。

但是请注意,与使用numpy提供的矩阵乘法(和其他函数)相比,使用numpy数组进行标量计算通常效率要低 。这些代码经过优化,并且没有 解释,并且通常明显优于Python等效代码。因此,我真的建议您使用这些。还对numpy函数进行了测试,从而减少了其中的bug。