Tournament bracket placement algorithm

前端 未结 10 587
生来不讨喜
生来不讨喜 2020-12-02 09:37

Given a list of opponent seeds (for example seeds 1 to 16), I\'m trying to write an algorithm that will result in the top seed playing the lowest seed in that round, the 2nd

相关标签:
10条回答
  • 2020-12-02 10:09

    With your assumptions, players 1 and 2 will play in the final, players 1-4 in the semifinals, players 1-8 in the quarterfinals and so on, so you can build the tournament recursively backwards from the final as AakashM proposed. Think of the tournament as a tree whose root is the final.

    In the root node, your players are {1, 2}.

    To expand the tree recursively to the next level, take all the nodes on the bottom layer in the tree, one by one, and create two children for them each, and place one of the players of the original node to each one of the child nodes created. Then add the next layer of players and map them to the game so that the worst newly added player plays against the best pre-existing player and so on.

    Here first rounds of the algorithm:

     {1,2}  --- create next layer
    
           {1, _}
          /         --- now fill the empty slots
     {1,2}
          \{2, _}
    
           {1, 4}   --- the slots filled in reverse order
          /         
     {1,2}
          \{2, 3}   --- create next layer again
    
    
                 /{1, _}
           {1, 4}
          /      \{4, _}
     {1,2}                  --- again fill
          \      /{2, _}
           {2, 3}
                 \{3, _}
    
                 /{1, 8}
           {1, 4}
          /      \{4, 5}    --- ... and so on
     {1,2}
          \      /{2, 7}
           {2, 3}
                 \{3, 6}
    

    As you can see, it produces the same tree you posted.

    0 讨论(0)
  • 2020-12-02 10:15

    I've come up with the following algorithm. It may not be super-efficient, but I don't think that it really needs to be. It's written in PHP.

    <?php
        $players = range(1, 32);
        $count = count($players);
        $numberOfRounds = log($count / 2, 2);
    
        // Order players.
        for ($i = 0; $i < $numberOfRounds; $i++) {
            $out = array();
            $splice = pow(2, $i); 
    
            while (count($players) > 0) {
    
                $out = array_merge($out, array_splice($players, 0, $splice));
                $out = array_merge($out, array_splice($players, -$splice));
    
            }            
    
            $players = $out;
        }
    
        // Print match list.
        for ($i = 0; $i < $count; $i++) {
            printf('%s vs %s<br />%s', $players[$i], $players[++$i], PHP_EOL);
        }
    ?>
    
    0 讨论(0)
  • 2020-12-02 10:15

    I also wrote a solution written in PHP. I saw Patrik Bodin's answer, but thought there must be an easier way.

    It does what darkangel asked for: It returns all seeds in the correct positions. The matches are the same as in his example, but in a prettier order, seed 1 and seed number 16 are on the outside of the schema (as you see in tennis tournaments).

    If there are no upsets (meaning a higher seeded player always wins from a lower seeded player), you will end up with seed 1 vs seed 2 in the final.

    It actually does two things more:

    1. It shows the correct order (which is a requirement for putting byes in the correct positions)

    2. It fills in byes in the correct positions (if required)

    A perfect explanation about what a single elimination bracket should look like: http://blog.playdriven.com/2011/articles/the-not-so-simple-single-elimination-advantage-seeding/

    Code example for 16 participants:

    <?php
    
    define('NUMBER_OF_PARTICIPANTS', 16);
    
    $participants = range(1,NUMBER_OF_PARTICIPANTS);
    $bracket = getBracket($participants);
    var_dump($bracket);
    
    function getBracket($participants)
    {
        $participantsCount = count($participants);  
        $rounds = ceil(log($participantsCount)/log(2));
        $bracketSize = pow(2, $rounds);
        $requiredByes = $bracketSize - $participantsCount;
    
        echo sprintf('Number of participants: %d<br/>%s', $participantsCount, PHP_EOL);
        echo sprintf('Number of rounds: %d<br/>%s', $rounds, PHP_EOL);
        echo sprintf('Bracket size: %d<br/>%s', $bracketSize, PHP_EOL);
        echo sprintf('Required number of byes: %d<br/>%s', $requiredByes, PHP_EOL);    
    
        if($participantsCount < 2)
        {
            return array();
        }
    
        $matches = array(array(1,2));
    
        for($round=1; $round < $rounds; $round++)
        {
            $roundMatches = array();
            $sum = pow(2, $round + 1) + 1;
            foreach($matches as $match)
            {
                $home = changeIntoBye($match[0], $participantsCount);
                $away = changeIntoBye($sum - $match[0], $participantsCount);
                $roundMatches[] = array($home, $away);
                $home = changeIntoBye($sum - $match[1], $participantsCount);
                $away = changeIntoBye($match[1], $participantsCount);
                $roundMatches[] = array($home, $away);
            }
            $matches = $roundMatches;
        }
    
        return $matches;
    
    }
    
    function changeIntoBye($seed, $participantsCount)
    {
        //return $seed <= $participantsCount ?  $seed : sprintf('%d (= bye)', $seed);  
        return $seed <= $participantsCount ?  $seed : null;
    }
    
    ?>
    

    The output:

    Number of participants: 16
    Number of rounds: 4
    Bracket size: 16
    Required number of byes: 0
    C:\projects\draw\draw.php:7:
    array (size=8)
      0 => 
        array (size=2)
          0 => int 1
          1 => int 16
      1 => 
        array (size=2)
          0 => int 9
          1 => int 8
      2 => 
        array (size=2)
          0 => int 5
          1 => int 12
      3 => 
        array (size=2)
          0 => int 13
          1 => int 4
      4 => 
        array (size=2)
          0 => int 3
          1 => int 14
      5 => 
        array (size=2)
          0 => int 11
          1 => int 6
      6 => 
        array (size=2)
          0 => int 7
          1 => int 10
      7 => 
        array (size=2)
          0 => int 15
          1 => int 2
    

    If you change 16 into 6 you get:

    Number of participants: 6
    Number of rounds: 3
    Bracket size: 8
    Required number of byes: 2
    C:\projects\draw\draw.php:7:
    array (size=4)
      0 => 
        array (size=2)
          0 => int 1
          1 => null
      1 => 
        array (size=2)
          0 => int 5
          1 => int 4
      2 => 
        array (size=2)
          0 => int 3
          1 => int 6
      3 => 
        array (size=2)
          0 => null
          1 => int 2
    
    0 讨论(0)
  • 2020-12-02 10:17

    For JavaScript code, use one of the two functions below. The former embodies imperative style & is much faster. The latter is recursive & neater, but only applicable to relatively small number of teams (<16384).

    // imperative style
    function foo(n) {
      const arr = new Array(n)
      arr[0] = 0
      for (let i = n >> 1, m = 1; i >= 1; i >>= 1, m = (m << 1) + 1) {
        for (let j = n - i; j > 0; j -= i) {
          arr[j] = m - arr[j -= i]
        }
      }
      return arr
    }
    

    Here you fill in the spots one by one by mirroring already occupied ones. For example, the first-seeded team (that is number 0) goes to the topmost spot. The second one (1) occupies the opposite spot in the other half of the bracket. The third team (2) mirrors 1 in their half of the bracket & so on. Despite the nested loops, the algorithm has a linear time complexity depending on the number of teams.

    Here is the recursive method:

    // functional style
    const foo = n =>
      n === 1 ? [0] : foo(n >> 1).reduce((p, c) => [...p, c, n - c - 1], [])
    

    Basically, you do the same mirroring as in the previous function, but recursively:

    • For n = 1 team, it's just [0].

    • For n = 2 teams, you apply this function to the argument n-1 (that is, 1) & get [0]. Then you double the array by inserting mirrored elements between them at even positions. Thus, [0] becomes [0, 1].

    • For n = 4 teams, you do the same operation, so [0, 1] becomes [0, 3, 1, 2].

    If you want to get human-readable output, increase each element of the resulting array by one:

    const readableArr = arr.map(i => i + 1)
    
    0 讨论(0)
提交回复
热议问题