Skip to the last edit
I have a list of Person
objects and I need to pair them off randomly with a randomize_pairs
function, each Person
object has a property target
of who they are paired with.
My constraints are that no-one can pair with themselves (duh) and they shouldn't be paired with the same person twice.
I got this working by making a temporary list and popping people off if the constraints were satisfied but I'm sure there is a cleaner/better/more pythonic way of doing this. Anyone know?
edit
I used the word "pair" a lot in this question but that was the wrong word. This is for a game where each person is assigned another person as a target so these are one way relationships where your target's target is not necessarily you.
Targets will only be changed at the beginning of each round so it's all at once.
edit 2
Here's what I've settled on for now though it can be improved so I'm leaving the question open.
def randomize_targets(players):
# get player count
count = len(players)
# copy the list of players
available_targets = list(players)
# shuffle the player order so if the last one has to have the same
# target twice it's not always the same player
players = list(players)
random.shuffle(players)
# loop over each player
for player in players:
# get the list of possible targets
potential_targets = [target for target in available_targets \
if target != player \
and target != player.target]
# try to pick one at random
try:
target = random.choice(potential_targets)
# if we have to, use the same target as last time
except IndexError:
pass
# remove the target from the available targets list
available_targets.remove(target)
# assign target
player.target = target
edit 3
I decided on this method even though I don't like the potentially long time looping until it finds a combo that works at least it always yields valid results
def randomize_targets2 (players):
targets = list(players)
# run this until it generates a valid result
valid = False
while not valid:
# randomize the targets
random.shuffle(targets)
# validate them
round_valid = True
for player, target in zip(players, targets):
round_valid = round_valid and player != target and player.target != target
valid = round_valid
# apply the validated targets
for player, target in zip(players, targets):
player.target = target