假设 A 是某个方阵。如何在R中轻松取幂这个矩阵?
我已经尝试了两种方法:试用1使用for-loop黑客,试用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)
答案 0 :(得分:13)
如果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
。
答案 1 :(得分:12)
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
答案 2 :(得分:12)
尽管Reduce
更优雅,但for循环解决方案更快,似乎与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是正确的(再次)。当指数上升时,for循环在时间上呈线性,而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
答案 3 :(得分:2)
具有特征值分解的较短解决方案:
"%^%" <- function(S, power)
with(eigen(S), vectors %*% (values^power * t(vectors)))
答案 4 :(得分:2)
确实expm的包使用exponentiation by squaring。
在纯粹的r中,这可以像这样有效地完成,
class ErrorRaisingArgumentParser(argparse.ArgumentParser):
def parse_args(self, *args, **kwargs):
parse_args = super(ErrorRaisingArgumentParser, self).parse_args
return stderr_to_parser_error(parse_args, *args, **kwargs)
def exit(self, *args, **kwargs):
exit = super(ErrorRaisingArgumentParser, self).exit
return stderr_to_parser_error(exit, *args, **kwargs)
def error(self, *args, **kwargs):
error = super(ErrorRaisingArgumentParser, self).error
return stderr_to_parser_error(error, *args, **kwargs)
计时,
"%^%" <- 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
}
因此,正如预期的那样,它们的速度相同,因为它们使用相同的算法。看起来循环r代码的开销并没有显着差异。
因此,如果您不想使用expm,并且需要该性能,那么您可以使用它,如果您不介意查看命令式代码。
答案 5 :(得分:0)
简单的解决方案
`%^%` <- function(A, n) {
A1 <- A
for(i in seq_len(n-1)){
A <- A %*% A1
}
return(A)
}
答案 6 :(得分:-1)
这里的解决方案并不是非常有效但工作且易于理解/实施,在基地工作,并且几乎立即工作,尽管效率低于例如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
同样,如果效率至关重要,那么其他答案就更为优秀。但是,当你只想在划痕工作中计算一些矩阵功率时,这需要更少的编码开销和没有包。