YuK
YuK

Reputation: 29

R: Calculation combinations and variable iteration for loop

I want to calculate combinations in R.

I want to calculate and get results as in the below code, but in my code, the number in the for loop depends on the number of variables (e.g., length(ncomb)).

How do I set the number in a for loop? Or is there a better way to calculate the combinations that I want?

#Block
nblock = c(1,2,3)
num_nblock = length(nblock)

#Position
tol = c(1:6)
total = length(tol)


#Calculate number of Combination
#6C1*5C2*3C3
t1 = total
ncomb=c()
for (i in 1:num_nblock) {
  ncomb[i] = choose(t1,nblock[i])
  t1 = t1-nblock[i]
}


#Calculate Combination
Clist = data.frame()

for (i in 1:ncomb[1]) {
  comb1 = combn(total,nblock[1])
  remain = setdiff(tol,comb1[,i])  
  
  for (j in 1:ncomb[2]) {
    comb2 = combn(remain,nblock[2])
    remain2 = setdiff(remain,comb2[,j])
    
    for (k in 1:ncomb[3]) {
      comb3 = combn(remain2,nblock[3])
      
      ans = c(comb1[,i],comb2[,j],comb3[,k])
      Clist =rbind(Clist,ans)
      
    }
    
  }
}

#Result :Clist
   X1L X2L X3L X4L X5L X6L
1    1   2   3   4   5   6
2    1   2   4   3   5   6
3    1   2   5   3   4   6
4    1   2   6   3   4   5
5    1   3   4   2   5   6
6    1   3   5   2   4   6
7    1   3   6   2   4   5
8    1   4   5   2   3   6
9    1   4   6   2   3   5
10   1   5   6   2   3   4
.....
50   5   4   6   1   2   3
51   6   1   2   3   4   5
52   6   1   3   2   4   5
53   6   1   4   2   3   5
54   6   1   5   2   3   4
55   6   2   3   1   4   5
56   6   2   4   1   3   5
57   6   2   5   1   3   4
58   6   3   4   1   2   5
59   6   3   5   1   2   4
60   6   4   5   1   2   3

Upvotes: 0

Views: 339

Answers (1)

Yuan Yin
Yuan Yin

Reputation: 126

So here is an idea I have which may be harder to understand but solves your problem of having a variable number of for loops.

Before I show my code, let me explain the idea using your example of dividing 1 through 6 into blocks of 1, 2, and 3. As you said we can calculate the total number of combinations as 6C1*5C2*3C3=60. Now the question is how to fill up the 60 entries.

So if you think about a tree from Block 1 to 3, each branch of Block 1 correspond to 5C2 number of branches of Block2, and each branch of Block 2 correspond to 3C3 branch of Block 3. In this way, the total number of branches will be 6C1*5C2*3C3=60. Essentially how you wanna fill up the output matrix is to repeat each branch in Block 1 5C2*3C3 times, each branch in Block 2 3C3 time, and each branch in Block 3 should appear uniquely. To summarize you want to repeat each branch the number of times to the "cardinality" of Blocks to the right hand side.

This is what the following code is doing.

# ++++ Using your example and initialization ++++
# Block
nblock = c(1,2,3)
num_nblock = length(nblock)

# Position
tol = c(1:6)
total = length(tol)

t1 = total
ncomb=c()
for (i in 1:num_nblock) {
  ncomb[i] = choose(t1,nblock[i])
  t1 = t1-nblock[i]
}
# ++++++++

# Initialize result matrix
Clist = matrix(nrow = prod(ncomb), ncol = total)

# Block col ID: produce list of (1),(2,3),(4,5,6) as col ID of output matrix
block_cols = list()
start = 1
for (i in 1:num_nblock) {
  block_cols[[i]] = start:(start+nblock[i]-1)
  start = start + nblock[i]
}

# Fill the output matrix: iterate each (row,block) of matrix
for (i in 1:prod(ncomb)) {
  for (j in 1:num_nblock) {

    # First col ID of each block. In this example, always 1, 2, 4
    block_first_col_id = block_cols[[j]][1]

    # Fill the pos when its still NA
    if (is.na(Clist[i, block_first_col_id])) {

      # Filler is all combination having removed numbers appeared in left blocks
      remain = setdiff(tol, Clist[i, 0:(block_first_col_id-1)])
      com = combn(remain, nblock[j])

      # Key step: replicate to fill remaining cardinality
      filler = apply(com, 1, function(x) rep(x, each = prod(ncomb[(j+1):length(ncomb)])))
      
      # Store filler to output.
      # Filler may be a vector, in which case dim will return NULL
      filler_nrow = ifelse(is.null(dim(filler)[1]), 1, dim(filler)[1])
      Clist[i:(i + filler_nrow - 1), block_cols[[j]]] = filler

    }
  }
}

Upvotes: 3

Related Questions