-3

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

在此处输入图像描述

上面的这张图片描述了我想要做的事情。底部只是使用 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)

}
4

1 回答 1

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

但有几件事效率较低:

  1. R 没有很好地进行尾递归,所以理论上这可能会填满调用堆栈并耗尽 R;和
  2. 这是我在关于迭代调用的评论中建议不要做的事情。rbind
  3. 它很容易出错:如果你用n < startor调用n==0,那么它会失败。

很可能:

  1. 如果你不能以factorial这种方式使用,你可以用prod(1:n). 下面的其余功能将使用此prod方法,交给您首选。
  2. 两者factorial都会prod以非常高的速度开始失败n,可能远远超出您将用于此作业的限制。在这些数字上,可能有必要进入gamma更高效的高阶乘计算领域n(并且可能需要在 R 完全支持 64 位整数之前)。

修复其中一些问题的迭代可能是

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

r坦率地说,可以通过一些巧妙的数学运算来摆脱计数器ij

但它仍然容易出现问题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循环的情况下生成它,但是它需要更多的矢量化魔法,这既超出了本作业的范围,也超出了我的时间来学习本课。

于 2019-06-10T16:24:06.993 回答