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

Hey monks... I have twenty four players, lets call them 'A'..'X'. A "team" consists of 4 "players". generating all the possible combinations from this list of 24 "players" is very easy using the Math::Combinatorics module:
my @n = 'A'..'X'; my $comboObj = Math::Combinatorics->new(count => 4, data => [@n], );
there are 10626 possible combinations. However I would like to place a unique restriction on the combinations. I only want the ones in which no two "players" repeat on a team. Example: The first combo returned is A, B, C, D. because of this any future combo containing AB, AC, AD, BC, BD, or CD is invalid. I believe the next valid combo would be A, E, F, G. What would be the best way of generating the combos in this manner, or filtering the output from Math:Combinatorics to filter the list? Thanks

Replies are listed 'Best First'.
Re: Unique Combos with Math::Combinatorics
by pc88mxer (Vicar) on Mar 02, 2008 at 00:44 UTC
    One straight-forward way is just to remember the past pairs that have appeared using a hash:

    my %seen; for each combination @c: #however you do this with Math::Combinatorics my $wanted = 1; CHECK: for my $i (0..$#c) { for my $j ($i+1..$#c) { if ($seen{$c[$i],$c[$j]} || $seen{$c[$j],$c[$i]}) { $wanted = 0; last CHECK; } } next unless $wanted; # emit combination here # go through pairs again to mark them as seen for my $i (0..$#c) { for my $j ($i+1..$#c) { $seen{$c[$i],$c[$j]} = 1; $seen{$c[$j],$c[$i]} = 1; } } }
      OK with your idea I came up with this:
      #!/usr/bin/perl #program will generate all unique combinations of 4 from a pool of pla +yers #without repeating any pair of players use strict; use Math::Combinatorics; my $count; my %seen; sub trackSeen{ my @players = @{$_[0]}; #For each set of 4 players the following positions are ones we do +not want to see again my @pairs = ($players[0].$players[1], #0,1 $players[0].$players[2], #0,2 $players[0].$players[3], #0,3 $players[1].$players[2], #1,2 $players[1].$players[3], #1,3 $players[2].$players[3] #2,3 ); foreach my $pair (@pairs){ $seen{$pair} = 1; } } sub notSeen{ my @players = @{$_[0]}; my @pairs = ($players[0].$players[1], #0,1 $players[0].$players[2], #0,2 $players[0].$players[3], #0,3 $players[1].$players[2], #1,2 $players[1].$players[3], #1,3 $players[2].$players[3] #2,3 ); foreach my $pair (@pairs){ if (scalar grep /$pair/, keys(%seen) ){ return 0; } } return 1; } my @n = 'A'..'X'; my $comboObj = Math::Combinatorics->new(count => 4, data => [@n]); print "combinations of 4 from: " . join(" ", @n)."\n"; print "-----------------------------" . ("----" x scalar(@n))."\n"; while ( my @combo = $comboObj->next_combination){ if ( notSeen(\@combo) == 1) { print join(', ', @combo) . "\n"; $count++; trackSeen(\@combo); } } print "$count combinations\n";
      The magic number for 24 players seems to be 35. I am sure this is not the most efficient way of doing it, but it works. Thanks. Output:
      A, B, C, D A, E, F, G A, H, I, J A, K, L, M A, N, O, P A, Q, R, S A, T, U, V B, E, H, K B, F, I, L B, G, J, M B, N, Q, T B, O, R, U B, P, S, V C, E, I, M C, F, H, N C, G, K, O C, J, L, P C, Q, U, W C, R, T, X D, E, J, N D, F, K, P D, G, H, L D, I, O, Q D, M, R, V D, S, T, W E, L, O, S E, P, Q, X F, J, O, T F, M, S, U F, V, W, X G, I, N, R H, M, O, W I, K, S, X J, K, Q, V L, N, U, X 35 combinations

        Unfortunately, that would mean that W only gets to play on 4 teams while everybody else gets to play on more and six players (A B C D F O) get to play on 7 teams. I doubt your players will consider that fair.

        Update: You can get pretty fair with this subset of teams:

        A, E, F, G A, H, I, J A, N, O, P A, Q, R, S A, T, U, V B, E, H, K B, F, I, L B, N, Q, T B, O, R, U B, P, S, V C, E, I, M C, G, K, O C, J, L, P C, Q, U, W C, R, T, X D, E, J, N D, G, H, L D, M, R, V D, S, T, W E, P, Q, X F, M, S, U F, V, W, X G, I, N, R H, M, O, W I, K, S, X J, K, Q, V L, N, U, X

        Which has half of the players on 5 teams (A B C E I N Q R S U V X) and half of them on 4 teams (D F G H J K L M O P T W).

        Update: And if you add the following teams:

        T, K, G, M D, F, H, P W, J, L, O

        Then every player gets to play on 5 teams and there are only 4 pairings that ever get repeated (DH KG WO JL).

        - tye        

        Why did you remove the loops in favour of all that typing and redundancy?

        And [ @n ] needlessly creates a copy of the array when you could simply use \@n.

Re: Unique Combos with Math::Combinatorics
by ambrus (Abbot) on Mar 02, 2008 at 11:18 UTC

    If you start from the lines of the affine plane for GF(5), you get 30 teams of 5 players each, chosen from 25 players:

    ABCDE AFKPU AGMSY AHOQX AILTW AJNRV BFOSW BGLQV BHNTU BIKRY BJMPX CFNQ +Y CGKTX CHMRW CIOPV CJLSU DFMTV DGORU DHLPY DINSX DJKQW EFLRX EGNPW E +HKSV EIMQU EJOTY FGHIJ KLMNO PQRST UVWXY

    If you omit the last player from each team (thus always omitting Y from the teams ey is in), you get the following 30 teams.

    ABCD AFKP AGMS AHOQ AILT AJNR BFOS BGLQ BHNT BIKR BJMP CFNQ CGKT CHMR +CIOP CJLS DFMT DGOR DHLP DINS DJKQ EFLR EGNP EHKS EIMQ EJOT FGHI KLMN + PQRS UVWX

    This is minimal maximal in that you cannot add a new team to it, but if you remove the team UVWX, you can add several more. If you keep adding the (lexicographically) first team that goes with the rest, you get these 35 teams:

    ABCD AFKP AGMS AHOQ AILT AJNR BFOS BGLQ BHNT BIKR BJMP CFNQ CGKT CHMR +CIOP CJLS DFMT DGOR DHLP DINS DJKQ EFLR EGNP EHKS EIMQ EJOT FGHI KLMN + PQRS AEUV BEWX FJUW GJVX KOUX LOVW

    Update: Similarly, you also get 35 teams if you start from nothing and keep adding the (lexicographically) first team that doesn't intersect any chosen team in two or more members (Update: this is the same result as ketema's got):

    ABCD AEFG AHIJ AKLM ANOP AQRS ATUV BEHK BFIL BGJM BNQT BORU BPSV CEIM +CFHN CGKO CJLP CQUW CRTX DEJN DFKP DGHL DIOQ DMRV DSTW ELOS EPQX FJOT + FMSU FVWX GINR HMOW IKSX JKQV LNUX

    The J source code to generate this is (takes several seconds to run):

    players =: a.{~65+i.24 teams =: (#~]-:"1~."1)~./:~/:~"1>,{4$<players good =: teams#~[:(*./"1) 1>: teams&(+/@:e."1"1 _) ,,&' '"1 (,{.@:good)^:35 i.0 4
      ambrus:

      Could you elaborate a little on what you're talking about? I did a google search on "affine GF(n)" but found sites wanting me to log in, abstracts of papers I most likely wouldn't understand, or articles that went whizzing above my head.

      And then I remembered mathworld! Here are a couple of links for people like me who might want to figure out a little bit of what ambrus is talking about:

      Affine Plane

      Finite Field (which discusses Galois field, which they abbreviate as GF(n)).

      ...roboticus

      Update: Fixed links, as suggested by ikegami and pKai

        Yep, I'm too lazy to explain finite fields and finite geometry now. Two books come into my mind that explain finite geometry – both are suitable for beginners as they don't have much prerequisites:

        • Hraskó András (editor), Új matematikai mozaik. TypeTEX Kiadó, Budapest, 2002.
        • Reiman István, A geometria határterületei. Gondolat, Budapest, 1986.

        I won't recommend any books on finite fields for there are plenty of those and this application really only needs the basics.

        I also forgot to link to the homepage of the J programming language.

      Trimming your original 30 teams more fairly, it is pretty easy to come up with a fair arrangement where each player gets to play on 5 teams. For example:

      AGMS BIKR CFNQ DHLP EJOT UVWX AHOQ AKPU ALTW ANRV BCDE BHTU BJPX BLQV CHMW CIPV CJSU DGOR DINX DJKQ EGNW EHKS EIMU FGIJ FLRX FMTV FOSW GKTX LMNO PQRS

      I strongly doubt that such can be broken into "rounds", however.

      - tye        

        The 30 lines of the affine plane certainly can be broken to 6 rounds or parallel lines. (

        ABCDE FGHIJ KLMNO PQRST UVWXY AFKPU AGMSY AHOQX AILTW AJNRV BFOSW BGLQV BHNTU BIKRY BJMPX CFNQY CGKTX CHMRW CIOPV CJLSU DFMTV DGORU DHLPY DINSX DJKQW EFLRX EGNPW EHKSV EIMQU EJOTY
        )

        Update: yeah, the example is wrong, but the statement is still true. Uh.

        ABCDE FGHIJ KLMNO PQRST UVWXY AFKPU BGLQV CHMRW DINSX EJOTY AGMSY BHNTU CIOPV DJKQW EFLRX AHOQX BIKRY CJLSU DFMTV EGNPW AILTW BJMPX CFNQY DGORU EHKSV AJNRV BFOSW CGKTX DHLPY EIMQU
Re: Unique Combos with Math::Combinatorics
by ambrus (Abbot) on Mar 02, 2008 at 12:16 UTC

    And here's a 36er.

    ELQV AEPR IKNR FHTW BEIS DFMQ CQRX CGJV DOPX FGSX BDHR MNWX GKLP ABCT +EHNU JMST EFKO BFLN AJOW ADGU BPVW AKMV AHIQ IMPU KQUW CHPS NPQT CDEW + DIJL GMOR TUVX LRSW DNSV CLOU FJRU BJKX

    I've found it by randomly adding new teams until the set was maximal, starting from no teams repeatedly until it got larger than 35.

    J source (modified from the above post; to see how slow it works, it prints the number of teams in a set it's found after each try):

    players =: a.{~65+i.24 teams =: (#~]-:"1~."1)~./:~/:~"1>,{4$<players good =: teams#~[:(*./"1) 1>: teams&(+/@:e."1"1 _) fmt =: ,/@:(,&' '"1)"2 rand =: 3 :'((,(?@:#{])@:good) ::])^:_ i.0 4' fmt 3 :('while. 1!:2&2#(t =: rand 0)'; '35>:#t'; 'do. end.'; 't') 0

    Update: I got lucky. Here's a set of 38 teams (generated using the same code except by raising the constant 35).

    BEHN KQTX BJMR GIPQ ELTW DNPV AGHU GLMO JNOT CEFI KOSW ADMX AJVW CLPU +AFLS HJSX DIOU BFGK FOPR BDQS CHQW HIKM ACKN KLRV NRSU FHTV EGRX CDGT + EMUV MPST FJQU EJKP FMNW BPWX COVX AEOQ ILNX ABIT

      Note that none of your solutions are fair because they all have some players playing on more teams than other players.

      Update: Also note that a likely scenario for applying this algorithm is a gathering of 24 people where they break up into teams. This requires that the list of possible teams can be divided into "rounds" where each round has every player on exactly one team. I'm curious what the maximal solutions are with and without the requirement of rounds (but with the requirement of being "fair", of course). Even the maximal number of teams without a requirement of fairness would be interesting.

      But from a practical standpoint, it may be even more valuable to come up with solutions that are more fair but don't require the number of repeated pairings to be zero, just to be minimized.

      - tye        

Re: Unique Combos with Math::Combinatorics (max)
by tye (Sage) on Mar 02, 2008 at 04:49 UTC

    The trick is to pick as many teams as possible. If you can get each player to be on 7 teams (42 teams total), then each player will have played with all but 2 of the other players. But doing that certainly isn't easy (while holding to your criteria). Picking teams at random, I get to about 32 teams and no other teams are possible, even though most players have a lot of players that they haven't played with yet.

    If I stick to having each player being on the same number of teams, then I usually get stuck after each player has been on two teams.

    My quick look for some systematic way of choosing teams to maximize the number of teams possible also got stuck after two teams each. I think it is probably possible to get to 3 teams per player, while I suspect that it will be possible to prove that 4 teams per player is impossible.

    - tye