简体   繁体   中英

Finding all combinations using recursion in R

I'm having issue with returning values from recursive functions, hoping you could help me out. I have a list with a bunch of matrices, each matrix representing a set of possible combinations and generated using combn(). As an example, this could be 3 matrices inside the list:

# set 1 has 4 elements, do nCk = 4C1:
set1 <- c("b2","b3","b4","b5")
set1 <- combn(set1,1,simplify = T)  

# set 2 has 3 elements, choose 2:
set2 <- c("c1","c2","b2")
set2 <- combn(set2,2,simplify = T)

# set 3 has 10 elements, choose 1:
set3 <- combn(c(1:10),1, simplify = T)

If we were to print set2, for instance, it would have 2 rows (choose 2), and 3 columns (3C2 = 3):

> set2
     [,1] [,2] [,3]
[1,] "c1" "c1" "c2"
[2,] "c2" "b2" "b2"

I need get all possible 4-element combinations (1 element per set above). I can do this using a while loop and simulating a state machine, but that solution is clunky and makes for long code. I know this can be done using recursion as I was able to print the 120 combinations correctly (code below), but when trying to return them or save them in a variable, either I get a <font color="red">subscript out of bounds error or the results repeat thousands of times. I want to avoid global variables too, this will be embedded in a rather large project, so I'd prefer to avoid bloating my workspace with more variables than needed.

Of course, when deployed the number of sets will be dynamic, and the elements per set will change too. The sets aren't too big either, so I would love to implement a recursive approach!

Working code to print:

combb <- function(allsets, number, carry){
  if(number>length(allsets)){
    print(carry)
    return()
  } else{
    for(j in 1:length(allsets[[number]][1,])){
      newcarry <- c(carry, allsets[[number]][,j])
      number2 <- number + 1
      combb(allsets, number2, newcarry)
    }
  }
}

Thank you!

I found that it was very hard to carry the results back and forth, as it required flags and lists or a different solution. What I did instead was create a wrapper function where I created a local variable. The recursive function is defined inside, and accesses ("globally") the variable mentioned above. Then, that variable is returned by the wrapper:

combb <- function(allsets){

  carry <- integer(0)
  height <- 0L
  for (j in 1:length(allsets)) {
    height <- height + length(allsets[[j]][, 1])
  }

  output <- matrix(allsets[[1]][0, 1], nrow = height, ncol = 0)

  combb1 <- function(allsets, number, carry) {
    if(number > length(allsets)){
      output <<- cbind(output, carry, deparse.level = 0)
      return()
    } else{
      for (j in 1:length(allsets[[number]][1,])) {
        # Only add unique combinations (some combinations are vectors)
        if((TRUE %in% (allsets[[number]][, j] %in% carry)) == FALSE) {
          newcarry <- c(carry, allsets[[number]][, j], use.names = FALSE)
          number2 <- number + 1
          combb1(allsets, number2, newcarry)
        } else{
          next()
        }
      }
    }
  }

  combb1(allsets, 1, carry)

  return(output)
}

As you can see from that solution, recursion is neat (combb1 function) and doesn't clutter any of the global/workspace variables.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM