如何使用 R 中的递归创建长度为 n 的所有 2^n 个二进制序列的矩阵?
How to create matrix of all 2^n binary sequences of length n using recursion in R?
我知道我可以为此使用 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 正在递归填充。
根据评论(下方)的要求,这里是二进制序列的有限实现:
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的"base case",然后递归获取所有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
的整个副本与每个值相关联(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]])
}
}
为什么这个名字好笑?因为我们可能想要做一些 post-processing,比如给结果起个好听的名字。这是一个更完整的实现:
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
显然这是所需的递归代码:
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。然后我们将两个矩阵rbind得到最终的产品。所以我明白了算法在做什么,但我并不真正理解递归在做什么。比如根据算法从n = 2到n = 3的步骤我就没看懂
我知道我可以为此使用 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 正在递归填充。
根据评论(下方)的要求,这里是二进制序列的有限实现:
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的"base case",然后递归获取所有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
的整个副本与每个值相关联(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]])
}
}
为什么这个名字好笑?因为我们可能想要做一些 post-processing,比如给结果起个好听的名字。这是一个更完整的实现:
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
显然这是所需的递归代码:
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。然后我们将两个矩阵rbind得到最终的产品。所以我明白了算法在做什么,但我并不真正理解递归在做什么。比如根据算法从n = 2到n = 3的步骤我就没看懂