在128人锦标赛中放置32名种子选手的算法

时间:2014-04-09 10:13:21

标签: algorithm tournament

我正在尝试创建一个网球锦标赛模拟器,其中游戏的结果是随机的(有点)。在Grand Slams有128名球员,其中32名是种子。目前我正试图将种子放在适当位置的平局中。我根据正态分布(将替代他们的排名)生成玩家的优势,并将它们存储在升序排序的std :: array中。我想简单地将抽奖表示为vector<double> Draw(128)。理想情况下,我会有一个算法将每个玩家放在平局中的正确位置,但我还没有能够提出一个,所以我决定只是在数组中输入位置并根据需要选择合适的数组在比赛中有多少球员。

职位如下:0,127,95,32,64,96,31,63,16,112,79,48,15,111,80,47,41,72,8,119,23,104,55,87,71,39 ,24,7,56,88,103,120

前几个术语以32的倍数表示:0 * 32,4 * 32-1,3 * 32-1,1 * 32,2 * 32,3 * 32,1 * 32-1 ,2 * 32-1,0.5 * 32,3.5 * 32-1,2.5 * 32-1,1.5 * 32,0.5 * 32-1,3.5 * 32,2.5 * 32。

我还没有弄清楚这个模式。有没有已知的算法?

3 个答案:

答案 0 :(得分:1)

基本算法说明

让我们假设你想在8场比赛中为4名选手打球。

        [ ][ ][ ][ ][ ][ ][ ][ ]    8 empty positions

为第一个玩家播种很容易,我们把他放在哪里并不重要。我们把他放在了开头,这样算法变得更容易了。

        [1][ ][ ][ ][ ][ ][ ][ ]

如果我们想要为第二个玩家播种,我们需要将整个区域分成两个部分。位于左侧的球员将仅在决赛中与右侧的球员会面。因此,必须将第二名球员放入正确的位置,以便两名最佳球员在最后一场比赛之前不会相遇。我们再一次把玩家放在他的开头。

      [1][ ][ ][ ]    [2][ ][ ][ ]

现在我们再次拆分这两个部分,并将第三个和第四个玩家放入新的空白部分。现在第三名球员将在半决赛中遇到第一名球员。

[1][ ]    [3][ ]        [2][ ]    [4][ ]

请注意,算法会在左侧部分放置不均匀的数字,在右侧部分放置偶数。现在可以用随机玩家填充空单元格。该算法与@Nico Schertler建议的基本相同。

<强>编程

我的想法是定义一个获取玩家位置的函数(例如1,2,3,4等)和自由位置的数量(在你的例子中为128)并返回你应该放置的位置播放器。我用Java编写了这个函数,但应该很容易适应它。

/**
 * @param rank
 *            rank of the player, best player == 1, second best player == 2
 * @param partSize
 *            number of total free positions. Has to be a power of 2 (e.g.
 *            2,4,8,16,32)
 * @return returns the start position of the player, zero based
 */
public static int seedPlayer(int rank, int partSize) {
    // base case, if rank == 1, return position 0
    if (rank <= 1) {
        return 0;
    }

    // if our rank is even we need to put the player into the right part
    // so we add half the part size to his position
    // and make a recursive call with half the rank and half the part size
    if (rank % 2 == 0) {
        return partSize / 2 + seedPlayer(rank / 2, partSize / 2);
    }

    // if the rank is uneven, we put the player in the left part
    // since rank is uneven we need to add + 1 so that it stays uneven
    return seedPlayer(rank / 2 + 1, partSize / 2);
}

示例

让我们举办第一场比赛(8名种子选手,共8名选手)

for (int i = 1; i <= 8; i++) {
    System.out.printf("seeded player %d in position %d%n", i, seedPlayer(i, 8) + 1);
}

打印:

seeded player 1 in position 1
seeded player 2 in position 5
seeded player 3 in position 3
seeded player 4 in position 7
seeded player 5 in position 2
seeded player 6 in position 6
seeded player 7 in position 4
seeded player 8 in position 8

导致此字段:

[1][5][3][7][2][6][4][8] Perfect! Like expected!

进一步通知

我不会超过25%的球员,因此多年来锦标赛将会发生变化,每个不太好的球员都有机会与不同的球员比赛。

答案 1 :(得分:0)

我可以想到两种方法:

  1. Absurd-Mind的答案,并为其添加更多信息。
  2. 我们不仅需要将前两个种子分开,以避免它们在最后一轮之前的任何地方会面,将它们分成两个单独的一半,我们必须确保顶级播种的玩家能够在最后一个种子中播放第一轮,第二个种子选手将获得第二轮,依此类推。

    Absurd-Mind提出的算法实际上就是这样做的。如果我不得不改变任何东西,它只会在最后一个函数seedPlayer中。或者我只是添加另一个步骤,从结果的任何一侧提取极值,将它们放在另一个数组中,以获得问题中询问的数组位置。

    For n=8, 
    [1][5][3][7][2][6][4][8] \\previous result
    
    \\extracting the extreme values on either side and placing them in another array:
    [1][8] [5][4] [3][6] [7][2]
    
    \\next round (assuming the higher seed wins):
    [1][4] [3][2]
    
    \\final round:
    [1][2]
    
    Hence, the order obtained initially is correct
    
    1. 观察模式
    2. 没有特定的公式来获得该系列的第n个术语(至少我找不到一个)。但是有一个可以利用的隐藏模式。当您以这种特殊方式编写种子时,模式就出现了:

      (Arrowhead indicates the direction of the ascending order)
      For n=2,
      1 ---> 2
      
      For n=4,
      1 ---> 2
      4 <--- 3
      
      For n=8,
      1 ---> 2
      4 <--- 3
      5 ---> 6
      8 <--- 7
      
      In general,
      m ---> m+1
      ....
      ....
      n <--- n-1
      

      首先将条目数(或参与者数)除以2.为方便起见,我们假设两个数组X和Y(每个大小为n / 2)。箭头一侧的条目存储在数组X中,另一半存储在数组Y中。

      1. Enter n (number of players, should be a power of 2)
      2. Arrays X[n/2], Y[n/2]
         Integer a=1
      3. for (i=1, i<=n/4, i++)
             {
             if(i=1){
               X[i]=a;
               Y[i]=a+1;}
             else if (i%2=0) {  \\if i is even
               X[i]=2i;
               Y[i]=X[i]-1;}
             else {
               X[i]=2i-1;
               Y[i]=X[i]+1; }
             a++;
             }
      
      Resulting arrays (for n=16):
      X=[1][4][5][8][9][12][13][16]
      Y=[2][3][6][7][10][11][14][15]
      

      现在,我们将前两个种子划分为两个独立的一半。我们也相应地将其他种子分开,以避免在前几轮中出现不公平的匹配。我现在要写另一个步骤来提取每个数组中的极值并按以下顺序放置它们

      X= *[1]* [4][5][8][9][12][13] *[16]*
      X= *[4]* [5][8][9][12] *[13]* 
      and so on...
      

      我们获得,

      A=[1][16] [4][13] [5][12] [8][9]
      
      Similarly for the other half,
      B=[2][15] [3][14] [6][11] [7][10] 
      

      继续提取双方极端的胜利者,直到你从A组和B组中获胜,他们将在决赛中相互比赛。

      希望有所帮助!

答案 2 :(得分:0)

有一个按输出顺序选择球员的公式。

基于0的玩家数量,数学运算更容易,所以我会这样做,根据输出和输入的需要进行转换。

从位置0的玩家种子0开始。

PC = Player count (filled up with unseeded players then bye's to power of 2)
slot[0] = seeded[0]
for(n = 1; n < PC; n++) {
    seed = slot[n-(1<<ffs(n))]^(PC>>ffs(n))
    slot[n] = seeded[seed];
}

ffs是第一个找到的,也称为尾随0的计数。低位设置应返回0. enter code here