【问题标题】:How to write a recursive function in R to generate dynamic nested for-loops?如何在 R 中编写递归函数来生成动态嵌套 for 循环?
【发布时间】:2016-04-27 06:11:40
【问题描述】:

我需要生成“n”个变量的所有可能组合,使变量总和为 100。变量的范围从 0 到 100,并且可以改变 1 的步长。考虑到,我在 R 中为此创建了一个代码n = 10,生成的数据框包含所有可能的组合。但是,我正在寻找使“n”动态化的可能性,以便用户可以灵活地在启动时将 n 作为参数传递。 任何帮助将不胜感激..

row <- list()
z = 1
for (a in seq(from = 0, to = 100, by = 1)) {
  for (b in seq(from = 0, to = 100, by = 1)) {
    for (c in seq(from = 0, to = 100, by = 1)) {
      for (d in seq(from = 0, to = 100, by = 1)) {
        for (e in seq(from = 0, to = 100, by = 1)) {
          for (f in seq(from = 0, to = 100, by = 1)) {
            for (g in seq(from = 0, to = 100, by = 1)) {
              for (h in seq(from = 0, to = 100, by = 1)) {
                for (i in seq(from = 0, to = 100, by = 1)) {
                  for (j in seq(from = 0, to = 100, by = 1)) {
                    if (a + b + c + d + e + f + g + h + i + j == 100) {
                      row[[z]] <- (c(a,b,c,d,e,f,g,h,i,j))
                      z = z + 1
                    }    
                  }
                }
              }
            }
          }
        }        
      }        
    }
  }
}

finaldata <- as.data.frame(do.call(rbind, row))

【问题讨论】:

  • 这是数论的问题,它被称为:数字100的分区(允许零)。您必须从具有 9 个部分的分区中递归具有 10 个部分的分区(依此类推......从 8 个部分...... 7 个部分......)!
  • 示例:第 10 部分的值可以是 0, 1, ..., 100。因此您可以从 0+“100 的分区”构建“100 分 10 部分”的分区9 部分”和 1+“99 部分”的部分和 ...
  • 即使你成功地完成了这个,你也不会喜欢结果。 R 中 for 循环的性能很糟糕。这么多嵌套的 for 循环会让 R 陷入困境。

标签: r recursion


【解决方案1】:
ptn <- function(n,k) if (k<=1L) list(n) else do.call(c,lapply(seq_len(n+1L)-1L,function(x) lapply(ptn(x,k-1L),c,n-x)));

演示:

ptn(1,1);
## [[1]]
## [1] 1
##

ptn(2,1);
## [[1]]
## [1] 2
##

ptn(1,2);
## [[1]]
## [1] 0 1
##
## [[2]]
## [1] 1 0
##

ptn(2,2);
## [[1]]
## [1] 0 2
##
## [[2]]
## [1] 1 1
##
## [[3]]
## [1] 2 0
##

ptn(3,2);
## [[1]]
## [1] 0 3
##
## [[2]]
## [1] 1 2
##
## [[3]]
## [1] 2 1
##
## [[4]]
## [1] 3 0
##

ptn(3,3);
## [[1]]
## [1] 0 0 3
##
## [[2]]
## [1] 0 1 2
##
## [[3]]
## [1] 1 0 2
##
## [[4]]
## [1] 0 2 1
##
## [[5]]
## [1] 1 1 1
##
## [[6]]
## [1] 2 0 1
##
## [[7]]
## [1] 0 3 0
##
## [[8]]
## [1] 1 2 0
##
## [[9]]
## [1] 2 1 0
##
## [[10]]
## [1] 3 0 0
##

生成你想要的分区集是不切实际的,即从10变成100。即使从5变成100也是推它:

system.time({ x <- ptn(100,5); });
##    user  system elapsed
##  32.594   0.141  32.790
length(x);
## [1] 4598126
system.time({ print(unique(sapply(x,sum))); });
## [1] 100
##    user  system elapsed
##   6.938   0.063   7.004
length(unique(x));
## [1] 4598126

在这里,我还编写了一个递归计算分区集大小的函数,而不会产生实际生成集的 CPU 或内存成本。注意:缓存是必不可少的,否则 CPU 命中将类似于完整生成算法。

ptnSize <- function(n,k,cache=new.env()) if (k<=1L) 1 else { key <- paste0(n,'/',k); if (is.null(cache[[key]])) cache[[key]] <- do.call(sum,lapply(seq_len(n+1L)-1L,function(x) ptnSize(x,k-1L,cache))); cache[[key]]; };

演示:

ptnSize(1,1);
## [1] 1
ptnSize(2,1);
## [1] 1
ptnSize(1,2);
## [1] 2
ptnSize(2,2);
## [1] 3
ptnSize(3,2);
## [1] 4
ptnSize(3,3);
## [1] 10
ptnSize(100,5);
## [1] 4598126
ptnSize(100,10);
## [1] 4.263422e+12

正如我们所见,您想要的分区集相当大。我估计它需要数百 TB 的内存来存储。

【讨论】:

  • 非常感谢..它可以根据我的需要完美运行..我意识到 ptn(100,10) 将是巨大的,也许我可以考虑以 2 或 4 的步骤增加分区..
【解决方案2】:
parti <- function(n, k) {
  if (n<0) { message("error: n<0"); return(NA) }
  if (k==1) return(matrix(n,1,1))
  M <- cbind(parti(n, k-1), 0)
  if (n>0) for (i in 1:n) M <- rbind(M, cbind(parti(n-i, k-1), i))
  M
}

parti(5, 3)

结果:

> parti(5, 3)
        i  
 [1,] 5 0 0
 [2,] 4 1 0
 [3,] 3 2 0
 [4,] 2 3 0
 [5,] 1 4 0
 [6,] 0 5 0
 [7,] 4 0 1
 [8,] 3 1 1
 [9,] 2 2 1
[10,] 1 3 1
[11,] 0 4 1
[12,] 3 0 2
[13,] 2 1 2
[14,] 1 2 2
[15,] 0 3 2
[16,] 2 0 3
[17,] 1 1 3
[18,] 0 2 3
[19,] 1 0 4
[20,] 0 1 4
[21,] 0 0 5

对于你的情况(n=100, k=10)你会遇到内存和时间问题,因为分区很多!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-19
    • 2021-08-11
    • 2018-08-12
    • 1970-01-01
    相关资源
    最近更新 更多