C++ 如何将R函数转换为C++;使用Rcpp的函数?

C++ 如何将R函数转换为C++;使用Rcpp的函数?,c++,r,function,rcpp,C++,R,Function,Rcpp,我定义了以下函数: pij = function(vec){ out = vec %*% t(vec) diag(out) = NA out = sum(out, na.rm = T) return(out) } 其中vec是一个向量,例如vec=rnorm(10^4,0,1) 我想知道C++如何使用RCPP包来编写这个函数。 < P>这是我发现的解决方案: library(Rcpp) library(inline) rcpp_inc = "using namespace R

我定义了以下函数:

pij = function(vec){
  out = vec %*% t(vec)
  diag(out) = NA
  out = sum(out, na.rm = T)
  return(out)
}
其中
vec
是一个向量,例如
vec=rnorm(10^4,0,1)


<>我想知道C++如何使用RCPP包来编写这个函数。

< P>这是我发现的解决方案:

library(Rcpp)
library(inline)

rcpp_inc = "using namespace Rcpp;
using namespace arma;"

src = "
vec vec1 = as<vec>(vecin);
mat out = vec1*trans(vec1);
out.diag().zeros();

return(wrap(accu(out)));
"
pij_rcpp = cxxfunction(signature(vecin="numeric"), src, plugin='RcppArmadillo', rcpp_inc)

我得到
pij_r
的经过时间为1.101,
pij_rcpp
的经过时间为1.323这是我找到的解决方案:

library(Rcpp)
library(inline)

rcpp_inc = "using namespace Rcpp;
using namespace arma;"

src = "
vec vec1 = as<vec>(vecin);
mat out = vec1*trans(vec1);
out.diag().zeros();

return(wrap(accu(out)));
"
pij_rcpp = cxxfunction(signature(vecin="numeric"), src, plugin='RcppArmadillo', rcpp_inc)

<> P>>代码>PIJYRR <代码>,1.323为<代码> PIJYRCPP < < /P> < P>这里是一个更好、更直接的版本,其中C++最终胜出:

// [[Rcpp::depends(RcppArmadillo)]]

#include <RcppArmadillo.h>

// [[Rcpp::export]]
double pij_cpp(const arma::vec & v) {
  arma::mat m = v * v.t();
  m.diag().zeros();
  double s = arma::as_scalar(arma::accu(m));
  return(s);
}

/*** R
library(rbenchmark)
set.seed(123)

pij <- function(vec){
  out <- vec %*% t(vec)
  diag(out) <- NA
  out <- sum(out, na.rm = T)
}

x <- rnorm(1000)

## make sure they are the same
all.equal(pij(x), pij_cpp(x))

## benchmark
benchmark(R=pij(x), Cpp=pij_cpp(x))
*/
/[[Rcpp::depends(RcppArmadillo)]]
#包括
//[[Rcpp::导出]]
双pij_cpp(常数arma::vec&v){
arma::mat m=v*v.t();
m、 diag().zeros();
双s=arma::as_标量(arma::acu(m));
申报表;
}
/***R
图书馆(rbenchmark)
种子集(123)
皮吉
更大的收获是。。。你看错了问题。你的R函数是
已经高度矢量化,并调用了大部分编译代码,所以没有太多的东西可以获得。

< P>这里是一个更好、更直接的版本,其中C++最终赢得了一点:

// [[Rcpp::depends(RcppArmadillo)]]

#include <RcppArmadillo.h>

// [[Rcpp::export]]
double pij_cpp(const arma::vec & v) {
  arma::mat m = v * v.t();
  m.diag().zeros();
  double s = arma::as_scalar(arma::accu(m));
  return(s);
}

/*** R
library(rbenchmark)
set.seed(123)

pij <- function(vec){
  out <- vec %*% t(vec)
  diag(out) <- NA
  out <- sum(out, na.rm = T)
}

x <- rnorm(1000)

## make sure they are the same
all.equal(pij(x), pij_cpp(x))

## benchmark
benchmark(R=pij(x), Cpp=pij_cpp(x))
*/
/[[Rcpp::depends(RcppArmadillo)]]
#包括
//[[Rcpp::导出]]
双pij_cpp(常数arma::vec&v){
arma::mat m=v*v.t();
m、 diag().zeros();
双s=arma::as_标量(arma::acu(m));
申报表;
}
/***R
图书馆(rbenchmark)
种子集(123)
皮吉
更大的收获是。。。你看错了问题。你的R函数是
已经高度矢量化并调用大部分已编译的代码,因此没有太多收获。

我建议首先考虑问题背后的数学。对于要计算的向量
v

sum_{i=1}^{N-1} sum_{j=i+1}^{N} 2 * v_i * v_j
您可以先创建矩阵
v_i*v_j
,但如果
v
很大,那么这可能会很昂贵。因此,在C++中直接实现双和比较容易:

#include <Rcpp.h>
// [[Rcpp::export]]
double pij_cpp(Rcpp::NumericVector vec) {
  double out{0.0};
  int N = vec.size();
  for (int i = 0; i < N; ++i) {
    for (int j = i + 1; j < N; ++j) {
      out += 2 * vec[i] * vec[j];
    }
  }
  return out;
}
这使我们能够通过从高端开始到低端来摆脱双环路:

#include <Rcpp.h>
// [[Rcpp::export]]
double pij_opt(Rcpp::NumericVector vec) {
  double out{0.0};
  double sum{0.0};
  int N = vec.size();
  for (int i = N -1; i > 0; --i) {
    sum += vec[i];
    out += sum * vec[i-1];
  }
  return 2 * out;
}

R和犰狳约为PAR(可能受内存分配限制)。第一个C++版本的速度比10个更快,第二个则是50000倍!p> 完整代码:

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

// [[Rcpp::export]]
double pij_arma(arma::vec vec) {
  arma::mat out = vec * vec.t();
  out.diag().zeros();
  return arma::accu(out);
}

// [[Rcpp::export]]
double pij_cpp(Rcpp::NumericVector vec) {
  double out{0.0};
  int N = vec.size();
  for (int i = 0; i < N; ++i) {
    for (int j = i + 1; j < N; ++j) {
      out += 2 * vec[i] * vec[j];
    }
  }
  return out;
}

// [[Rcpp::export]]
double pij_opt(Rcpp::NumericVector vec) {
  double out{0.0};
  double sum{0.0};
  int N = vec.size();
  for (int i = N -1; i > 0; --i) {
    sum += vec[i];
    out += sum * vec[i-1];
  }
  return 2 * out;
}


/*** R
pij = function(vec){
  out = vec %*% t(vec)
  diag(out) = NA
  out = sum(out, na.rm = T)
  return(out)
}


set.seed(42)
vec = rnorm(10^4,0,1)
pij(vec)

bench::mark(pij(vec), pij_cpp(vec), pij_opt(vec), pij_arma(vec))
*/
在R中使用矢量化函数速度更快,但仍然不如pij_opt快:

pij_opt_r2 <- function(vec) {
  N <- length(vec)
  vec <- rev(vec)
  sums <- cumsum(vec)
  2 * sum(vec[2:N] * sums[1:N-1])
}

pij_opt_r2我建议首先考虑问题背后的数学。对于要计算的向量
v

sum_{i=1}^{N-1} sum_{j=i+1}^{N} 2 * v_i * v_j
您可以先创建矩阵
v_i*v_j
,但如果
v
很大,那么这可能会很昂贵。因此,在C++中直接实现双和比较容易:

#include <Rcpp.h>
// [[Rcpp::export]]
double pij_cpp(Rcpp::NumericVector vec) {
  double out{0.0};
  int N = vec.size();
  for (int i = 0; i < N; ++i) {
    for (int j = i + 1; j < N; ++j) {
      out += 2 * vec[i] * vec[j];
    }
  }
  return out;
}
这使我们能够通过从高端开始到低端来摆脱双环路:

#include <Rcpp.h>
// [[Rcpp::export]]
double pij_opt(Rcpp::NumericVector vec) {
  double out{0.0};
  double sum{0.0};
  int N = vec.size();
  for (int i = N -1; i > 0; --i) {
    sum += vec[i];
    out += sum * vec[i-1];
  }
  return 2 * out;
}

R和犰狳约为PAR(可能受内存分配限制)。第一个C++版本的速度比10个更快,第二个则是50000倍!p> 完整代码:

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

// [[Rcpp::export]]
double pij_arma(arma::vec vec) {
  arma::mat out = vec * vec.t();
  out.diag().zeros();
  return arma::accu(out);
}

// [[Rcpp::export]]
double pij_cpp(Rcpp::NumericVector vec) {
  double out{0.0};
  int N = vec.size();
  for (int i = 0; i < N; ++i) {
    for (int j = i + 1; j < N; ++j) {
      out += 2 * vec[i] * vec[j];
    }
  }
  return out;
}

// [[Rcpp::export]]
double pij_opt(Rcpp::NumericVector vec) {
  double out{0.0};
  double sum{0.0};
  int N = vec.size();
  for (int i = N -1; i > 0; --i) {
    sum += vec[i];
    out += sum * vec[i-1];
  }
  return 2 * out;
}


/*** R
pij = function(vec){
  out = vec %*% t(vec)
  diag(out) = NA
  out = sum(out, na.rm = T)
  return(out)
}


set.seed(42)
vec = rnorm(10^4,0,1)
pij(vec)

bench::mark(pij(vec), pij_cpp(vec), pij_opt(vec), pij_arma(vec))
*/
在R中使用矢量化函数速度更快,但仍然不如pij_opt快:

pij_opt_r2 <- function(vec) {
  N <- length(vec)
  vec <- rev(vec)
  sums <- cumsum(vec)
  2 * sum(vec[2:N] * sums[1:N-1])
}

pij_opt_r2到目前为止你尝试了什么?我建议你研究一下RcppArmadillo的这种矩阵操作。所需的代码总量并没有那么高。c、 f.犰狳的
accu()
用于
sum()
.diag().zeros()
以去除对角线分量。感谢您的回复。到目前为止,我还没有尝试过任何东西,因为我不知道C++是如何工作的。我会看一下RCPP犰狳,为什么要把它移到C++?如果是出于性能原因,则不应创建一个大(对称)矩阵来对其元素求和。那是很多不必要的内存分配。我是个老古董。我建议你先学习一下C++是如何工作的。到目前为止你尝试了什么?我会建议RcppArmadillo研究这种矩阵运算。所需的代码总量并没有那么高。c、 f.犰狳的
accu()
用于
sum()
.diag().zeros()
以去除对角线分量。感谢您的回复。到目前为止,我还没有尝试过任何东西,因为我不知道C++是如何工作的。我会看一下RCPP犰狳,为什么要把它移到C++?如果是出于性能原因,则不应创建一个大(对称)矩阵来对其元素求和。那是很多不必要的内存分配。我是个老古董。我建议你先学习一下C++是如何工作的。“我建议先思考一下问题背后的数学问题。”对于任何问题,无论使用何种语言,第一种算法都无法改进。“我建议先考虑问题背后的数学。”很好。无论使用哪种语言,都不存在第一种算法无法改进的问题。