Replace cyclic rotation with Latin-square design so groups meet once
The fixed +1/-4 offsets in get_courses only produced a collision-free rotation for certain group counts; for n=9 (three groups per course) the pairwise gaps collapsed and 9 pairs of groups met twice. Replace it with a resolvable transversal design (_rotation_hosts): slots form a k x 3 grid, each course is a parallel class partitioning all groups into transversal tables of three, guaranteeing every pair meets at most once for any n >= 9. n=3/6 are combinatorially impossible and fall back to a degenerate same-row rotation that still satisfies the structural invariants. Add a regression test asserting no pair meets more than once.
This commit is contained in:
@@ -73,6 +73,20 @@ class TestGetCourses:
|
||||
assert len(guests) == 3
|
||||
assert {g % 3 for g in guests} == {0, 1, 2}
|
||||
|
||||
@pytest.mark.parametrize("n", [n for n in GROUP_COUNTS if n >= 9])
|
||||
def test_no_two_groups_meet_more_than_once(self, n):
|
||||
# The whole point of the Latin-square rotation: for n >= 9 (>= 3 groups per
|
||||
# course) every pair of groups shares a table at most once across the evening.
|
||||
# (n = 3 and 6 are combinatorially impossible and deliberately excluded.)
|
||||
hosts_of = {i: set(get_courses(i, n)) for i in range(n)}
|
||||
meetings = Counter()
|
||||
for host in range(n):
|
||||
guests = sorted(g for g in range(n) if host in hosts_of[g])
|
||||
for a, b in itertools.combinations(guests, 2):
|
||||
meetings[(a, b)] += 1
|
||||
repeats = {pair: c for pair, c in meetings.items() if c > 1}
|
||||
assert repeats == {}, f"pairs meeting more than once: {repeats}"
|
||||
|
||||
|
||||
class TestFastTotalTime:
|
||||
def test_matches_hand_computed_value_n3(self):
|
||||
|
||||
Reference in New Issue
Block a user