比赛支架放置算法

时间:2011-12-02 10:57:59

标签: algorithm language-agnostic brackets tournament

给出一个对手种子列表(例如种子1到16),我正在尝试编写一种算法,该算法将导致头号种子在该轮中播放最低种子,第二粒种子播放第二低种子等等。

将1和16,2和15等分组为“匹配”相当容易,但我还需要确保较高种子将在后续轮次中播放较低种子。

具有正确位置的示例括号:

1 vs 16
            1 vs 8
8 vs 9
                        1 vs 4
4 vs 13
            4 vs 5
5 vs 12
                                    1 vs 2
2 vs 15
            2 vs 7
7 vs 10
                        2 vs 3
3 vs 14
            3 vs 6
6 vs 11

如你所见,种子1和种子2只在决赛中见面。

10 个答案:

答案 0 :(得分:13)

此JavaScript返回一个数组,其中每个偶数索引播放下一个奇数索引

function seeding(numPlayers){
  var rounds = Math.log(numPlayers)/Math.log(2)-1;
  var pls = [1,2];
  for(var i=0;i<rounds;i++){
    pls = nextLayer(pls);
  }
  return pls;
  function nextLayer(pls){
    var out=[];
    var length = pls.length*2+1;
    pls.forEach(function(d){
      out.push(d);
      out.push(length-d);
    });
    return out;
  }
}

> seeding(2)
[1, 2]
> seeding(4)
[1, 4, 2, 3]
> seeding(8)
[1, 8, 4, 5, 2, 7, 3, 6]
> seeding(16)
[1, 16, 8, 9, 4, 13, 5, 12, 2, 15, 7, 10, 3, 14, 6, 11]

答案 1 :(得分:10)

根据你的假设,球员1和2将参加决赛,半决赛中的球员1-4,四分之一决赛中的球员1-8等等,所以你可以在AakashM提出的决赛中以递归的方式建立锦标赛。把锦标赛想象成一棵树,其根源是决赛。

在根节点中,您的玩家为{1,2}。

要将树递归地扩展到下一个级别,请逐个获取树中底层的所有节点,并为每个节点创建两个子节点,并将原始节点的一个播放器放置到每个节点中。创建的子节点。然后添加下一层玩家并将它们映射到游戏中,以便最新添加的最差玩家与最好的预先存在的玩家对战等等。

这是算法的第一轮:

 {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}

如您所见,它会生成与您发布的树相同的树。

答案 2 :(得分:4)

我想出了以下算法。它可能不是超级高效的,但我认为它确实不需要。它是用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);
    }
?>

答案 3 :(得分:3)

我还写了一个用PHP编写的解决方案。我看到Patrik Bodin的回答,但认为必须有一个更简单的方法。

它实现了darkangel所要求的:它将所有种子返回到正确的位置。匹配与他的示例相同,但在更漂亮的顺序中,种子1和种子编号16位于模式的外部(正如您在网球锦标赛中看到的那样)。

如果没有沮丧(意味着较高种子的球员总是从低种子球员中获胜),你将在决赛中以种子1和种子2结束。

它实际上做了两件事:

  1. 显示正确的顺序(这是将byes放在正确位置的要求)

  2. 它会在正确的位置填写(如果需要)

  3. 关于单个消除括号应该是什么样子的完美解释:http://blog.playdriven.com/2011/articles/the-not-so-simple-single-elimination-advantage-seeding/

    16名参与者的代码示例:

    <?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;
    }
    
    ?>
    

    输出:

    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
    

    如果您将16改为6,则会得到:

    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
    

答案 4 :(得分:1)

# Here's one in python - it uses nested list comprehension to be succinct:

from math import log, ceil

def seed( n ):
    """ returns list of n in standard tournament seed order

    Note that n need not be a power of 2 - 'byes' are returned as zero
    """

    ol = [1]

    for i in range( ceil( log(n) / log(2) ) ):

        l = 2*len(ol) + 1

        ol = [e if e <= n else 0 for s in [[el, l-el] for el in ol] for e in s]

    return ol

答案 5 :(得分:0)

  • 按照播种标准每轮排序小组
  • (如果一轮中有n支队伍)在第i位的球队与队伍n-i + 1队比赛

答案 6 :(得分:0)

因为在搜索主题时会出现这种情况,并且无法找到解决问题的另一个答案并将种子放在一个更漂亮的&#34;顺序,我将从darkangel添加我的PHP代码版本。我还补充说,有可能向更高级别的种子玩家提供再见。

这是在OO环境中编码的,因此参与者的数量在$ this-&gt;决赛选手中,并且byes的数量在$ this-&gt; byes中。我只测试了没有byes和两个byes的代码。

  public function getBracket() {
      $players = range(1, $this->finalists);
      for ($i = 0; $i < log($this->finalists / 2, 2); $i++) {
        $out = array();
        $reverse = false;
        foreach ($players as $player) {
          $splice = pow(2, $i);
          if ($reverse) {
            $out = array_merge($out, array_splice($players, -$splice));
            $out = array_merge($out, array_splice($players, 0, $splice));
            $reverse = false;
          } else {
            $out = array_merge($out, array_splice($players, 0, $splice));
            $out = array_merge($out, array_splice($players, -$splice));
            $reverse = true;
          }
        }
        $players = $out;
      }
      if ($this->byes) {
        for ($i = 0; $i < $this->byes; $i++ ) {
          for ($j = (($this->finalists / pow(2, $i)) - 1); $j > 0; $j--) {
            $newPlace = ($this->finalists / pow(2, $i)) - 1;
            if ($players[$j] > ($this->finalists / (pow(2 ,($i + 1))))) {
              $player = $players[$j];
              unset($players[$j]);
              array_splice($players, $newPlace, 0, $player);
            }
          }
        }
        for ($i = 0; $i < $this->finalists / (pow(2, $this->byes)); $i++ ) {
          $swap[] = $players[$i];
        }
        for ($i = 0; $i < $this->finalists /(pow(2, $this->byes)); $i++ ) {
          $players[$i] = $swap[count($swap) - 1 - $i];
        }
        return array_reverse($players);
      }
      return $players;
    }

答案 7 :(得分:0)

对于JavaScript代码,请使用以下两个功能之一。前者体现了势在必行的风格和要快得多。后者是递归的&amp;更整洁,但仅适用于相对较少的团队(&lt; 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
}

在这里,您可以通过镜像已经占用的点逐个填充斑点。例如,头号种子队(即0号)进入最顶端。第二个(1)占据括号另一半的相反位置。第三个小组(2)在括号的一半中反映1&amp;等等。尽管存在嵌套循环,但算法具有线性时间复杂度,具体取决于团队数量。

这是递归方法:

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

基本上,您执行与上一个函数相同的镜像,但是递归:

  • 对于n = 1团队,它只是[0]

  • 对于n = 2个团队,您将此函数应用于参数n-1(即, 1)&amp;得到[0]。然后通过插入镜像使数组加倍 在偶数位置之间的元素。因此,[0]变为[0, 1]

  • 对于n = 4个团队,您执行相同的操作,因此[0, 1]变为[0, 3, 1, 2]

如果要获得人类可读的输出,请将结果数组的每个元素增加一个:

const readableArr = arr.map(i => i + 1)

答案 8 :(得分:0)

我使用的是PHP / Laravel插件,可以生成带/不带初步循环的括号。也许它对你有用,我不知道你正在使用什么技术。这是github。

https://github.com/xoco70/kendo-tournaments

希望它有所帮助!

答案 9 :(得分:0)

C版。

int * pctournamentSeedArray(int PlayerCnt)
{
    int * Array;
    int * PrevArray;
    int i;

    Array = meAlloc(sizeof(int) * PlayerCnt);

    if (PlayerCnt == 2)
    {
        Array[0] = 0;
        Array[1] = 1;
        return Array;
    }

    PrevArray = pctournamentSeedArray(PlayerCnt / 2);
    for (i = 0; i < PlayerCnt;i += 2)
    {
        Array[i] = PrevArray[i / 2];
        Array[i + 1] = (PlayerCnt - 1) - Array[i] ;
    }
    meFree(PrevArray);
    return Array;
}