`outer`足够快,我的双倍总和?

时间:2016-11-06 14:45:26

标签: r matrix sum

我正在尝试在r中评估以下双和:

enter image description here

我知道outer是一种快速的方法。我试过以下

sum(f(outer(X,X,function(x,y) (x-y)/c)))
虽然它似乎有效,但我不确定它与某些替代品相比有多快?首先是outer然后我的功能,反之亦然,它在速度方面是否有所不同?有更好的方法吗?

1 个答案:

答案 0 :(得分:3)

我想首先指出你可以把代码写成

sum(f(outer(x, x, "-") / c))

这减少了函数调用开销,因为R中的减法已经是一个函数。试试"-"(5, 2)

outer对您的应用来说足够快。唯一不恰当的情况是当函数f在0附近对称时,即f(-u) = f(u)。在这种情况下,最优计算仅对组合矩阵outer(x, x, "-")的下三角进行求和,并将该和乘以2以用于非对角线上的求和。最后,添加了对角线结果。

以下功能可以做到这一点。我们为组合矩阵的下三角部分(不包括对角线)生成(i, j)索引,然后outer(x, x, "-") / c的下三角部分将是dx <- (x[i] - x[j]) / c。现在,

  • 如果f是对称的,则结果为2 * sum(f(dx)) + n * f(0),这比outer快;
  • 如果f不对称,我们必须执行sum(f(dx)) + sum(f(-dx)) + n * f(0),这对outer没有任何优势。
## `x` is the vector, `f` is your function of interest, `c` is a constant
## `symmetric` is a switch; only set `TRUE` when `f` is symmetric around 0
g <- function (x, f, c, symmetric = FALSE) {
  n <- length(x)
  j <- rep.int(1:(n-1), (n-1):1)
  i <- sequence((n-1):1) + j
  dx <- (x[i] - x[j]) / c
  if (symmetric) 2 * sum(f(dx)) + n * f(0)
  else sum(f(dx)) + sum(f(-dx)) + n * f(0)
  }

在这里考虑一个小例子。我们假设c = 2和向量x <- 1:500。我们还考虑对称函数f1 <- cos和非对称函数f2 <- sin。让我们做一个基准:

x <- 1:500
library(microbenchmark)

我们首先考虑f1的对称情况。请务必为symmetric = TRUE设置g

microbenchmark(sum(f1(outer(x,x,"-")/2)), g(x, f1, 2, TRUE))

#Unit: milliseconds
#                        expr      min       lq     mean   median       uq
# sum(f2(outer(x, x, "-")/2)) 32.79472 35.35316 46.91560 36.78152 37.63580
#           g(x, f2, 2, TRUE) 20.24940 23.34324 29.97313 24.45638 25.33352
#      max neval cld
# 133.5494   100   b
# 120.3278   100  a 

我们在这里看到g更快。

现在考虑使用f2的非对称案例。

microbenchmark(sum(f2(outer(x,x,"-")/2)), g(x, f2, 2))

#Unit: milliseconds
#                        expr      min       lq     mean   median       uq
# sum(f2(outer(x, x, "-")/2)) 32.84412 35.55520 44.33684 36.95336 37.89508
#                 g(x, f2, 2) 36.71572 39.11832 50.54516 40.25590 41.75060
#      max neval cld
# 134.2991   100   a
# 142.5143   100   a

正如所料,这里没有优势。

是的,我们还想检查g是否正在进行正确的计算。用x <- 1:5来考虑一个小例子就足够了。

x <- 1:5

#### symmetric case ####

sum(f1(outer(x, x, "-") / 2))
# [1] 14.71313

g(x, f1, 2, TRUE)
# [1] 14.71313

#### asymmetric case ####

sum(f2(outer(x, x, "-") / 2))
# [1] 0

g(x, f2, 2)
# [1] 0

所以g是正确的。