使用组迭代(循环)复杂的数字范围以生成括号表

时间:2013-09-01 15:28:36

标签: php loops iteration

我正在尝试构建一个处理竞赛括号表的算法。我需要经历一系列数字。每个号码都有运动员姓名。数字随机分配给运动员,但号码的配对必须始终保持不变。有两组奇数偶数,即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,64 128 运动员的工作表,则必须应用相同的逻辑!

编辑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:

最后一部分是,我打算在其中添加一个名为 name 及其状态的数组。 按状态我的意思是,如果运动员以前(强)已经成为冠军,那么他/她获得 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 的人必须位于不同的组中,即 A B 。但是我们只有两组的数字奇数甚至,在这个例子中,有 3名强运动员。因此,他们中的两个将在同一组。最后的结果必须是,那两个强大的运动员,在同一组中,必须在第一次战斗中见面(这意味着他们不会在同一对数字和尽可能远离彼此,所以他们也不会在第二次战斗中见面。)

然后随机,我打算重新安排阵列,并将运动员送到支架表 - 每次,不同数字每次都有标志 1 进入不同的团体和/或永远不会在第一场比赛中见面每次都将运动员的名字分配给同一对数字。

3 个答案:

答案 0 :(得分: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相同。您可以阅读有关bitwise operators的文档以获得进一步的解释。


关于强大的运动员,因为你想保持尽可能多的随机性,这是一个解决方案(可能不是最佳的,但这是我最初的想法):

  1. 制作两个阵列,一个使用 strongs ,一个使用弱点
  2. 如果没有 strongs 或单个,只需将整个数组洗牌并转到8。
  3. 如果 strongs 弱点更多(如果在你的情况下可能发生dunno但是更好的安全而不是抱歉),请将 strongs 并使用 weaks 放置最后一个,以便两个数组的大小相同
  4. 否则,请使用null元素填充 strongs ,这样数组大小为2的幂,然后将其随机播放
  5. 洗牌弱者
  6. 准备与 strongs 数组中的元素一样多的组,并在每个组中添加 strongs 之一(如果您有null,则为无;元素)并根据需要完成尽可能多的弱点
  7. 随机播放
  8. 返回参与者,订购方式与上一个函数生成数组相同
  9. 以及相应的代码:

    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)));
    

答案 1 :(得分: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);
?>

Codepad demo

为了获得元素插入顺序的逻辑,我使用了this pattern

另外,由于我对PHP不太熟悉,可能有办法让一些事情更短,但是哦,好吧,只要它有效^^

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

EDIT2:管理将密钥移入数组;见here

EDIT3:我认为'额外'的参与者应该超出支架。如果你想在括号中使用null,你可以使用它。

EDIT4:不知何故,codepad上的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

答案 2 :(得分: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);

?>