Алгоритм турнира Round Robin в C#

У меня есть некоторые проблемы, чтобы достичь этого маленького раунд Робин. То, что я пытаюсь сделать, это создать предварительный календарь игр

тогда я хочу вывести;

день 1: Команда 1 против команды 2; Команда 3 команда против 4; Команда 5ВС 6;

день 2 Команда 1 против команды 4; Команда 6 против команды 3; Команда 2 против команды 5;

до конца чемпионата;

вот код, который у меня есть до сих пор, но у меня возникли проблемы с первой командой а остальное время вращается...:

static void Main(string[] args)
   {
        string[] ListTeam = new string[] {"Equipe1", "Equipe2", "Equipe3", "Equipe4", "Equipe5", "Equipe6"};
        IList<Match> ListMatch = new List<Match>();
        it NumberOfDays = (ListTeam.Count()-1);
        int y = 2;

        for (int i = 1; i <= NumberOfDays; i++)
        {
            Console.WriteLine("nDay {0} : n",i);
            Console.WriteLine(ListTeam[0].ToString() + " VS " + ListTeam[i].ToString());

            for (y =ListTeam.Count(); y>0 ; y--)
            {
                Console.WriteLine(ListTeam[y].ToString() + " VS " + ListTeam[y+1].ToString());
                y++;
            }

        }
    }

EDIT: я нашел пример кода на Java, но Я не могу перевести его...

6 ответов


это должно быть достаточно легко сделать с помощью модульной арифметики:

обновление 2: (как и обещал правильный алгоритм)

public void ListMatches(List<string> ListTeam)
{
    if (ListTeam.Count % 2 != 0)
    {
        ListTeam.Add("Bye");
    }

    int numDays = (numTeams - 1);
    int halfSize = numTeams / 2;

    List<string> teams = new List<string>();

    teams.AddRange(ListTeam.Skip(halfSize).Take(halfSize));
    teams.AddRange(ListTeam.Skip(1).Take(halfSize -1).ToArray().Reverse());

    int teamsSize = teams.Count;

    for (int day = 0; day < numDays; day++)
    {
        Console.WriteLine("Day {0}", (day + 1));

        int teamIdx = day % teamsSize;

        Console.WriteLine("{0} vs {1}", teams[teamIdx], ListTeam[0]);

        for (int idx = 1; idx < halfSize; idx++)
        {               
            int firstTeam = (day + idx) % teamsSize;
            int secondTeam = (day  + teamsSize - idx) % teamsSize;
            Console.WriteLine("{0} vs {1}", teams[firstTeam], teams[secondTeam]);
        }
    }
}

который будет печатать матчи команды каждый день.

позвольте мне быстро объяснить, как работает алгоритм:

я заметил, что, поскольку мы вращаем все команды, кроме первой, если мы поместим все команды в массив, кроме первого, то мы должны просто прочитать первую команду из этого массива использование смещения индекса на основе дня и выполнение модульной арифметики для правильной обертки. На практике мы будем рассматривать этот массив как бесконечно повторяющийся в обоих направлениях, и мы будем постепенно сдвигать наш взгляд вправо (или влево).

есть одна загвоздка, однако, и это тот факт, что мы должны приказать командам очень определенным образом, чтобы это работало правильно. Иначе мы не получим правильную ротацию. Из-за этого нам нужно прочитать соответствия вторая команда тоже очень своеобразно.

правильный способ подготовки списка выглядит следующим образом:

  • никогда не помещайте первую команду (Команда № 1) в список.
  • возьмите последнюю половину списка команды и поместите их в начало списка.
  • возьмите первую половину списка, переверните его и поместите их в список (но не Команда № 1).

теперь правильный способ считывания списка таков: следуйте:

  • для каждого дня увеличьте первый индекс, который вы смотрите на 1.
  • для первой команды, которую вы видите в этом месте, сопоставьте эту команду с командой№1.
  • для следующей команды в списке ((day + idx) % numDays), мы обычно сопоставляем его с командой, которая компенсируется половиной количества команд минус 1 (минус 1, потому что мы сами справились с первым матчем). Однако, поскольку вторая половина нашего списка была подготовлена путем возврата, нам необходимо сопоставьте это смещение во второй половине списка. Более простой способ сделать это-наблюдать, что в этом эквивалентно сопоставлению того же индекса, но с конца списка. Учитывая текущее day смещение (day + (numDays - idx)) % numDays.

обновление 3: Я не был счастлив, что мое решение включало такой запутанный выбор, сопоставление, реверсирование элементов массива. После того, как я подумал о том, что мое решение включало, я понял, что я был слишком зациклен на сохранении порядок команд, как данность. Однако это не требование, и можно получить другой, но одинаково действительный график, не заботясь о первоначальном заказе. Все, что имеет значение, - это алгоритм выбора, который я описываю во второй части моего объяснения.

таким образом, вы можете упростить следующие строки:

teams.AddRange(ListTeam.Skip(halfSize).Take(halfSize));
teams.AddRange(ListTeam.Skip(1).Take(halfSize -1).ToArray().Reverse());

в:

teams.AddRange(ListTeam); // Copy all the elements.
teams.RemoveAt(0); // To exclude the first team.

похоже, вы хотите запланировать круговом турнире. Статья wp содержит алгоритм.

Я не вижу, где вы даже пытаетесь повернуть массив. Перестановка будет выглядеть: 1 -> 2 -> 3 -> 4 ... - > n/2 - 1 -> n-1 - > n-2 - > n-3 ->... -> n / 2 - > 1 (и 0 остается фиксированным). Вы можете сделать это в 2 петли (верхний ряд и нижний ряд).


Я сделал некоторые улучшения в блоке кода с ответом, который вычисляет двойной круговой график

GameEntities db = new GameEntities();


private void btnTeamFixtures_Click(object sender, RoutedEventArgs e)
    {
        txtResults.Text = null;

        var allTeams = db.Team.Select(t => t.TeamName);

        int numDays = allTeams.Count() - 1;
        int halfsize = allTeams.Count() / 2;

        List<string> temp = new List<string>();
        List<string> teams = new List<string>();

        teams.AddRange(allTeams);
        temp.AddRange(allTeams);
        teams.RemoveAt(0);

        int teamSize = teams.Count;

        for (int day = 0; day < numDays * 2; day++)
        {
            //Calculate1stRound(day);
            if (day % 2 == 0)
            {
                txtResults.Text += String.Format("\n\nDay {0}\n", (day + 1));

                int teamIdx = day % teamSize;

                txtResults.Text += String.Format("{0} vs {1}\n", teams[teamIdx], temp[0]);

                for (int idx = 0; idx < halfsize; idx++)
                {
                    int firstTeam = (day + idx) % teamSize;
                    int secondTeam = ((day + teamSize) - idx) % teamSize;

                    if (firstTeam != secondTeam)
                    {
                        txtResults.Text += String.Format("{0} vs {1}\n", teams[firstTeam], teams[secondTeam]);
                    }
                }
            }

            //Calculate2ndRound(day);
            if (day % 2 != 0)
            {
                int teamIdx = day % teamSize;

                txtResults.Text += String.Format("\n\nDay {0}\n", (day + 1));

                txtResults.Text += String.Format("{0} vs {1}\n", temp[0], teams[teamIdx]);

                for (int idx = 0; idx < halfsize; idx++)
                {
                    int firstTeam = (day + idx) % teamSize;
                    int secondTeam = ((day + teamSize) - idx) % teamSize;

                    if (firstTeam != secondTeam)
                    {
                        txtResults.Text += String.Format("{0} vs {1}\n", teams[secondTeam], teams[firstTeam]);
                    }
                }
            }
        }
    }

Если вы хотите, вы можете создать 2 метода и передать и целое число (День), как я сделал в 2 комментариях, чтобы отделить код.

Если у вас есть какие-либо вопросы или предложения, не стесняйтесь ответить.


Это может быть запутанный метод, но это может быть сведено к задаче теории графов. Создайте вершину графа для каждой команды и создайте ребро между каждой вершиной (так что это полный граф). Тогда для алгоритма:

для каждого дня i = 1 .. n:

  • выберите любые две немаркированные вершины, которые непосредственно связаны, и пометьте ребро между ними i. Отметьте обе вершины.
  • повторять, пока не будут отмечены все вершины.
  • выход помеченные края (т. е. team1 против team2, team3 против team4 и т. д.)
  • удалите помеченные ребра из графика и сбросьте все вершины на немаркированные.

Как насчет расчета возможных комбинаций для каждого дня, который вы хотите, то

  1. сортировать их в каждой паре, т. е. команда с наименьшим числом всегда первая в любой паре.
  2. сортировка перечисленных пар для каждого дня по первым в каждом пара.

предполагая, что у нас всегда есть четное число команд/игроков (если нечетно, добавьте BYE; для моего случая команды/игроки, ранжированные по их рейтингу, мы хотели бы иметь первую команду/игрока, чтобы играть слабее противника), вот моя реализация.

void CreateSchedule(int n)
{
    int[] orig = new int[n];
    for(int i=0;i<n; i++){
        orig[i] = i + 1;
    }   
    IEnumerable<int> rev = orig.Reverse();

    int len = orig.Length;
    for (int j = 0; j < len - 1; j++)
    {
        List<int> tmp = new List<int>();
        tmp.Add(orig[0]);
        tmp.AddRange(rev.Take(j).Reverse());
        if (j < len && len > 1 + j) tmp.AddRange(orig.Skip(1).Take(len - 1 - j));
        PrintMe(tmp, j + 1);
    }
}

void PrintMe(IEnumerable<int> arr, int round)
{

    Console.WriteLine("----------Round {0}----------------", round);
    int halfSize = arr.Count() / 2;

    IEnumerable<int> A = arr.Take(halfSize);
    IEnumerable<int> B = arr.Skip(halfSize).Take(halfSize).Reverse();

    var Result = A.Zip(B, (x, y) => $"{x} vs {y}");
    Console.WriteLin(Result);
}