查找不带包装的1:n数字的所有唯一组合

时间:2019-06-10 15:43:49

标签: r combn

我需要创建一个函数,为我提供1:n数字的所有可能组合。函数的自变量为n。我需要在不使用R内的combn函数或任何其他预安装函数的情况下执行此操作。

enter image description here

上面这张照片描绘了我想做的事。底部只是使用combn来检查上述功能是否有效。

我做了以下事情,但是显然这不是当前正确的方法。

pairwise_comp <- function(n) {

res <- matrix(nrow = 0, ncol = 2)
for (i in 1:n) {
  res <-rbind(res,cbind( i , i+1))
}


  return(res)

}

1 个答案:

答案 0 :(得分:3)

有几种方法可以解决这个问题,有些有效,有些可读(主观),但两种方法都不多。

例如,您可以递归进行操作,例如:

pairwise_recur <- function(n, start = 1) {
  if (n == start) return()
  nrows <- factorial(n) / (factorial(2) * factorial(n-2))
  res <- matrix(nrow = nrows, ncol = 2)
  rbind(
    cbind(rep(start, times = n - start),
          1 + start:(n-1)),
    pairwise_recur(n, start = start + 1)
  )
}
pairwise_recur(4)
#      [,1] [,2]
# [1,]    1    2
# [2,]    1    3
# [3,]    1    4
# [4,]    2    3
# [5,]    2    4
# [6,]    3    4

但是与此有关的几件事效率较低:

    R不能很好地执行尾递归,因此从理论上讲,这可以填充调用堆栈并耗尽R;和
  1. 这是我建议my comment中做的关于迭代调用rbind的事情。
  2. 这很容易出错:如果您使用n < startn==0进行呼叫,则会失败。

很有可能:

  1. 如果您无法以这种方式使用factorial,则可以将其与prod(1:n)混淆。下面的其余功能将使用此prod方法,这是您的首选。
  2. factorialprod都将以很高的n开始失败,很可能超出了您将用于此分配的限制。以这些数字,可能有必要进入gamma领域,对高n阶乘进行更高效的计算(并且可能有必要直到R完全兼容64位整数)。 / li>

解决其中一些问题的迭代器

pairwise_iter <- function(n) {
  nrows <- prod(1:n) / ( prod(1:2) * prod(1:(n-2)) )
  res <- matrix(nrow = nrows, ncol = 2)
  r <- 0
  for (i in 1:(n-1)) {
    for (j in (i+1):n) {
      r <- r + 1
      res[r,1] <- i
      res[r,2] <- j
    }
  }
  res
}
# same output

坦率地说,只要对ri进行一些聪明的数学运算,就可以摆脱j计数器。

但是在n < 3时仍然容易出现问题。可以通过以下方法缓解这种情况:

pairwise_iter2 <- function(n) {
  if (n <= 1) return(matrix(nrow = 0, ncol = 2))
  nrows <- prod(seq_len(n)) / ( prod(1:2) * prod(seq_len(n-2)) )
  res <- matrix(nrow = nrows, ncol = 2)
  r <- 0
  for (i in 1:(n-1)) {
    for (j in (i+1):n) {
      r <- r + 1
      res[r,1] <- i
      res[r,2] <- j
    }
  }
  res
}

pairwise_iter2(0)
#      [,1] [,2]
pairwise_iter2(1)
#      [,1] [,2]
pairwise_iter2(2)
#      [,1] [,2]
# [1,]    1    2
pairwise_iter2(3)
#      [,1] [,2]
# [1,]    1    2
# [2,]    1    3
# [3,]    2    3

一个区别(由开头的if / return减轻了)是使用seq_len:如果要一个长度为n的序列,则1:n仅与n >= 1一样准确。如果n为0,则1:0产生一个长度为2的向量,这不是您应该得到的;相反,seq_len(0)返回长度为0的向量,该向量更一致。


这在R的处理方式中仍然不是“高效”的。为此,您可以删除内部for循环并按向量进行分配:

pairwise_vec1 <- function(n) {
  if (n <= 1) return(matrix(nrow = 0, ncol = 2))
  nrows <- prod(seq_len(n)) / ( prod(1:2) * prod(seq_len(n-2)) )
  res <- matrix(nrow = nrows, ncol = 2)
  r <- 0
  for (i in 1:(n-1)) {
    vec <- seq_len(n - i)
    res[r + vec, 1] <- i
    res[r + vec, 2] <- i + vec
    r <- r + length(vec)
  }
  res
}

实际上甚至在外部for循环中也可能生成而没有,但是它需要更多的矢量化向导,这既超出了此任务的范围,也超出了我的时间专门用于本课。