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:
- A quiz rally must take place at a single location in a single
day, so it is desirable to have 6-8 matches irrespective of
the number of teams (not counting the semi-finals and finals,
which match off the four teams with the highest scores for the
day in a small single-elimination tournament at the end of the
day). Three-way quizzing allows each team to quiz more other
teams in the same number of matches than would otherwise
be possible.
- If there are more than about six teams in a given division (based
on age and skill level; currently we have three such divisions
in the North Central Ohio district; it is normal to have anywhere
from 3 to 12 or so teams in a division), it becomes difficult to
find enough quizmasters and rooms to have all teams quizzing in
two-way matches at the same time. Three-way quizzes allow
half again as many teams to quiz at once.
- There are three-way quizzes at the national level, so the district
would like to have them in order to better prepare the best quizzers
so that the district team will be able to do well at nationals.
- It's tradition.
So when constructing schedules for this type of quizzing, the constraints
are a bit different...
- There must be 6-8 rounds of quizzing.
- No team may quiz against itself.
- No team may quiz more than one match in any given round.
- Every team must have the same number of two-way quizzes,
the same number of three-way quizzes, and the same number
of byes (i.e., sit-out rounds) as every other team.
The number of byes per team should be kept to a minimum:
0 or 1 if possible, no more than 2 at most.
- Each team must quiz each other team at most once (unless there
are too few teams for this, in which case each team must quiz
each other team at least t and no more than t+1 times for some
value of t to be determined based on how large it needs to be
to get a good number of quizzes)
- The number of rooms needed should be reasonable: no more than
r rooms for up to r*3 teams if possible, and absolutely no more
than r+1 rooms for r*3+1 teams.
- No room may have more than one quiz taking place in it during
any given round: a room may have a two-way quiz with two
teams, a three-way quiz with three teams, or no quiz. Every
quiz must take place in a room.
- Whenever any team A has a two-way quiz against another team B,
team B must also have at the same time a two-way quiz against
team A, in the same room.
- Whenever any team A has a three-way quiz against two other
teams B and C, team B must also have a three-way quiz against
teams A and C, and team C must also have a three-way quiz
against teams A and B, at the same time, in the same room.
- The room where Nathan is acting as quizmaster must be located
as close to the kitchen area as possible.
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.
Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
Read Where should I post X? if you're not absolutely sure you're posting in the right place.
Please read these before you post! —
Posts may use any of the Perl Monks Approved HTML tags:
- a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
| |
For: |
|
Use: |
| & | | & |
| < | | < |
| > | | > |
| [ | | [ |
| ] | | ] |
Link using PerlMonks shortcuts! What shortcuts can I use for linking?
See Writeup Formatting Tips and other pages linked from there for more info.