如何在R中迭代生成组合?

4

我目前正在使用以下代码来生成组合:

combn(x,y)

但问题是该函数会存储所有可能的组合。我不想把它们存储,我只想通过类似循环之类的方式来生成它们。这对我的程序来说效率更高。是否有一种方法可以通过for循环生成组合而不是把它们全部存储?

我知道我在这里提过一个类似的问题: How do I find all possible subsets of a set iteratively in R?

但是在那个解决方案中,组合仍然被存储了...

这里有更多细节:

假设我想要找到4个元素中选2个的所有可能组合。使用combn(4,2)将会存储如下内容: ((1,4),(1,3),(1,2),(2,4),(2,3)(3,4))

我想要的结果是:

   loop{
       produces one combination at a time 
   }

1
是的,但在那个答案中,组合仍然被存储。 - user2560984
什么意思?它们是如何存储的? - GSee
我认为OP想要的是一个函数,它不仅输出所有可能的组合,而且还需要另一个变量,比如说i,来获取combn(x,y)的第i个值。虽然function(x,y,i)combn(x,y)[i]在技术上可以工作,但并不高效。 - nograpes
不确定是否适用,但通常使用data.table可以帮助提高性能:https://dev59.com/JnLYa4cB1Zd3GeqPc-rk - screechOwl
也许你可以在rosettacode找到一个算法。 - Karsten W.
阅读评论后,原帖是否在寻找随机遍历潜在组合的方法?唯一不存储所有组合的理由(我能想到的)是因为您不会对它们进行评估。这意味着原帖想要对一组组合进行采样。我很感兴趣听听! - MikeRSpencer
3个回答

2
为了逐个循环返回所有可能的组合,请执行以下操作:

#Sample data:
x <- c(1,2,3,4)
y <- 2
all_combinations <- combn(x,y)

#Return each value:
for (i in 1:ncol(all_combinations)) {
  print(all_combinations[,i])
}

但我不确定为什么您想在for循环中执行此操作,因为这样做相当慢。除了此应用程序外,是否有期望的最终输出?


我在我的问题中添加了更多的细节。 - user2560984
谢谢您提供的详细信息。这个答案应该适合您。例如,sample(1:4,2) - canary_in_the_data_mine
我认为这不起作用,因为它会随机生成每个组合,对吗? - user2560984
但是你正在存储值,这正是我不想要的... - user2560984
老实说,此时此刻我不知道你想达成什么目标。为什么你反对存储这些值呢? 如果你想要返回整个集合中的每个项目仅一次,那么你需要在第一次或每一次生成整个列表。 - canary_in_the_data_mine
我不想存储它的原因是因为我将处理非常大的数字。例如,假设我想计算1000个选择3个的组合数。存储那么多组合显然没有意义,因为我只需要逐个使用一个组合,然后继续下一个。 - user2560984

2
这里有一个建议,可以基于上一次循环使用的组合生成当前迭代的组合。
## Function definition
gen.next.cbn <- function(cbn, n){
    ## Generates the combination that follows the one provided as input
    cbn.bin      <- rep(0, n)
    cbn.bin[cbn] <- 1
    if (tail(cbn.bin, 1) == 0){
        ind <- tail(which(cbn.bin == 1), 1)
        cbn.bin[c(ind, ind+1)] <- c(0, 1)
    }else{
        ind <- 1 + tail(which(diff(cbn.bin) == -1), 1)
        nb  <- sum(cbn.bin[-c(1:ind)] == 1)
        cbn.bin[c(ind-1, (n-nb+1):n)] <- 0
        cbn.bin[ind:(ind+nb)]         <- 1
    }
    cbn <- which(cbn.bin == 1)
}

## Example parameters
n   <- 6
k   <- 3

## Iteration example
for (i in 1:choose(n, k)){
    if (i == 1){
        cbn <- 1:k
    }else{
        cbn <- gen.next.cbn(cbn, n)
    }
    print(cbn)
}

# [1] 1 2 3
# [1] 1 2 4
# [1] 1 2 5
# [1] 1 2 6
# [1] 1 3 4
# [1] 1 3 5
# [1] 1 3 6
# [1] 1 4 5
# [1] 1 4 6
# [1] 1 5 6
# [1] 2 3 4
# [1] 2 3 5
# [1] 2 3 6
# [1] 2 4 5
# [1] 2 4 6
# [1] 2 5 6
# [1] 3 4 5
# [1] 3 4 6
# [1] 3 5 6
# [1] 4 5 6

1
如果目的是将每个组合用作某些计算的输入,则可以使用combnFUN参数,类似于apply。看起来这不会存储组合,但仍将一次性返回应用于每个组合的函数的结果。

这里有一个带有虚拟函数的示例:

fct <- function(x, y){sum(x*y) + 2*x[1]}
y   <- 1:5
system.time(combn(1:20, 5, fct, y = y))
# user  system elapsed 
# 0.160   0.000   0.161 
system.time(apply(combn(1:20, 5), 2, fct, y = y))
# user  system elapsed 
# 0.224   0.000   0.222 

我在我的问题中添加了更多的细节。 - user2560984
这似乎是最好的解决方案,假设您想捕获每个返回值。否则,为什么不看一下combn的代码并根据您的需求进行调整呢? - Matthew Lundberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接