如何使用R中的递归创建长度为n的所有2 ^ n个二进制序列的矩阵?

时间:2019-10-15 17:44:56

标签: r recursion combinatorics binary-data

我知道我可以使用expand.grid,但是我正在尝试学习实际的编程。我的目标是采用下面的内容,并使用递归获得长度为n的所有2 ^ n个二进制序列。

我可以在n = 1的情况下执行此操作,但是我不明白如何以递归方式使用相同的函数来获得更高尺寸的答案。

这里是n = 1的地方:

binseq <- function(n){
  binmat <- matrix(nrow = 2^n, ncol = n)
  r <- 0 #row counter
  for (i in 0:1) {
        r <- r + 1
        binmat[r,] <- i
    }
  return(binmat)
  }

我知道我可能必须在return语句中使用cbind。我的直觉说,return语句应类似于cbind(binseq(n-1), binseq(n))。但是,老实说,我现在完全迷失了。

所需的输出应该产生类似于expand.grid给出的内容:

n = 5
expand.grid(replicate(n, 0:1, simplify = FALSE))

它应该只是一个矩阵,因为递归地填充了binmat。

2 个答案:

答案 0 :(得分:3)

按照注释中的要求(如下),这里仅适用于二进制序列:

eg.binary <- function(n, digits=0:1) {
  if (n <= 0) return(matrix(0,0,0))
  if (n == 1) return(matrix(digits, 2))
  x <- eg.binary(n-1)
  rbind(cbind(digits[1], x), cbind(digits[2], x))
}

在处理了R无法正确处理的初始情况之后,它将处理n = 1的“基本情况”,然后递归获得所有n-1位二进制字符串,并将每个数字附加到每个他们。数字是前缀,以便二进制字符串以其通常的字典顺序(与expand.grid相同)结束。

示例:

eg.binary(3)
     [,1] [,2] [,3]
[1,]    0    0    0
[2,]    0    0    1
[3,]    0    1    0
[4,]    0    1    1
[5,]    1    0    0
[6,]    1    0    1
[7,]    1    1    0
[8,]    1    1    1

下面是一个一般的解释(具有更灵活的解决方案)。


将问题简化为将数组y的值附加到数据帧X的行的基本操作,将X的整个副本与每个值相关联(< em> via cbind)并附加整个批次( via rbind):

cross <- function(X, y) {
  do.call("rbind", lapply(y, function(z) cbind(X, z)))
}

例如,

cross(data.frame(A=1:2, b=letters[1:2]), c("X","Y"))
  A b z
1 1 a X
2 2 b X
3 1 a Y
4 2 b Y

(稍后再担心列名。)

此类数组y的递归解决方案假定您已经对列表的最后一个元素执行了所有这些操作。它必须从某处开始,这显然包括将数组转换为单列数据帧。因此:

  eg_ <- function(y) {
    n <- length(y)
    if (n <= 1) {
      as.data.frame(y) 
    } else {
      cross(eg_(y[-n]), y[[n]])
    }
  }

为什么这个有趣的名字?因为我们可能要进行一些后处理,例如给结果取好名字。这是一个更完整的实现:

eg <- function(y) {
  # (Define `eg_` here to keep it local to `eg` if you like)
  X <- eg_(y)
  names.default <- paste0("Var", seq.int(length(y)))
  if (is.null(names(y))) {
    colnames(X) <- names.default
  } else {
    colnames(X) <- ifelse(names(y)=="", names.default, names(y))
  }
  X
}

例如:

eg(replicate(3, 0:1, simplify=FALSE))
  Var1 Var2 Var3
1    0    0    0
2    1    0    0
3    0    1    0
4    1    1    0
5    0    0    1
6    1    0    1
7    0    1    1
8    1    1    1
eg(list(0:1, B=2:3))
  Var1 B
1    0 2
2    1 2
3    0 3
4    1 3

答案 1 :(得分:0)

显然,这是所需的递归代码:

binseq <- function(n){
  if(n == 1){
    binmat <- matrix(c(0,1), nrow = 2, ncol = 1)
  }else if(n > 1){
    A <- binseq(n-1)
    B <- cbind(rep(0, nrow(A)), A)
    C <- cbind(rep(1, nrow(A)), A)
    binmat <- rbind(B,C)
  }
  return(binmat)
  }

基本上对于n = 1,我们创建一个[0,1]矩阵。之后,对于每n个,我们在原始矩阵中添加一列0,然后分别添加一列1。然后我们对两个矩阵进行求和以获得最终乘积。所以我知道算法在做什么,但是我不太了解递归在做什么。例如,基于该算法,我不了解从n = 2到n = 3的步骤。