如何建立和存储这个较大的下三角矩阵以进行矩阵矢量乘法?

时间:2018-08-21 14:59:45

标签: r performance matrix out-of-memory matrix-multiplication

我需要创建一个具有特殊结构的下三角矩阵,然后进行矩阵矢量乘法。

矩阵由值k参数化。它的主要对角线是k ^ 0的向量,即1;第一个对角线是k ^ 1的向量,第i个对角线保持k ^ i

这是一个{x {1}}的5 x 5示例:

k = 0.9

我需要构造一个structure(c(1, 0.9, 0.81, 0.729, 0.6561, 0, 1, 0.9, 0.81, 0.729, 0, 0, 1, 0.9, 0.81, 0, 0, 0, 1, 0.9, 0, 0, 0, 0, 1), .Dim = c(5L, 5L)) # [,1] [,2] [,3] [,4] [,5] #[1,] 1.0000 0.000 0.00 0.0 0 #[2,] 0.9000 1.000 0.00 0.0 0 #[3,] 0.8100 0.900 1.00 0.0 0 #[4,] 0.7290 0.810 0.90 1.0 0 #[5,] 0.6561 0.729 0.81 0.9 1 大的矩阵并将其用于计算。我需要最有效的存储方法。有任何想法吗?

2 个答案:

答案 0 :(得分:3)

您不必总是显式地形成矩阵来进行矩阵向量或矩阵矩阵乘法。例如,没有人真正形成对角矩阵并将其用于这种计算。

矩阵和对角矩阵之间没有实质性差异。

因此,您可以将操作简化为一系列矢量加法。这是一个简单的R级实现。

MatVecMul <- function (y, k) {
  n <- length(y)
  z <- numeric(n)
  for (i in 1:n) z[i:n] <- z[i:n] + k ^ (i - 1) * y[1:(n - i + 1)]
  z
  }

与直接矩阵构建和计算的比较。

d <- structure(c(1, 0.9, 0.81, 0.729, 0.6561, 0, 1, 0.9, 0.81, 0.729, 
0, 0, 1, 0.9, 0.81, 0, 0, 0, 1, 0.9, 0, 0, 0, 0, 1), .Dim = c(5L, 5L))
set.seed(0); y <- runif(5)
c(d %*% y)
#[1] 0.8966972 1.0725361 1.3374064 1.7765191 2.5070750

MatVecMul(y, 0.9)
#[1] 0.8966972 1.0725361 1.3374064 1.7765191 2.5070750

可以轻松地用Rcpp替换R级“ for”循环。

library(Rcpp)
cppFunction("NumericVector MatVecMul_cpp (NumericVector y, double k) {
  int n = y.size();
  NumericVector z(n);
  int i; double *p1, *p2, *end = &z[n];
  double tmp = 1.0;
  for (i = 0; i < n; i++) {
    for (p1 = &z[i], p2 = &y[0]; p1 < end; p1++, p2++) *p1 += tmp * (*p2);
    tmp *= k;
    }
  return z;
  }")

MatVecMul_cpp(y, 0.9)
#[1] 0.8966972 1.0725361 1.3374064 1.7765191 2.5070750

让我们有一个基准。

v <- runif(1e4)
system.time(MatVecMul(y, 0.9))
#   user  system elapsed 
#  3.196   0.000   3.198 
system.time(MatVecMul_cpp(y, 0.9))
#   user  system elapsed 
#  0.840   0.000   0.841 

但是请注意:注意机器的精度。 k ^ (i - 1)太小时,加法期间可能会丢失所有有效数字。参见R: approximating `e = exp(1)` using `(1 + 1 / n) ^ n` gives absurd result when `n` is large。在带有k = 0.9的示例中,有k ^ 400 = 5e-19。因此,即使完整矩阵为10000 x 10000,其数值也比较低的三角形为banded。这意味着我们实际上可以更早地终止循环。但是我不会实现这一点。

答案 1 :(得分:0)

尝试一下:

k <- 0.9
n <- 5

d <- diag(n)
replace(k ^ (row(d) - col(d)), upper.tri(d), 0)

给予:

       [,1]  [,2] [,3] [,4] [,5]
[1,] 1.0000 0.000 0.00  0.0    0
[2,] 0.9000 1.000 0.00  0.0    0
[3,] 0.8100 0.900 1.00  0.0    0
[4,] 0.7290 0.810 0.90  1.0    0
[5,] 0.6561 0.729 0.81  0.9    1