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