rconstraintscombinatoricssampling

Integers into random order with constraints in R?


I want to sample integers in R, but also set constraints that for example, "3 comes always before 2" and "8 comes always before 5".

EDIT: The integers need not to be next to each other, for example, c(x,3,y,2,z) and c(8,x,y,z,5) are both acceptable, given these constraints.

Without any constraints one could just write:

sample(1:10)

...and that's it, but now we need something more. I like to think with tables or matrices so describing the relations with a matrix was my solution to this problem i.e.

# Create constraint matrix
constraint_matrix <- matrix(0, nrow = 10, ncol = 10)

# Set constraints
constraint_matrix[3, 2] <- 1  # 3 comes before 2
constraint_matrix[8, 5] <- 1  # 8 comes before 5

# Check, if generated output satisfies set constraints
check_constraints <- function(numbers, constraint_matrix) {
  for (i in 1:(length(numbers) - 1)) {
    if (constraint_matrix[numbers[i+1], numbers[i]] == 1) {
      return(FALSE)  # A constraint NOT satisfied
    }
  }
  return(TRUE)  # All constraints are satisfied
}

# Generate random order and check if constraints are satisfies
set.seed(123)
numbers <- sample(1:10)

while (!check_constraints(numbers, contstraint_matrix)) {
  numbers <- sample(1:10)
}

print(numbers)

My question is whether there is a better, optimized function to do this? Or does someone know a better algorithm for this task?


Solution

  • Check if the order follows constraints, if not then just swap their positions:

    set.seed(1); x <- sample(1:10)
    x
    # [1]  9  4  7  1  2  5  3 10  6  8
    
    #"3 comes always before 2" and "8 comes always before 5".
    cons <- list(c(3, 2), c(8, 5))
    
    for(i in seq_along(cons)){
      ix <- match(cons[[ i ]], x)
      if(ix[ 1 ] > ix[ 2 ]) x[ rev(ix) ] <- cons[[ i ]]
      }
    
    x
    # [1]  9  4  7  1  3  8  2 10  6  5