4

我正在尝试建立一种算法来处理比赛的括号表。我需要遍历一系列数字。每个号码都有运动员姓名。号码是随机分配给运动员的,但号码的配对必须始终保持不变。有两组奇数偶数,即A和B。

唯一的问题是我找不到正确的算法来以确切的方式迭代数字,如下所示:

Group A:
--------
  1
  17

  9
  25
------
  5
  21

  13
  29
------
  3
  19

  11
  27
------                         
  7
  23

  15
  31


Group B:
--------
  2
  18

  10
  26
------                          
  6
  22

  14
  30
------   
  4
  20

  12
  28
------
  8
  24

  16
  32

有人可以提供有关如何获得上述输出的建议或示例吗?

编辑1:

上面的例子是32名运动员的支架表!如果您使用一张包含4、8、16、64128名运动员的表格,则必须应用相同的逻辑!

编辑2:

让我们通过 4 名运动员的表格示例和 16 名运动员的表格示例更清楚地说明这一点。

4名运动员的单子:

Group A:
--------
  1
  3

Group B:
--------
  2
  4

16名运动员的表格:

Group A:
--------
  1
  9

  5
  13
------
  3
  11

  7
  15

Group B:
--------
  2
  10

  6
  14
------                              
  4
  12

  8
  16

编辑 3:

最后一部分是我计划在其中包含运动员姓名及其状态的数组。我所说的状态是指,如果运动员以前曾是冠军(强),那么他/她的状态为1,如果运动员以前的成就未知或最小(弱),则状态为0。就是这样做的,所以我们可以将最强的运动员分成不同的组,并确保他们不会在第一场比赛中互相对抗,而是在接近半决赛或决赛时相遇。

PHP数组示例:

$participants = array(
array("John", 0),
array("Gagan", 0),
array("Mike Tyson", 1),
array("Gair", 0),
array("Gale", 0),
array("Roy Johnes", 1),
array("Galip", 0),
array("Gallagher", 0),
array("Garett", 0),
array("Nikolai Valuev", 1),
array("Garner", 0),
array("Gary", 0),
array("Gelar", 0),
array("Gershom", 0),
array("Gilby", 0),
array("Gilford", 0)
);

从这个例子中我们看到那些状态为1的人必须在不同的组中,即AB。但是我们只有两组数字,奇数偶数,在这个例子中,有3 位强壮的运动员。因此,其中两个将在同一组中。最终的结果一定是,同组的那两个实力强的运动员,在第一次交手时一定不会相遇(这意味着他们不会在同一对号码上,并且尽可能地远离彼此,所以他们也不会在第二场战斗中相遇)。

然后随机地,我计划重新排列阵列并将运动员送到括号表 -每次,都有不同的数字每次,那些有标志1的人都会去不同的组和/或在第一次战斗中从未见面时间,运动员的名字分配给同一对数字。

4

3 回答 3

4

考虑到参与者的数量总是 2 的幂,这段代码应该给你你期望的顺序。

function getOrder($numberOfParticipants) {
    $order = array(1, 2);

    for($i = 2; $i < $numberOfParticipants; $i <<= 1) {
        $nextOrder = array();
        foreach($order as $number) {
            $nextOrder[] = $number;
            $nextOrder[] = $number + $i;
        }
        $order = $nextOrder;
    }

    return $order; // which is for instance [1, 17, 9, 25, and so on...] with 32 as argument
}

关于它的工作方式,让我们来看看当参与者数量增加一倍时会发生什么。

Participants | Order
           2 | 1   2
           4 | 1   3=1+2   2   4=2+2
           8 | 1   5=1+4   3   7=3+4   2   6=2+4   4   8=4+4
         ... |
           N | 1         X         Y         Z         ...
          2N | 1   1+N   X   X+N   Y   Y+N   Z   Z+N   ...

我使用的算法是完全相同的逻辑。我从一个数组开始,它只包含[1, 2]并且$i实际上是这个数组的大小。然后我正在计算下一行,直到我到达具有正确数量参与者的那一行。

附带说明:$i <<= 1$i *= 2. 您可以阅读有关按位运算符的文档以获取更多说明。


关于强壮的运动员,因为您希望尽可能多地保持随机性,所以这里有一个解决方案(可能不是最佳的,但这是我首先想到的):

  1. 做两个阵列,一个有,一个有
  2. 如果没有强者或单个强者,只需将整个阵列洗牌并转到 8。
  3. 如果强项多于弱项(不知道在您的情况下是否会发生这种情况,但最好是安全而不是抱歉),将强项洗牌并将最后一个与弱项放在一起,这样两个数组的大小相同
  4. 否则,用元素填充强项null,使数组大小为 2 的幂,然后对其进行洗牌
  5. 洗牌弱者
  6. 准备尽可能多的组,因为它们是strongs数组中的元素,并在每个组中放置一个强项(如果有元素,则不放),并根据需要null完成尽可能多的弱项
  7. 洗牌每组
  8. 返回参与者,排序方式与前一个函数结果数组相同

以及对应的代码:

function splitStrongsAndWeaks($participants) {
    $strongs = array();
    $weaks = array();

    foreach($participants as $participant) {
        if($participant != null && $participant[1] == 1)
            $strongs[] = $participant;
        else
            $weaks[] = $participant;
    }

    return array($strongs, $weaks);
}

function insertNullValues($elements, $totalNeeded)
{
    $strongsNumber = count($elements);
    if($strongsNumber == $totalNeeded)
        return $elements;
    if($strongsNumber == 1)
    {
        if(mt_rand(0, 1))
            array_unshift($elements, null);
        else
            $elements[] = null;
        return $elements;
    }
    if($strongsNumber & 1)
        $half = ($strongsNumber >> 1) + mt_rand(0, 1);
    else
        $half = $strongsNumber >> 1;
    return array_merge(insertNullValues(array_splice($elements, 0, $half), $totalNeeded >> 1), insertNullValues($elements, $totalNeeded >> 1));
}

function shuffleParticipants($participants, $totalNeeded) {
    list($strongs, $weaks) = splitStrongsAndWeaks($participants);

    // If there are only weaks or a single strong, just shuffle them
    if(count($strongs) < 2) {
        shuffle($participants);
        $participants = insertNullValues($participants, $totalNeeded);
    }
    else {
        shuffle($strongs);

        // If there are more strongs, we need to put some with the weaks
        if(count($strongs) > $totalNeeded / 2) {
            list($strongs, $strongsToWeaks) = array_chunk($strongs, $totalNeeded / 2);
            $weaks = array_merge($weaks, $strongToWeaks);
            $neededGroups = $totalNeeded / 2;
        }
        // Else we need to make sure the number of groups will be a power of 2
        else {
            $neededGroups = 1 << ceil(log(count($strongs), 2));
            if(count($strongs) < $neededGroups)
                $strongs = insertNullValues($strongs, $neededGroups);
        }

        shuffle($weaks);

        // Computing needed non null values in each group
        $neededByGroup = $totalNeeded / $neededGroups;
        $neededNonNull = insertNullValues(array_fill(0, count($participants), 1), $totalNeeded);
        $neededNonNull = array_chunk($neededNonNull, $neededByGroup);
        $neededNonNull = array_map('array_sum', $neededNonNull);

        // Creating groups, putting 0 or 1 strong in each
        $participants = array();            
        foreach($strongs as $strong) {
            $group = array();

            if($strong != null)
                $group[] = $strong;
            $nonNull = array_shift($neededNonNull);
            while(count($group) < $nonNull)
                $group[] = array_shift($weaks);
            while(count($group) < $neededByGroup)
                $group[] = null;

            // Shuffling again each group so you can get for instance 1 -> weak, 17 -> strong
            shuffle($group);
            $participants[] = $group;
        }

        // Flattening to get a 1-dimension array
        $participants = call_user_func_array('array_merge', $participants);
    }

    // Returned array contains participants ordered the same way as getOrder()
    // (eg. with 32 participants, first will have number 1, second number 17 and so on...)
    return $participants;
}

如果您希望结果数组将括号中的数字作为索引,您可以简单地执行以下操作:

$order = getOrder(count($participants));
$participants = array_combine($order, shuffleParticipants($participants, count($order)));
于 2013-09-02T20:53:05.413 回答
3

好的,我终于设法将我的 Tcl 代码转换为 PHP!我也改变了一些东西:

<?php

// Function generating order participants will be placed in array
function getBracket($L) {
    // List will hold insert sequence
    $list = array();
    // Bracket will hold final order of participants
    $bracket = array();
    // The algorithm to generate the insert sequence
    for ($n = 1; $n <= $L; $n += 1) {
        // If 'perfect' number, just put it (Perfect no.s: 2, 4, 8, 16, 32, etc)
        if (substr(log($n)/log(2), -2) == ".0") {
            $list[] = $n;
        // If odd number, stuff...
        } elseif ($n % 2 == 1) {
            $list[] = $list[($n-1)/2];
        // Else even number, stuff...
        } else {
            $list[] = $list[$n/2-1]+$n/2;
        }
    }

    // Insert participant order as per insert sequence
    for ($i = 1; $i <= sizeof($list); $i += 1) {
        $id = $i-1;
        array_splice($bracket, $list[$id], 0, $i);
    }
    return $bracket;
}

// Find number of participants over 'perfect' number if any
function cleanList($L) {
    for ($d = 1; $L > $d; $d += 1) {
        $sq = $L-pow(2,$d);
        if($sq == 0) {break;}
        if($sq < 0) {
            $d = pow(2,$d-1);
            $diff = $L-$d;
            break;
        }
    }
    return $diff;
}

$participants = array(
    array(0, "John", 2),
    array(1, "Gagan", 1),
    array(2, "Mike Tyson", 1),
    array(3, "Gair", 1),
    array(4, "Gale", 0),
    array(5, "Roy Johnes", 0),
    array(6, "Galip", 0),
    array(7, "Gallagher", 0),
    array(8, "Garett", 0),
    array(9, "Nikolai Valuev", 0),
    array(10, "Garner", 1),
    array(11, "Gary", 0),
    array(12, "Gelar", 0),
    array(13, "Gershom", 1),
    array(14, "Gilby", 0),
    array(15, "Gilford", 1),
    array(16, "Arianna", 0)
);

// Extract strength of participant
foreach ($participants as $array) {
    $finorder[] = $array[2];
}
// Sort by strength, strongest first
array_multisort($finorder,SORT_DESC,$participants);

$order = array();
$outside = array();

// Remove participants above 'perfect' number
$remove = cleanList(sizeof($participants));
for ($r = 1; $r <= $remove; $r += 1) {
    $removed = array_shift($participants);
    $outside[] = $removed;
}

// Get corresponding bracket
$res = getBracket(sizeof($participants));
foreach ($res as $n) {
    $order[] = $n;
}

// Align bracket results with participant list
array_multisort($order, $participants);
$participants = array_combine($res, $participants);

echo "The final arrangement of participants\n";
print_r($participants);
print_r($outside);
?>

键盘演示

为了获得元素插入顺序的逻辑,我使用了这个模式

另外,由于我对 PHP 不太熟悉,可能有一些方法可以缩短一些东西,但是哦,好吧,只要它有效 ^^

编辑:修复了第一个参与者排序和添加新票号的问题。对于没有旧票号的结果,请参见此处

EDIT2:设法将键移动到数组中;看这里

EDIT3:我认为“额外”参与者应该超出括号。如果您想在括号中使用 null,则可以使用它。

EDIT4:不知何故,键盘上的PHP版本破坏了一些东西......在下面修复它并删除初始索引......:

<?php

    // Function generating order participants will be placed in array
    function getBracket($L) {
        // List will hold insert sequence
        $list = array();
        // Bracket will hold final order of participants
        $bracket = array();
        // The algorithm to generate the insert sequence
        for ($n = 1; $n <= $L; $n += 1) {
            // If 'perfect' number, just put it (Perfect no.s: 2, 4, 8, 16, 32, etc)
            if (int(log($n)/log(2)) || $n == 1) {
                $list[] = $n;
            // If odd number, stuff...
            } elseif ($n % 2 == 1) {
                $list[] = $list[($n-1)/2];
            // Else even number, stuff...
            } else {
                $list[] = $list[$n/2-1]+$n/2;
            }
        }

        // Insert participant order as per insert sequence
        for ($i = 1; $i <= sizeof($list); $i += 1) {
            $id = $list[$i-1]-1;
            array_splice($bracket, $id, 0, $i);
        }
        return $bracket;
    }

    // Find number of participants over 'perfect' number if any
    function cleanList($L) {
        for ($d = 1; $L > $d; $d += 1) {
            $diff = $L-pow(2,$d);
            if($diff == 0) {break;}
            if($diff < 0) {
                $diff = pow(2,$d)-$L;
                break;
            }
        }
        return $diff;
    }

    $participants = array(
        array("John", 2),
        array("Gagan", 1),
        array("Mike Tyson", 1),
        array("Gair", 1),
        array("Gale", 0),
        array("Roy Johnes", 0),
        array("Galip", 0),
        array("Gallagher", 0),
        array("Garett", 0),
        array("Nikolai Valuev", 0),
        array("Garner", 1),
    );

    // Extract strength of participant
    foreach ($participants as $array) {
        $finorder[] = $array[2];
    }
    // Sort by strength, strongest first
    array_multisort($finorder,SORT_DESC,$participants);

    $order = array();

    // Add participants until 'perfect' number
    $add = cleanList(sizeof($participants));
    for ($r = 1; $r <= $add; $r += 1) {
        $participants[] = null;
    }

    // Get corresponding bracket
    $res = getBracket(sizeof($participants));
    // Align bracket results with participant list
    foreach ($res as $n) {
        $order[] = $n;
    }
    array_multisort($order, $participants);
    $participants = array_combine($res, $participants);

    echo "The final arrangement of participants\n";
    print_r($participants);
?>

ideone
viper-7

于 2013-09-03T11:05:47.023 回答
1

这个粗略的代码可能是你想要的:

<?php

class Pair
{
    public $a;
    public $b;

    function __construct($a, $b) {
        if(($a & 1) != ($b & 1)) 
            throw new Exception('Invalid Pair');
        $this->a = $a;
        $this->b = $b;
    }
}

class Competition
{
    public $odd_group = array();
    public $even_group = array();

    function __construct($order) {
        $n = 1 << $order;
        $odd = array();
        $even = array();
        for($i = 0; $i < $n; $i += 4) {
            $odd[] = $i + 1;
            $odd[] = $i + 3;
            $even[] = $i + 2;
            $even[] = $i + 4;
        }
        shuffle($odd);
        shuffle($even);
        for($i = 0; $i < count($odd); $i += 2) {
            $this->odd_group[] = new Pair($odd[$i], $odd[$i+1]);
            $this->even_group[] = new Pair($even[$i], $even[$i+1]);
        }
        echo "Odd\n";
        for($i = 0; $i < count($this->odd_group); ++$i) {
            $pair = $this->odd_group[$i]; 
            echo "{$pair->a} vs. {$pair->b}\n";
        }
        echo "Even\n";
        for($i = 0; $i < count($this->even_group); ++$i) {
            $pair = $this->even_group[$i]; 
            echo "{$pair->a} vs. {$pair->b}\n";
        }
    }
}

new Competition(5);

?>
于 2013-09-02T19:20:35.850 回答