Расширение кругового турнира 1 на 1 до 1 на 1 на 1 на 1

Я пытаюсь расширить и улучшить циклический алгоритм от группы 1 на 1 до группы 1 на 1 на 1 на 1 (что-то вроде бесплатного для всех). Я сделал саму функцию для выполнения расписания, но когда я пытался ее расширить, некоторые команды повторялись. Например, у меня есть 16 команд, и я хочу, чтобы было 5 раундов, команда 1 появляется 7 раз в 5 раундах, а команда 2 появляется 3 раза в 5 раундах. Мне нужно, чтобы они появлялись максимум 5 раз. Я действительно не понимаю, как я могу это сделать. Приветствуются любые советы и ссылки.

function make_schedule(array $teams, int $rounds = null, bool $shuffle = true, int $seed = null): array
{


    $teamCount = count($teams);


   if($teamCount < 4) {
        return [];
    }
    //Account for odd number of teams by adding a bye
    if($teamCount % 2 === 1) {
        array_push($teams, null);
        $teamCount += 1;
    }
    if($shuffle) {
        //Seed shuffle with random_int for better randomness if seed is null
        srand($seed ?? random_int(PHP_INT_MIN, PHP_INT_MAX));
        shuffle($teams);
    } elseif(!is_null($seed)) {
        //Generate friendly notice that seed is set but shuffle is set to false
        trigger_error('Seed parameter has no effect when shuffle parameter is set to false');
    }
    $quadTeamCount = $teamCount / 4;
    if($rounds === null) {
        $rounds = $teamCount - 1;
    }

    $schedule = [];

    for($round = 1; $round <= $rounds; $round += 1) {
        $matchupPrev = null;

        foreach($teams as $key => $team) {
            if($key >= $quadTeamCount ) {
                break;
            }

            $keyCount = $key + $quadTeamCount;
            $keyCount2 = $key + $quadTeamCount + 1;
            $keyCount3 = $key + $quadTeamCount + 2;


            $team1 = $team;
            $team2 = $teams[$keyCount];
            $team3 = $teams[$keyCount2];
            $team4 = $teams[$keyCount3];


            //echo "<pre>Round #{$round}: {$team1} - {$team2} - {$team3} - {$team4} == KeyCount: {$keyCount} == KeyCount2: {$keyCount2} == KeyCount3: {$keyCount3}</pre>";

            //Home-away swapping
            $matchup = $round % 2 === 0 ? [$team1, $team2, $team3, $team4 ] : [$team2, $team1, $team4, $team3];

            $schedule[$round][] = $matchup ;
        }
        rotate($teams);
    }

    return $schedule;
}

Функция поворота:

   function rotate(array &$items)
{
    $itemCount = count($items);
    if($itemCount < 3) {
        return;
    }
    $lastIndex = $itemCount - 1;
    /**
     * Though not technically part of the round-robin algorithm, odd-even 
     * factor differentiation included to have intuitive behavior for arrays 
     * with an odd number of elements
     */
    $factor = (int) ($itemCount % 2 === 0 ? $itemCount / 2 : ($itemCount / 2) + 1);
    $topRightIndex = $factor - 1;
    $topRightItem = $items[$topRightIndex];
    $bottomLeftIndex = $factor;
    $bottomLeftItem = $items[$bottomLeftIndex];
    for($i = $topRightIndex; $i > 0; $i -= 1) {
        $items[$i] = $items[$i - 1];
    }
    for($i = $bottomLeftIndex; $i < $lastIndex; $i += 1) {
        $items[$i] = $items[$i + 1];
    }
    $items[1] = $bottomLeftItem;
    $items[$lastIndex] = $topRightItem;
}

Например:

Если я установлю раундов на 5, каждая команда сыграет 5 матчей. Снимок экрана примера массива

Работа с 5-м раундом:

Ну, после того, как я немного подумал, может быть, у них нет возможности играть без повторов, но если его снизить до минимума, как каждая команда должна играть только 5 раз - это означает, что один раз за раунд. Это то, что я имел в виду. И что я имел в виду под «они повторяются», так это то, что есть примерно: 16 команд, 5 раундов, и некоторые команды проходят 7 раз за все эти раунды, а другие команды проходят 3 раза за эти 5 раундов. Я хочу избежать этого и заставить каждую команду играть максимум 5 раундов.


person Stf Kolev    schedule 18.08.2018    source источник
comment
Я не понимаю. Круговая система означает, что каждая команда играет с каждой другой командой хотя бы один раз. Как 16 команд могут появиться не более 5 раз? Пожалуйста, обновите свой вопрос с ожидаемым результатом.   -  person fubar    schedule 18.08.2018
comment
@fubar Обновлен скриншотом.   -  person Stf Kolev    schedule 18.08.2018
comment
Пожалуйста, отредактируйте свой вопрос, чтобы включить объяснение того, как команды должны сопоставляться друг с другом. И что такого особенного в 5 совпадениях? И как вы хотите обрабатывать повторяющихся противников, когда у вас более 5 совпадений?   -  person Progman    schedule 18.08.2018
comment
@Progman Добавил еще несколько объяснений, если нужно, я напишу в комментариях. Я привел эти 5 раундов в качестве примера. Например, каждая команда должна сыграть n раундов, которые установлены. Если я поставлю 7 туров, каждая команда должна сыграть 7 раз.   -  person Stf Kolev    schedule 18.08.2018
comment
Измените свой вопрос, включив в него исходный код функции rotate(). Кроме того, похоже, что этот код предназначен для совпадений 1 на 1, но вы должны добавить свой код, который у вас есть (или попробовать) для совпадений 1 на 1 на 1 на 1, пример вывода, который он генерирует, и проблему, с которой вы столкнулись.   -  person Progman    schedule 18.08.2018
comment
@Progman, опубликованный код действительно генерирует приведенный пример, просто он немного сбивает с толку то, как он написан. Что мне непонятно, так это то, почему примерный массив, показанный на скриншоте, неверен.   -  person ehymel    schedule 18.08.2018
comment
@Progman Отредактировал и добавил функцию, а также мой тест с 1v1v1v1 make_schedule.   -  person Stf Kolev    schedule 18.08.2018
comment
Что не так с сопряжением на скриншоте? Каждый игрок играет в общей сложности 5 матчей и всего 5 раундов.   -  person Progman    schedule 18.08.2018
comment
@Progman Ты спрашиваешь меня или отвечаешь ehymel?   -  person Stf Kolev    schedule 18.08.2018


Ответы (1)


Ваш foreach() с выбором остальных 3-х команд неверен. Один из них должен сделать шаги, кратные 4. Если вы этого не сделаете, вы выберете несколько команд в начале и вообще не выберете команды в конце массива. Это приведет к неправильному подбору команд, подобно этому (команды здесь обозначаются буквами):

abcd
bcde
cdef
defg

И тогда ваш break; попадает.

Вместо этого это должно выглядеть примерно так:

for ($i=0; $i<4; $i++) {
    $matchup = array();
    for ($j=0; $j<4; $j++) {
        $matchup[] = $teams[4*$i+$j];
    }
    $schedule[$round][] = $matchup ;
}

Таким образом, вы получите следующую пару (опять же, используя буквы как команды):

abcd
efgh
ijkl
mnop

Этот алгоритм разделит список команд на четыре группы:

abcd|efgh|ijkl|mnop

Имейте в виду, что в зависимости от перетасовки массива $teams для следующего раунда вы можете получить одного и того же противника дважды.

adei|klnf|gjmc|pobh

Здесь снова встретятся команды ad, kl и op.

person Progman    schedule 18.08.2018
comment
Это нормально, чтобы встретиться лицом к лицу еще раз. Решение действительно надежное, спасибо за ваше терпение, хотя я думаю, что придумал способ улучшить это. Я поделюсь ответом позже, если мне это удастся. Еще раз, спасибо. Хорошего дня! - person Stf Kolev; 18.08.2018