0

我正在尝试创建一个网球锦标赛模拟器,其中游戏的结果是随机的(有点)。大满贯赛事共有 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。

我还没有从中找出一个模式。有一个已知的算法吗?

4

3 回答 3

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% 的球员,所以比赛会随着时间的推移而改变,每个不那么优秀的球员都有机会与不同的球员比赛。

于 2014-04-09T13:28:27.567 回答
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 是 find first set,也称为尾随 0 的计数。低位设置应返回 0。enter code here

于 2015-11-09T18:05:57.797 回答
0

我可以想到两种方法:

  1. Absurd-Mind 的答案并添加了更多信息。

我们不仅需要将前两名种子分开,为了避免他们在最后一轮之前的任何地方相遇,将它们分成两半,我们必须确保头号种子选手在第一轮比赛中与最后一名种子选手交手,第二个种子玩家可以打倒数第二个,依此类推。

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. 观察图案

没有特定的公式来获得该系列的第 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 组中选出获胜者,他们将在决赛中互相比赛。

希望有帮助!

于 2015-07-13T02:04:58.703 回答