Title: Generate a schedule for a round robin tournament in Python
My book Essential Algorithms, Second Edition describes an algorithm for creating a round robin schedule in detail. You can also find a description if the algorithm used here at University of Cambridge, NRICH: Tournament Scheduling.
Function generate_round_robin_odd returns a list of lists where results[i][j] gives the opponent of team i in round j of the round robin tournament. This function works only for an odd number of teams. The link above explains the method.
def generate_round_robin_odd(num_teams):
'''Return a list orlists where results[i][j] gives
the opponent of team i in round j.
Note: num_teams must be odd.
'''
# Make an empty list of lists.
results = [list(range(num_teams)) for i in range(num_teams)]
# Initialize the list of teams.
teams = list(range(num_teams))
# Start the rounds.
n2 = (num_teams - 1) // 2
for round in range(num_teams):
for i in range(n2):
team1 = teams[n2 - i]
team2 = teams[n2 + i + 1]
results[team1][round] = team2
results[team2][round] = team1
# Set the team with the bye.
results[teams[0]][round] = 'bye'
# Rotate the array.
rotate_list(teams)
return results
Helper function rotate_list rotates the items in the team list by moving the last item to the beginning of the list. The algorithm calls this routine after each round.
def rotate_list(teams):
'''
Rotate the entries one position.
Move the last item to the front of the list.
'''
teams.insert(0, teams.pop())
Function generate_round_robin_even returns a similar array for an even number of teams. It calls generate_round_robin_odd to make a schedule for a tournament with one fewer teams. It then expands the result array and replaces the byes with the additional team. See the link above for a more complete explanation.
def generate_round_robin_even(num_teams):
# Generate the result for one fewer teams.
results = generate_round_robin_odd(num_teams - 1)
# Copy the results into a bigger array,
# replacing the byes with the extra team.
results2 = [list(range(num_teams - 1)) for i in range(num_teams)]
for team in range(num_teams - 1):
for round in range(num_teams - 1):
if results[team][round] == 'bye':
# Change the bye to the new team.
results2[team][round] = num_teams - 1
results2[num_teams - 1][round] = team
else:
results2[team][round] = results[team][round]
return results2
Finally, function generate_round_robin simply calls functions generate_round_robin_odd or generate_round_robin_even depending on whether the number of teams is odd or even.
def generate_round_robin(num_teams):
if num_teams % 2 == 0:
return generate_round_robin_even(num_teams)
else:
return generate_round_robin_odd(num_teams)
To make the algorithm more useful, this example also includes two methods that print out schedules. The following print_matrix methods just prints the schedule's rows. The entry in row i column j gives the opponents for team i in round j.
def print_matrix(schedule):
for row in schedule:
print(row)
The following print_schedule method prints out the tournament's rounds in a more readable format.
def print_schedule(schedule):
num_teams = len(schedule)
num_rounds = len(schedule[0])
for round in range(num_rounds):
print(f'Round {round}')
for team1 in range(num_teams):
team2 = schedule[team1][round]
# Only print if it's a bye or team 1 < team 2.
if team2 == 'bye':
print(f' {team1} has a bye')
elif team1 < team2:
print(f' {team1} vs. {team2}')
This code loops through the rounds. For each round, it loops through the teams.
Notice that each matchup is in each round twice. If team A plays team B, then team B also plays team A. The method doesn't need to print them both, so it only prints an entry if team A has a bye or if team A is numerically less than team B.
Download the example to see all of the details.
|