考虑将一组元素作为输入向量并返回列表中的幂集的函数:
> pwr_set(letters[1:3])
[[1]]
character(0)
[[2]]
[1] "a"
[[3]]
[1] "b"
[[4]]
[1] "a" "b"
[[5]]
[1] "c"
[[6]]
[1] "a" "c"
[[7]]
[1] "b" "c"
[[8]]
[1] "a" "b" "c"
R定义:
pwr_set <- function(els){
n_els <- length(els)
out <- vector(mode="list",length = 2 ^ n_els)
out[[1]] <- character() # first element in power set is the empty set
listIdx <- 1L # start a listIdx
for(i in 1L:n_els){
for(j in 1L:listIdx){
listIdx <- listIdx + 1L
out[[listIdx]] <- c(out[[j]], els[i])
}
}
out
}
#include <Rcpp.h>
#include <Math.h>
using namespace Rcpp;
// [[Rcpp::export]]
List pwr_set_cpp(CharacterVector els) {
int n_els = els.size(); // size of set
int pwrset_card = pow(2,n_els); // number of subsets to make power set is 2^n_elements
List out(pwrset_card); // list for output
int listidx = 0; // to count through list indeces
out[0] = CharacterVector::create(); // first element of list represents empty set
CharacterVector tmp; // to hold new extended vector
for (int i=0; i < n_els; ++i) {
for (int j=0; j <= listidx; ++j) {
listidx++;
tmp = out[j];
tmp.push_back(els[i]);
out[listidx] = tmp;
}
}
return out;
}
> pwr_set_cpp(letters[1:3])
给我错误: attempt to set index 8/8 in SET_VECTOR_ELT
使用Google搜索并查看源代码here会让我认为我正在尝试索引超出SET_VECTOR_ELT
缓存的内容?这必然意味着我误解了如何在Rcpp
或类似的东西中逐步执行输入/输出循环。
任何帮助我理解这里的指导都会很精彩。提前致谢。
根据@Romain Francois和@nicola的回答/评论,关键的误解就是你在R
中逐步完成循环的方式很聪明! (至少我现在比以前更欣赏它了)。要在c++
中实现相同的功能,我必须将listidx
分解为counter
变量(这是j
的条件检查)和临时cnt2
这基本上记录了在当前计数器状态之上采取的j
步数。 counter
然后在使用当前值cnt2
传递每个出口后进行更新。
#include <Rcpp.h>
#include <Math.h>
using namespace Rcpp;
// [[Rcpp::export]]
List pwr_set_cpp(CharacterVector els) {
int n_els = els.size();
int pwrset_card = pow(2,n_els);
List out(pwrset_card);
out[0] = StringVector::create();
CharacterVector tmp;
int counter = 0;
for (int i=0; i < n_els; ++i) {
int cnt2 = counter; // capture counter state
for (int j =0; j <= counter; ++j) {
cnt2++; // capture counter + j steps
tmp = as<StringVector>(out[j]);
tmp.push_back(as<std::string>(els[i]));
out[cnt2] = tmp;
}
counter = cnt2; // update counter state
}
return out;
}
只是为了一个有趣的基准。虽然我确信有更有效的方法(使用相同的算法结构),因为我正在进行大量STRSXP
元素/向量的复制。
x <- letters[1:18]
pwr_set_bitecompile <- compiler::cmpfun(pwr_set) # R 3.2.0 !
microbenchmark::microbenchmark(
pwr_set(x),
pwr_set_bitecompile(x),
pwr_set_cpp(x))
Unit: milliseconds
expr min lq mean median uq max neval
pwr_set(x) 748.6553 820.0667 841.2828 834.1229 856.2436 1023.1324 100
pwr_set_bitecompile(x) 365.9969 480.9474 498.2100 503.5115 518.8562 596.8205 100
pwr_set_cpp(x) 155.9447 283.8771 295.8411 300.4865 314.0826 342.0261 100
答案 0 :(得分:2)
问题是你正在尝试将某些内容分配到out[8]
,并且该内容高于列表中的元素数量。
请参阅此添加的行:
Rprintf( "out.size() = %d, listidx = %d\n", out.size(), listidx );
out[listidx] = tmp;
你会得到:
> pwr_set_cpp(letters[1:3])
out.size() = 8, listidx = 1
out.size() = 8, listidx = 2
out.size() = 8, listidx = 3
out.size() = 8, listidx = 4
out.size() = 8, listidx = 5
out.size() = 8, listidx = 6
out.size() = 8, listidx = 7
out.size() = 8, listidx = 8
Error in pwr_set_cpp(letters[1:3]) :
tentative de modification de l'index 8/8 dans SET_VECTOR_ELT
Calls: sourceCpp ... withVisible -> eval -> eval -> pwr_set_cpp -> <Anonymous>
Exécution arrêtée
另见@nicola的评论。您对listidx
和j
做错了。如果溢出没有阻止它,你将获得无限循环。
可能令人困惑的是R代码:
for(j in 1L:listIdx){
listIdx <- listIdx + 1L
out[[listIdx]] <- c(out[[j]], els[i])
}
评估1L:listIdx
一次,以便在循环内部,您可以使用listIdx
执行其他操作。在C ++中并非如此。