Reputation: 6561
I have a list of people:
people<-c("Betty", "Joe", "Bob", "Will", "Frank")
I would like to randomly assign two people to each person (self-assignment is not allowed) and I would like each person to be assigned to another an equal amount of times (in the example above each person can only be assigned to another twice).
So for example the match up could be
Betty (Joe and Bob), Joe (Bob and Will), Bob (Will and Frank), Will (Frank and Betty)
Of course I have just used their ordering but it would be nice if this could be randomised.
Any ideas where to start?
Upvotes: 3
Views: 162
Reputation: 118789
New (easier) solution: Using shift
function from TaRifx
package from Ari B. Friedman
tt <- sample(people)
lapply(seq_len(length(tt))-1, function(x) shift(tt, x)[1:3])
# if you don't want it to be ordered, just add a sample(.)
lapply(seq_len(length(tt))-1, function(x) sample(shift(tt, x)[1:3]))
# [[1]]
# [1] "Bob" "Frank" "Betty"
#
# [[2]]
# [1] "Frank" "Betty" "Joe"
#
# [[3]]
# [1] "Betty" "Joe" "Will"
#
# [[4]]
# [1] "Joe" "Will" "Bob"
#
# [[5]]
# [1] "Will" "Bob" "Frank"
Old solution (for the idea):
I'd go this way. Basically, once you sample
"people", you can always go, 1,2,3, 2,3,4, 3,4,5, 4,5,1. So, let's do that. That is, generate these indices and then sample people and get the triplets.
# generate index
len <- length(people)
choose <- 3 # at a time
idx <- outer(seq(choose), seq(choose+2)-1, '+')
# [,1] [,2] [,3] [,4] [,5]
# [1,] 1 2 3 4 5
# [2,] 2 3 4 5 6
# [3,] 3 4 5 6 7
# sample people
tt <- sample(people)
# [1] "Joe" "Will" "Bob" "Frank" "Betty"
max.idx <- 2*choose + 1
tt[(len+1):max.idx] <- tt[seq(max.idx-len)]
# [1] "Joe" "Will" "Bob" "Frank" "Betty" "Joe" "Will"
tt[idx]
# [1] "Joe" "Will" "Bob" "Will" "Bob" "Frank" "Bob" "Frank" "Betty" "Frank"
# [15] "Betty" "Joe" "Betty" "Joe" "Will"
split(tt[idx], gl(ncol(idx), nrow(idx)))
# $`1`
# [1] "Joe" "Will" "Bob"
#
# $`2`
# [1] "Will" "Bob" "Frank"
#
# $`3`
# [1] "Bob" "Frank" "Betty"
#
# $`4`
# [1] "Frank" "Betty" "Joe"
#
# $`5`
# [1] "Betty" "Joe" "Will"
Now we can put this all in a function:
my_sampler <- function(x, choose) {
len <- length(x)
idx <- outer(seq(choose), seq(choose+2)-1, '+')
sx <- sample(x)
max.idx <- 2*choose + 1
sx[(len+1):max.idx] <- sx[seq(max.idx-len)]
split(sx[idx], gl(ncol(idx), nrow(idx)))
}
# try it out
my_sampler(people, 3)
my_sampler(people, 4) # 4 at a time
# if you want this and want a non-ordered solution, wrap this with `lapply` and `sample`
lapply(my_sampler(people, 3), sample)
Upvotes: 3
Reputation: 59970
Interesting problem. This will get you half-way there. The bit that is missing is the constraint on people being in an equal number of partnerships. If you want to randomly assign two people to someone except themselves this can be achieved in a one liner like so...
assigns <- lapply( people , function(x) { c( x , sample( people[!(people %in% x)] , 2 ) ) } )
First person will be assignee and last two will be assigned.
assigns
#[[1]]
#[1] "Betty" "Bob" "Will"
#[[2]]
#[1] "Joe" "Bob" "Frank"
#[[3]]
#[1] "Bob" "Betty" "Joe"
#[[4]]
#[1] "Will" "Betty" "Joe"
#[[5]]
#[1] "Frank" "Will" "Betty"
Upvotes: 0