Revisiting xmas kindle draw
Posted on Mon 18 November 2019 in analysis
Using jupyter notebook, to figure out how to draw our next christmas kindle.
A very simple kindle draw¶
Revisiting the simple kindle draw. And getting utterly confused.
import random
def simple_secret_santa(names,printit=False):
n = len(names)
s = random.sample(names,k=n)
out = []
for i,name in enumerate(s):
if i == len(s)-1:
to = s[0]
else:
to = s[i+1]
out.append(name+'-'+to)
if printit:
print(str(i+1) + " "+name + ' gives to ' + to)
return out
names = ['Ro','Pa','Ao','HaM',
'Fi', 'Ci',
'Te','Ja','Ch','HaC',
'Co','An','It',
'To',
'Et',
'Ro', 'Li']
s = simple_secret_santa(names,printit=True)
How random is this ?¶
First lets import matplotlib package so we can do some vizualisation.
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
import itertools
def monte_carlo(names,n=1000):
comb = {name1+'-'+name2:0 for name1,name2 in itertools.permutations(names,2)}
for i in range(n):
for couple in simple_secret_santa(names):
comb[couple] += 1
fig1 = plt.figure()
plt.plot([comb[k] for k in comb.keys()])
plt.xlabel('Iterations')
plt.ylabel('Count each combo')
#plt.axhline(y=n, color='r', linestyle='-')
plt.title('Xmas Kindle')
plt.show()
return comb
c = monte_carlo(names,n=1000000)
That is very odd, I would have expected each combination to appear roughly the same number of times, it looks like some combination are twice as frequent ? ... Let's try with a smaller set.
names2 = ['Jolene','Niamh','Sophie','Lorraine','Paschal']
#only 5 people in the draw, which should make 20 possible donor combinations
comb2 = {name1+'-'+name2:0 for name1,name2 in itertools.permutations(names2,2)}
print(len(comb2))
c2 = monte_carlo(names2,n=50000)
This looks about right.Let's used a reduced inital set.
c_Reduced = monte_carlo(names[:14],n=1000000)
Again this looks more as expected, after inspecting the initial names selection, I actually realized the name were not uniques.
duplicates = [names.count(n)>=2 for n in names]
print(duplicates)
# Replicating the issue with duplicate names
names3 = ['Jolene','Niamh','Sophie','Lorraine','Jolene']
#only 5 people in the draw, which should make 20 possible donor combinations, with a duplicate
comb3 = {name1+'-'+name2:0 for name1,name2 in itertools.permutations(names3,2)}
print(len(comb2))
c3 = monte_carlo(names3,n=50000)
#This explain the disruption in randomness so we can easily fix that by adding a check for name uniqueness
def isThereDuplicates(names):
dups = [names.count(n)>=2 for n in names]
if any(dups):
print("Duplicate names")
print(names[dups.index(True)])
return True
else:
return False
isThereDuplicates(names)
# Fixing the duplicates
def simple_secret_santa(names,printit=False):
n = len(names)
s = random.sample(names,k=n)
out = []
if not isThereDuplicates(names):
for i,name in enumerate(s):
if i == len(s)-1:
to = s[0]
else:
to = s[i+1]
out.append(name+'-'+to)
if printit:
print(str(i+1) + " "+name + ' gives to ' + to)
return out
Family draws¶
However for a family draw, you may want to avoid gift between siblings ( my particular family case ) or between partners (not considered here), so only gift to cousin are allowed, to do this I am introducing a dictionary, with the name of the parent as value and the kid name as key.
def simple_secret_santa_avoid_siblings(names):
givers = list(names.keys())
recipients = []
s = random.sample(givers,k=len(givers))
out = []
if not isThereDuplicates(list(givers)):
for i,name in enumerate(s):
possible = [r for r in names.keys() if (names[r] != names[name] and r not in recipients)]
recipient = random.sample(possible,k=1)[0]
recipients.append(recipient)
print(str(i+1) + ': ' + name + ' gives to ' + recipient + ' ::: '+ str(len(recipients))+':'+str(len(possible)))
out.append(name+'-'+recipient)
#Check
print('Recipients: '+str(len(recipients))+':: names:'+str(len(names.keys())))
names_siblings = {'Child1':'Parent1','Child2':'Parent1','Child3':'Parent1','Child4':'Parent1',
'Child5':'Parent2', 'Child6':'Parent2',
'Child7':'Parent3','Child8':'Parent3','Child9':'Parent3','Child10':'Parent3',
'Child11':'Parent4','Child12':'Parent4','Child13':'Parent4',
'Child14':'Parent5',
'Child15':'Parent6',
'Child16':'Parent7', 'Child17':'Parent7'}
simple_secret_santa_avoid_siblings(names_siblings)