Secret Santa in R

Our office just exchanged presents for Secret Santa, a tradition where each person is randomly assigned someone else to give an anonyous gift. One of the challenges of Secret Santa is keeping the pairs of gift-givers and receivers both random and secret. How can you do this while also taking part yourself? Using R, of course!

Firstly, recruit people! Write their names down, one per line, in a text file. The order doesn’t matter. For example, we might have a file called santa_names.txt, with contents as follows:

Tom
Dick
Harry
Jane
Leslie
Susan
Alex
Sam
Chris

Then read these names into R.

names <- readLines('santa_names.txt')

We now have a character vector called names.

The key to a truly random pairing of gift givers and recipients is that it shouldn’t depend on any systematic order, such as the order people signed up, or the alphabetical order of their names.

Here is one way we might try to do it. First, randomly reorder the names. Then, have every person in this list give to the next person on the list, with the very last person then giving to the first.

names2 <- sample(names)
data.frame(sender = names2,
           recipient = c(tail(names2, -1), names2[1]))
##   sender recipient
## 1   Dick     Susan
## 2  Susan    Leslie
## 3 Leslie     Chris
## 4  Chris      Alex
## 5   Alex      Jane
## 6   Jane       Tom
## 7    Tom       Sam
## 8    Sam     Harry
## 9  Harry      Dick

In R, the sample() function draws randomly from a vector, and by default it does this without replacement, so we don’t have to worry about missing anybody off or somebody appearing twice. We can use it as a quick way to reorder the list of names at random. The tail command with argument -1 will select every element in the list except the first.

This works in the sense that everybody gives and receives one gift and nobody gives to themselves, but is not entirely random or secret. If you find out that Susan gave to Leslie, then you know for a fact that Leslie didn’t give to Susan. With enough of these titbits of information you could reconstruct the entire list. Ideally we want no discernable pattern.

Why not just randomly sample twice from the names list?

data.frame(sender = sample(names),
           recipient = sample(names))
##   sender recipient
## 1  Harry       Tom
## 2  Chris      Dick
## 3 Leslie       Sam
## 4  Susan    Leslie
## 5   Dick      Alex
## 6   Jane      Jane
## 7    Sam     Susan
## 8   Alex     Harry
## 9    Tom     Chris

There’s a problem. Jane has been assigned to herself! That won’t do! The solution is quite straightforward, though: just keep sampling until this doesn’t happen. We can keep the order of senders fixed and vary the order of the recipients until nobody gives to themselves, like so.

sender <- sample(names)
recipient <- sample(names)
i <- 1
while (any(sender == recipient)) {
  i <- i + 1
  recipient <- sample(names)
}
data.frame(sender, recipient)
##   sender recipient
## 1  Harry      Jane
## 2  Chris      Alex
## 3 Leslie       Sam
## 4  Susan      Dick
## 5   Dick     Chris
## 6   Jane       Tom
## 7    Sam    Leslie
## 8   Alex     Harry
## 9    Tom     Susan

Success! Everybody gives and receives a gift, nobody gives to themselves and there is no particular pattern. (Unlike the first method, Sam gives to Leslie even though Leslie already gives to Sam.)

If you’re curious, you can find out how many attempts it took to find a valid set of pairs:

i
## [1] 2

Now, how do you let everybody know who they have been assigned, without revealing it anybody else? (Including you!) We can write a text file for every person, with the name of the file corresponding to the giver and the contents of the file revealing the recipient.

for (i in seq_along(names)) {
  writeLines(recipient[i], paste0(sender[i], '.txt'))
}

If you were feeling really clever, you could automate the e-mail sending from R as well, but it should be easy enough to attach and send the files by hand without peeking.

Putting it all together:

names <- readLines('santa_names.txt')
sender <- sample(names)
recipient <- sample(names)
while (any(sender == recipient)) {
  recipient <- sample(names)
}
for (i in seq_along(names)) {
  writeLines(recipient[i], paste0(sender[i], '.txt'))
}

Merry Christmas!

Secret Santa at Warwick