A ^ k для матричного умножения в R?

Предположим, что A - некоторая квадратная матрица. Как легко измерить эту матрицу в R?

Я уже пробовал два пути: Trial 1 с хакером for-loop и Trial 2 немного элегантнее, но он все еще далек от простоты A k.

Испытание 1

set.seed(10)
t(matrix(rnorm(16),ncol=4,nrow=4)) -> a 
for(i in 1:2){a <- a %*% a}

Испытание 2

a <- t(matrix(c(0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0),nrow=4))
i <- diag(4) 
(function(n) {if (n<=1) a else (i+a) %*% Recall(n-1)})(10)

Ответ 1

Хотя Reduce более изящный, решение for-loop работает быстрее и, кажется, выполняется так же быстро, как expm::% ^%

m1 <- matrix(1:9, 3)
m2 <- matrix(1:9, 3)
m3 <- matrix(1:9, 3)
system.time(replicate(1000, Reduce("%*%" , list(m1,m1,m1) ) ) )
#   user  system elapsed 
#  0.026   0.000   0.037 
mlist <- list(m1,m2,m3)
m0 <- diag(1, nrow=3,ncol=3)
system.time(replicate(1000, for (i in 1:3 ) {m0 <- m0 %*% m1 } ) )
#   user  system elapsed 
#  0.013   0.000   0.014 

library(expm)  # and I think this may be imported with pkg:Matrix
system.time(replicate(1000, m0%^%3))
# user  system elapsed 
#0.011   0.000   0.017 

С другой стороны, решение matrix.power намного, намного медленнее:

system.time(replicate(1000, matrix.power(m1, 4)) )
   user  system elapsed 
  0.677   0.013   1.037 

@BenBolker верен (еще раз). Поток цикла становится линейным по времени, когда показатель возрастает, тогда как функция expm::% ^% оказывается даже лучше, чем log (показатель степени).

> m0 <- diag(1, nrow=3,ncol=3)
> system.time(replicate(1000, for (i in 1:400 ) {m0 <- m0 %*% m1 } ) )
   user  system elapsed 
  0.678   0.037   0.708 
> system.time(replicate(1000, m0%^%400))
   user  system elapsed 
  0.006   0.000   0.006 

Ответ 2

Если A является диатонизируемым, вы можете использовать разложение по собственным значениям:

matrix.power <- function(A, n) {   # only works for diagonalizable matrices
   e <- eigen(A)
   M <- e$vectors   # matrix for changing basis
   d <- e$values    # eigen values
   return(M %*% diag(d^n) %*% solve(M))
}

Когда A не диагонализуемо, матрица M (матрица собственных векторов) является особой. Таким образом, использование его с помощью A = matrix(c(0,1,0,0),2,2) даст Error in solve.default(M) : system is computationally singular.

Ответ 3

В пакете expm есть оператор %^%:

library("sos")
findFn("{matrix power}")
install.packages("expm")
library("expm")
?matpow
set.seed(10);t(matrix(rnorm(16),ncol=4,nrow=4))->a
a%^%8

Ответ 4

Более короткое решение с разложением на собственные значения:

"%^%" <- function(S, power)
         with(eigen(S), vectors %*% (values^power * t(vectors)))

Ответ 5

Действительно, пакет expm использует возведение в степень по квадрату.

В чистом r это можно сделать достаточно эффективно,

"%^%" <- function(mat,power){
    base = mat
    out = diag(nrow(mat))
    while(power > 1){
        if(power %% 2 == 1){
            out = out %*% base
        }
        base = base %*% base
        power  = power %/% 2
    }
    out %*% base
}

Сроки этого,

m0 <- diag(1, nrow=3,ncol=3)
system.time(replicate(10000, m0%^%4000))#expm %^% function
user  system elapsed 
0.31    0.00    0.31 
system.time(replicate(10000, m0%^%4000))# my %^% function
user  system elapsed 
0.28    0.00    0.28 

Итак, как и ожидалось, они имеют одинаковую скорость, потому что они используют один и тот же алгоритм. Похоже, что накладные расходы на код цикла r не имеют существенной разницы.

Итак, если вы не хотите использовать expm, и вам нужна эта производительность, вы можете просто использовать это, если не возражаете смотреть на императивный код.

Ответ 6

Здесь решение, которое не очень эффективно, но работает и легко понимается/реализуется, работает в базе и работает почти мгновенно, несмотря на то, что оно менее эффективно, чем, например, решение expm:

eval(parse(text = paste(rep("A", k), collapse = '%*%')))

например.

set.seed(032149)
# force A to be a Markov matrix so it converges
A = matrix(abs(rnorm(100)), 10, 10)
A = A/rowSums(A)
eval(parse(text = paste(rep("A", 1000), collapse = '%*%')))[1:5, 1:5]
#            [,1]       [,2]      [,3]      [,4]       [,5]
# [1,] 0.09768683 0.07976306 0.1074442 0.1284846 0.07789486
# [2,] 0.09768683 0.07976306 0.1074442 0.1284846 0.07789486
# [3,] 0.09768683 0.07976306 0.1074442 0.1284846 0.07789486
# [4,] 0.09768683 0.07976306 0.1074442 0.1284846 0.07789486
# [5,] 0.09768683 0.07976306 0.1074442 0.1284846 0.07789486

Опять же, если эффективность имеет первостепенное значение, другие ответы намного превосходят. Но для этого требуется меньше накладных расходов на кодирование и нет пакетов для ситуаций, когда вы просто хотите вычислить некоторые свойства матрицы в работе с вашими царапинами.