Rcpp R 向量大小限制(不允许负长度向量)

Posted

技术标签:

【中文标题】Rcpp R 向量大小限制(不允许负长度向量)【英文标题】:Rcpp R vector size limit (negative length vectors are not allowed) 【发布时间】:2020-06-27 11:48:30 【问题描述】:

根据https://***.com/a/48676389/3846213 和https://***.com/a/5234293/3846213,R 向量限制为 2^31 - 1 个项目。但是,我已经能够通过 Rcpp 以该数字的一半触发“不允许负长度向量”错误(这应该是尝试分配过大向量的标志)。这一切都源于我尝试调试基于 Rcpp 的 R 包 (https://github.com/tpq/propr/issues/13)。

library(Rcpp)

cppFunction("
IntegerVector test(int size) 
    int veclen = size * (size - 1) / 2;
    IntegerVector vec(veclen);
    return vec;

")

vec <- test(47000)
Error in test(47000) : negative length vectors are not allowed

47000^2 / 2 几乎是 2^31 的一半。 我在纯R中没有这样的问题,即vec &lt;- 1:(47000*(47000-1)/2)运行良好,所以Rcpp应该有一些特别的东西。

【问题讨论】:

@Jan 是的,你是对的。有溢出。我想,我现在可以删除我的问题了。 @Jan 您愿意发表一个答案,以便我接受。你帮了我很多忙。 不,@duckmayr 是对的。我可以复制你的问题。但这不仅仅是溢出。我们必须继续寻找。 @Jan 实际上是溢出,但只是由于操作顺序;请看下面我的回答 【参考方案1】:

问题是乘法溢出。当你这样做时

size * (size - 1) / 2

操作顺序会咬你,因为

size * (size - 1)

即使整个表达式没有溢出也可能溢出。 我们可以通过添加打印语句来看到这一点:

IntegerVector test(int size) 
    int veclen = size * (size - 1) / 2;
    Rcpp::Rcout << veclen << std::endl;
    IntegerVector vec(veclen);
    return vec;

vec <- test(47000)
# -1043007148

所以,我们可以通过改变我们执行该操作的方式来修复它:

IntegerVector test(int size) 
    int veclen = (size / 2) * (size - 1);
    Rcpp::Rcout << veclen << std::endl;
    IntegerVector vec(veclen);
    return vec;

没有问题

vec <- test(47000)
# 1104476500
str(vec)
# int [1:1104476500] 0 0 0 0 0 0 0 0 0 0 ...

更新:奇数问题

Eli Korvigo 在 cmets 中提出了一个关于奇数整数除法行为的精彩观点。为了说明,考虑用偶数 4 和奇数 5 调用函数

even <- 4
odd  <- 5

even * (even - 1) / 2
# [1] 6
odd  * (odd  - 1) / 2
# [1] 10

它应该分别创建长度为 6 和 10 的向量。 但是,会发生什么?

test(4)
# 6
# [1] 0 0 0 0 0 0
test(5)
# 8
# [1] 0 0 0 0 0 0 0 0

哦不! 5 / 2 在整数除法中是 2,而不是 2.5,所以在奇数情况下,这并不能完全满足我们的要求。 不过,幸运的是,我们可以通过简单的流控制轻松解决这个问题:

IntegerVector test2(int size) 
    int veclen;
    if ( size % 2 == 0 ) 
        veclen = (size / 2) * (size - 1);
     else 
        veclen = size * ((size - 1) / 2);
    
    Rcpp::Rcout << veclen << std::endl;
    IntegerVector vec(veclen);
    return vec;

我们可以看到这处理奇数和偶数情况都很好:

test2(4)
# 6
# [1] 0 0 0 0 0 0
test2(5)
# 10
# [1] 0 0 0 0 0 0 0 0 0 0

【讨论】:

感谢您重申我对该算术表达式的疑虑。 不幸的是,重新排序会给您带来另一种麻烦:整数除法。 veclen = size * (size - 1) / 2; 对奇数和偶数的计算相同。但是,(size / 2) * (size - 1) 对于奇数和偶数的行为不同。 @EliKorvigo 好点!很抱歉我最初忽略了这一点。我已经在答案的更新中解决了这个问题。

以上是关于Rcpp R 向量大小限制(不允许负长度向量)的主要内容,如果未能解决你的问题,请参考以下文章

不使用向量、大小或其他参数的递归回文检查

向量运算

与简单的 Rcpp 实现相比,为啥 zoo::rollmean 慢?

如何在Rstudio中调用Rcpp向量的对数函数

R - 大数据 - 向量超出向量长度限制

如何通过在 Rcpp 或 Armadillo 中将矩阵乘以向量元素来复制 R 的功能?