jonadab has asked for the wisdom of the Perl Monks concerning the following question:

This is more an algorithm question than Perl per se, but I consider it to be an interesting problem, so...

The basic form of Round Robin tournament schedule with all two-way matches is a reasonably well understood problem, so I won't go into a lot of detail here, but in general the constraints are that each team should quiz each other team at most (ideally, exactly) once, that every team plays the same total number of matches, that no team may play more than one match in any given round, that no team may play against itself, and that when team A plays team B, team B also plays team A at the same time. This is a solved problem.

The following is based on Florence's Algorithm. There may also be other solutions, but here is one.

sub roundrobin { my ($n, $rounds, $bye) = @_; if ($n % 2) { $rounds = $n; $bye = 1; # There will be $rounds rounds with exactly one bye for each team. } else { $rounds = $n - 1; $bye = 0; # There will be $rounds rounds, and every team will quiz every rou +nd. } my $m = $n; ++$m if $n % 2; # $m is the lowest even number greater t +han or equal to $n my %s = (teams => $n, rounds => $rounds, bye => $bye); # The working + schedule. # Fill in the working schedule with a nice diagonal pattern: for my $round (0 .. ($rounds - 1)) { for my $i (0 .. ($round-1)) { $s{table}->[$round]->[$i] = (($rounds + $round - $i + 1) + $m) % + $m; } for my $i ($round .. ($n-1)) { $s{table}->[$round]->[$i] = (($rounds + $round - $i) + $m) % $m; } } use Data::Dumper; print Dumper(+{tentative => \%s}); # Now, do knight-like moves with the 0 (bye) in the first row. # Every time the 0 lands on a number, put that number in the first c +olumn: my $byeround = 0; for my $i (reverse (1 .. ($m - 2))) { $byeround = (($byeround - 2) + $rounds) % $rounds; $s{table}->[$byeround]->[0] = $s{table}->[$byeround]->[$i] || 'Oop +sie'; $s{table}->[$byeround]->[$i] = 0; } # If m != n, then remove team n from all the games, and replace with + -1 (which means a bye, i.e., the team sits out that round): if (not $m == $n) { for my $i (0 .. $rounds-1) { $s{table}->[$i]->[$i] = -1; } } return \%s; }

That's a little too clever for easy maintenance, but it works (I'll spare you the code that checks the results). Well, it works as long as you only have two-way matches, and on modern hardware it's near-instant for any number of teams you're likely to have in this type of tournament. (Once you go past about fifty teams, it's impractical for them all to play eachother anyway, because it would be too many matches for each team, so instead you break into divisions and do double-elimination or somesuch. This algorithm can handle fifty teams in a matter of seconds.)

However, the quizzing program that I'm involved in doesn't just have two-way matches. There are also three-way quizzes. There are several reasons why the three-way quizzes are necessary:

So when constructing schedules for this type of quizzing, the constraints are a bit different...

I've been looking at this problem off and on for a couple of years, but I've never gotten anywhere very useful. Some numbers of teams (e.g., 9) are very easy to do by hand, but others are not, so I'd really like to have an algorithm for doing this. Ideas? What approach should I take? Should I predetermine how many two-way and three-way quizzes there should be, based on the number of teams, or should I approach it from a different direction?


Sanity? Oh, yeah, I've got all kinds of sanity. In fact, I've developed whole new kinds of sanity. Why, I've got so much sanity it's driving me crazy.

Replies are listed 'Best First'.
Re: Round-Robin with Three-way Matches
by kvale (Monsignor) on May 13, 2006 at 17:58 UTC
    I would model teams as vertices of a graph and quizzes as directed edges. Each edge would be decorated with the round number. Then the problem becomes one of creating a graph with uniform in and out degree at each vertex, ie, a kind of directed regular graph. Furthermore the round numbers must be picked to satisfy all the simultaneity contstraints and the constraints imposed by mapping related vertices to rooms. It is a mess :)

    In complicated problems like this, I would assign each violation of a constraint a numerical weight. Then every configuration of teams, quizzes, round numbers, and rooms would be graded by the sum of all the weights of the violations. Then the problem becomes one of minimizing the total weight over all configurations.

    To minimize, I would use a genetic algorithm (e.g., Algorithm::Evolutionary) or simulated annealing (Algorithm::Evolutionary has some simulated annealing capabilities as well). In generating mutations, i.e., new configurations, it is best to satisify the truly hard constraints automatically. So always create mutations that have the same number of one-quizzes, two-quizzes and three-quizzes for each team.

    Can't help you with the kitchen until you add more geometry to the spec :)

    -Mark