我正在尝试优化设计用于计算两个方阵的元素乘积的双和的代码。假设我们有两个大小为 n , W 和 V 的平方矩阵。需要计算的对象是带有元素
的向量 B
简单来说:计算两个不同矩阵中两个不同行的逐个元素的乘积并取它们的和,然后在第二个矩阵的所有行上得到一个额外的总和(没有相同的索引)。
问题是,这个任务的计算复杂度看似 O(n 3 ),因为我们正在创建这个对象的长度, B ,是 n ,每个元素需要两个求和。这就是我想出的:
如果我们表示,则两个步骤将如下所示 和。
但是,写一个循环结果非常低效。 n = 100 快速工作(0.05秒)。但是,例如,当 n = 500 时(我们在这里讨论实际应用程序),平均计算时间为3秒,对于 n = 1000 ,它跳到22秒。
k 上的内部循环可以很容易地被一个和替换,但是外部的......在this question中,建议的解决方案是sapply
,但它意味着必须对所有元素进行求和。
这是我试图在宇宙热量死亡之前为大型 n 评估的代码。
set.seed(1)
N <- 500
x1 <- rnorm(N)
x2 <- rchisq(N, df=3)
bw1 <- bw.nrd(x1)
bw2 <- bw.nrd(x2)
w <- outer(x1, x1, function(x, y) dnorm((x-y)/bw1) )
w <- w/rowSums(w)
v <- outer(x2, x2, function(x, y) dnorm((x-y)/bw2) )
v <- v/rowSums(v)
Bij <- matrix(NA, ncol=N, nrow=N)
for (i in 1:N) { # Around 22 secs for N=1000
for (j in 1:N) {
Bij[i, j] <- (sum(w[i, ]*v[j, ]) - w[i, i]*v[j, i] - w[i, j]*v[j, j]) * (i!=j)
}
}
Bi <- rowSums(Bij)
专家 R 程序员如何对这种循环进行矢量化?
答案 0 :(得分:3)
如果不查看矩阵w
和v
的内容,可以使用一个矩阵乘法(tcrossprod
)替换简单的矩阵运算,转置( t
)和对角线提取:
Mat.ij <- tcrossprod(w, v) -
matrix(rep(diag(w), times = N), nrow = N) * t(v) -
w * matrix(rep(diag(v), each = N), nrow = N)
diag(Mat.ij) <- 0
all.equal(Bij, Mat.ij)
[1] TRUE
答案 1 :(得分:3)
<强>更新强>
事实上,考虑到你对B_ {ij}的表达,我们也可以做以下
diag(w) <- diag(v) <- 0
BBij <- tcrossprod(w, v)
diag(BBij) <- 0
range(rowSums(BBij) - Bi)
# [1] -2.220446e-16 0.000000e+00
range(BBij - Bij)
# [1] -6.938894e-18 5.204170e-18
因此,虽然有些明显,但对于您的目的而言,它可能也是一个有趣的观察结果,B_ {ij}和B_i都不依赖于W和V的对角线。
初步回答:
由于 其中W和V的对角线可以设置为零,V _ {。k}表示V的第k列的总和,我们有
diag(w) <- diag(v) <- 0
A <- w * v
rowSums(sweep(w, 2, colSums(v), `*`)) - rowSums(A) + diag(A)
,其中
range(rowSums(sweep(w, 2, colSums(v), `*`)) - rowSums(A) + diag(A) - Bi)
# [1] -1.110223e-16 1.110223e-16