我遇到了以下问题:
假设我有三对序列:
{a_1, ..., a_N}, {A_1, ..., A_T}, {b_1, ..., b_N}, {B_1, ..., B_T}, {c_1, ..., c_N}, {C_1, ..., C_T}
。
我的目标是执行以下操作(不循环!):
for (i in 1:N) {
for (j in 1:N) {
for (k in 1:N) {
ret[i,j,k] <- \sum_{t=1}^T (a_i - A_t) * (b_j - B_t) * (c_k - C_t)
}}}
我不想循环的原因是可能有更多的序列对而不仅仅是那三个。我希望尽可能“有效和灵活”地构建代码。
如果我们只有两对序列,则问题非常简单,因为它简化为简单矩阵乘法((N x T)
的{{1}}矩阵和{(a_i - A_t)
的{{1}}矩阵1}}你将第一个与第二个的转置相乘。
但是一旦你有两对以上的序列,我不确定它是否可以在没有循环的情况下完成,因为(N x T)
的维数取决于序列对的数量......
------------------------------------------相关问题(11月2013年8月8日)------------------------------------------
由于 @ - mrip ,我成功实施了第一部分。但是,如果我想要以下内容,代码将如何改变:
(b_i - B_t)
output
是一些常见的双变量函数。是否有“通用”解决方案,或者您是否需要有关for (i in 1:N) {
for (j in 1:N) {
for (k in 1:N) {
ret[i,j,k] <- \sum_{t=1}^T foo(a_i, a_i - A_t) * foo(b_j, b_j - B_t) * foo(c_k, c_k - C_t)
}}}
我尝试使用简单的解决方案并简单地实现循环。当然,这既不灵活(因为我必须事先限制自己可能的对/维数),也不快(因为foo(a, a-A)
和foo(a, a-A)
都可能很大 - 尽管可能是这种情况a
只是标量和A
一些观察结果。)
我知道我可能会要求。但是很长一段时间以来我一直坚持这个问题......所以任何帮助都非常受欢迎。
答案 0 :(得分:4)
这实际上根本不需要任何矩阵乘法。它只需要采用外部产品,最终结果可以高效,简洁地利用R矩阵和多维数组的列主要布局。通过将循环中的乘积扩展为单独的加数,可以使计算更有效。这导致以下实现。
vecout<-function(...)as.vector(outer(...))
f2<-function(a,b,c,A,B,C){
N<-length(a)
t<-length(A)
ab<-vecout(a,b)
ret<-array(vecout(ab,c),c(N,N,N))*t
ret<-ret - ab * sum(C)
ret<-ret - vecout(a,rep(c,each = N)) * sum(B)
ret<-ret - rep(vecout(b,c) * sum(A),each=N)
ret<-ret + a * sum(B*C)
ret<-ret + rep(b * sum(A*C),each=N)
ret<-ret + rep(c * sum(A*B),each=N^2)
ret<-ret - sum(A*B*C)
ret
}
我们可以比较运行时间并检查正确性如下。这是天真的实现:
f1<-function(a,b,c,A,B,C){
N<-length(a)
ret<-array(0,c(N,N,N))
for(i in 1:N)
for(j in 1:N)
for(k in 1:N)
ret[i,j,k]<-sum((a[i]-A)*(b[j]-B)*(c[k]-C))
ret
}
优化版本的运行速度要快得多,并产生相同的结果,直至达到数值精度误差:
> a<-rnorm(100)
> b<-rnorm(100)
> c<-rnorm(100)
> A<-rnorm(200)
> B<-rnorm(200)
> C<-rnorm(200)
> system.time(r1<-f1(a,b,c,A,B,C))
user system elapsed
9.006 1.125 10.204
> system.time(r2<-f2(a,b,c,A,B,C))
user system elapsed
0.203 0.033 0.242
> max(abs(r1-r2))
[1] 1.364242e-12
如果每个序列超过三个,那么相同的想法就可以了。编写通用解决方案应该不会太难,事实上,用硬编码的3序列解决方案编写一般代码的代码可能很少,尽管需要花一些时间来考虑所有的索引操作恰到好处。
编辑:好的,无法抗拒。这是一个通用解决方案,具有任意数量的对,作为两个矩阵的列传递:
f3<-function(a,A){
subsets<-as.matrix(expand.grid(rep(list(c(F,T)),ncol(a))))
ret<-array(0,rep(nrow(a),ncol(a)))
for(i in 1:nrow(subsets)){
sub<-as.logical(subsets[i,])
temp<-Reduce(outer,as.list(data.frame(a[,sub,drop=F])),init=1)
temp<-temp*sum(apply(A[,!sub,drop=F],1,prod))
temp<-aperm(array(temp,dim(ret)),order(c(which(sub),which(!sub))))
ret<-ret+temp*(-1)^(sum(!sub))
}
ret
}
> system.time(r3<-f3(cbind(a,b,c),cbind(A,B,C)))
user system elapsed
0.258 0.056 0.303
> max(abs(r3-r1))
[1] 9.094947e-13
---------------------再次编辑(2013年11月8日)------------------- -
当数组A
,B
和C
很大时,上面的答案是最有效的。如果A
,B
和C
远小于a
,b
和c
,那么这里是一个替代,更简洁溶液:
f4<-function(a,A){
ret<-array(0,rep(nrow(a),ncol(a)))
for(i in 1:nrow(A)){
temp<- Reduce(outer,as.list(data.frame(a-rep(A[i,],each=nrow(a)))),init=1)
ret<-ret + as.vector(temp )
}
ret
}
在上述设置中,a
,b
,c
的长度为100
和A
,B
,{{1长度为C
,这比其他解决方案慢:
200
但是,如果> system.time(r3<-f3(cbind(a,b,c),cbind(A,B,C)))
user system elapsed
0.704 0.092 0.256
> system.time(r4<-f4(cbind(a,b,c),cbind(A,B,C)))
user system elapsed
65.824 19.060 3.553
> max(abs(r3-r4))
[1] 2.728484e-12
,A
和B
的长度为C
,则会更快:
1