4

我一直在编写一个 R 函数来计算关于某些分布的积分,请参见下面的代码。

EVofPsi = function(psi, probabilityMeasure, eps=0.01, ...){

distFun = function(u){
 probabilityMeasure(u, ...)
}
xx = yy = seq(0,1,length=1/eps+1)
summand=0

for(i in 1:(length(xx)-1)){
  for(j in 1:(length(yy)-1)){
    signPlus = distFun(c(xx[i+1],yy[j+1]))+distFun(c(xx[i],yy[j]))
    signMinus = distFun(c(xx[i+1],yy[j]))+distFun(c(xx[i],yy[j+1]))
    summand = c(summand, psi(c(xx[i],yy[j]))*(signPlus-signMinus))
  }
}
sum(summand)
}

它工作正常,但速度很慢。通常听说用 C++ 等编译语言重新编写函数会加快速度,尤其是因为上面的 R 代码涉及双循环。我也是,使用 Rcpp:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
double EVofPsiCPP(Function distFun, Function psi, int n, double eps) {

  NumericVector xx(n+1);
  NumericVector yy(n+1);
  xx[0] = 0;
  yy[0] = 0;

  // discretize [0,1]^2
  for(int i = 1; i < n+1; i++) {
      xx[i] = xx[i-1] + eps;
      yy[i] = yy[i-1] + eps;
  }

  Function psiCPP(psi);
  Function distFunCPP(distFun);
  double signPlus;
  double signMinus;
  double summand = 0;

  NumericVector topRight(2); 
  NumericVector bottomLeft(2);
  NumericVector bottomRight(2);
  NumericVector topLeft(2);

  // compute the integral
  for(int i=0; i<n; i++){
    //printf("i:%d \n",i);
    for(int j=0; j<n; j++){
      //printf("j:%d \n",j);
      topRight[0] = xx[i+1];
      topRight[1] = yy[j+1];
      bottomLeft[0] = xx[i];
      bottomLeft[1] = yy[j];
      bottomRight[0] = xx[i+1];
      bottomRight[1] = yy[j];
      topLeft[0] = xx[i];
      topLeft[1] = yy[j+1];
      signPlus = NumericVector(distFunCPP(topRight))[0] +  NumericVector(distFunCPP(bottomLeft))[0];
      signMinus = NumericVector(distFunCPP(bottomRight))[0] + NumericVector(distFunCPP(topLeft))[0];
      summand = summand + NumericVector(psiCPP(bottomLeft))[0]*(signPlus-signMinus);
      //printf("summand:%f \n",summand);
    }
  }
  return summand;
}

我很高兴,因为这个 C++ 函数运行良好。但是,当我测试这两个函数时,C++ 的运行速度较慢:

sourceCpp("EVofPsiCPP.cpp")
pFGM = function(u,theta){
  u[1]*u[2] + theta*u[1]*u[2]*(1-u[1])*(1-u[2])
}
psi = function(u){
  u[1]*u[2]
}
print(system.time(
for(i in 1:10){
  test = EVofPsi(psi, pFGM, 1/100, 0.2)  
}
))
test

print(system.time(
  for(i in 1:10){
    test = EVofPsiCPP(psi, function(u){pFGM(u,0.2)}, 100, 1/100)  
  }
))

那么,有没有好心的专家愿意给我解释一下呢?我是否像猴子一样编码,有没有办法加快该功能?此外,我还有第二个问题。事实上,我可以用 SEXP 替换输出类型 double,也可以用 SEXP 替换参数类型 Function,它似乎没有任何改变。那么区别是什么呢?

非常感谢你,吉尔达斯

4

1 回答 1

13

其他人已经在评论中回答了。所以我只想强调一点:回调 R 函数是昂贵的,因为我们需要对错误处理格外小心。仅在 C++ 中使用循环并调用 R 函数并不会在 C++ 中重写您的代码。尝试重写psipFGM作为 C++ 函数并在这里报告发生了什么。

你可能会争辩说你失去了一些灵活性,你不能再使用任何 R 函数。对于这样的情况,我建议使用某种混合解决方案,您已经在 C++ 中实现了最常见的情况,否则回退到 R 解决方案。

至于另一个问题, aSEXP是一个 R 对象。这是 R API 的一部分。它可以是任何东西。当您Function从中创建 a 时(就像在创建带参数的函数时为您隐式完成的那样Function),您可以保证这确实是一个 R 函数。开销非常小,但在代码表现力方面的收益是巨大的。

于 2013-07-31T10:16:02.743 回答